Fedora Account System
Red Hat Associate
Red Hat Customer
AI_ONLY_REPORT package: device-mapper-persistent-data-1.3.0-1.el10 ------ Summary: Reachable DoS in sync read path from `u64::MAX` block index (`find_runs` overflow -> invalid run -> panic): a crafted metadata-derived block index can terminate affected tooling by reaching an empty vectored read in the synchronous batching path. Requirements to exploit: An attacker needs to supply, or induce a user to process, crafted metadata or an equivalent block list containing `u64::MAX` so that the package reaches the synchronous `read_many_` batching path. This is a local, input-driven denial of service; no privilege escalation, confidentiality impact, or code execution is demonstrated. Component affected: `device-mapper-persistent-data`, specifically the synchronous read path through `src/io_engine/gaps.rs` (`find_runs`), `src/io_engine/sync.rs` (`SyncIoEngine::read_many_`), and `src/io_engine/utils.rs` (`VectoredBlockIo::read_blocks`). Version affected: `device-mapper-persistent-data-1.3.0-1.el10` Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H - 5.5 (MEDIUM) AV:L - the attacker needs local access to provide crafted metadata or a malicious image to the affected tooling. AC:L - the trigger condition is a single malformed block index reaching an otherwise normal batching path. PR:N - no privileges in the vulnerable component are required beyond supplying the crafted input. UI:R - a user or automation must invoke an affected tool on the crafted input. S:U - the impact is limited to the vulnerable process. C:N - no confidentiality impact is demonstrated. I:N - no integrity impact is demonstrated. A:H - the process can panic and terminate, preventing the requested operation from completing. Impact: Moderate. Under Red Hat's severity guidance, this is a real availability issue in a reachable code path, but it does not rise to Important or Critical because the demonstrated attack is local and input-driven rather than a remote service compromise or system compromise. It is more than Low because successful exploitation directly terminates the affected process once the vulnerable path is reached. Embargo: no Reason: this is a local, crafted-input denial of service with straightforward mitigation and no demonstrated confidentiality, integrity, privilege, or code-execution impact. Acknowledgement: Aisle Research Vulnerability Details: The synchronous batched-read path constructs end-exclusive runs with unchecked `+ 1` arithmetic in `src/io_engine/gaps.rs`: ```rust for b in blocks { if let Some((begin, end)) = last { #[allow(clippy::comparison_chain)] if *b > end { let len = b - end; if len > gap_threshold { runs.push(Run(begin, end)); last = Some((*b, *b + 1)); } else { runs.push(Run(begin, end)); runs.push(Gap(end, *b)); last = Some((*b, *b + 1)); } } else if *b == end { last = Some((begin, b + 1)); } else { runs.push(Run(begin, end)); last = Some((*b, b + 1)); } } else { last = Some((*b, b + 1)); } } ``` When `b == u64::MAX`, release builds can wrap this into an invalid end-exclusive range such as `Run(u64::MAX, 0)`. In `SyncIoEngine::read_many_`, `RunOp::Run(b, e)` populates buffers by iterating `for b in *b..*e`; for `Run(u64::MAX, 0)`, that range is empty, so no buffers are pushed even though the batch start is still tracked from the run start. The later vectored read sink in `src/io_engine/utils.rs` then dereferences the first buffer unconditionally: ```rust fn read_blocks(&self, buffers: &mut [&mut [u8]], mut pos: u64) -> Result<Vec<Result<()>>> { let block_size = buffers[0].len(); ``` This produces a process-terminating panic. The available evidence supports reachability through real metadata-derived block lists passed into `read_many`, although not every caller or I/O engine is shown to behave identically. This report establishes the synchronous path. In non-release builds, the same input may instead panic earlier on integer overflow, but the availability impact remains a crash. Steps to reproduce: 1. Add the following regression test to `src/io_engine/sync/tests.rs`: ```rust #[test] #[should_panic] fn panic_on_max_block_index() { let mut v = MockVio::new(); let _ = SyncIoEngine::read_many_(v.into(), &[u64::MAX]).unwrap(); } ``` 2. Run: ```bash cargo test --release panic_on_max_block_index – --nocapture ``` 3. Observe a panic originating from `src/io_engine/utils.rs` when `VectoredBlockIo::read_blocks` indexes `buffers[0]`. Without `--release`, the same input may panic earlier on the unchecked overflow in `find_runs`, which still demonstrates a denial of service. Mitigation: Until a fix is applied, avoid processing untrusted metadata with affected builds, and reject block indices equal to `u64::MAX` or otherwise outside the valid device block range before they reach `generate_runs` or `read_many_`. Caller-side validation is a useful short-term defense, but it should not replace fixing the underlying arithmetic and empty-buffer handling. Proposed Fix: A minimal fix is to use checked arithmetic when constructing end-exclusive runs and to return early from empty vectored reads. ```diff diff --git a/src/io_engine/gaps.rs b/src/io_engine/gaps.rs @@ fn find_runs(blocks: &[u64], gap_threshold: u64) -> Vec<RunOp> { @@ for b in blocks { + let next = match b.checked_add(1) { + Some => n, + None => { + if let Some((begin, end)) = last.take() { + runs.push(Run(begin, end)); + } + continue; + } + }; if let Some((begin, end)) = last { @@ last = Some((*b, *b + 1)); + last = Some((*b, next)); @@ last = Some((*b, *b + 1)); + last = Some((*b, next)); @@ last = Some((begin, b + 1)); + last = Some((begin, next)); @@ last = Some((*b, b + 1)); + last = Some((*b, next)); } } else { last = Some((*b, b + 1)); + last = Some((*b, next)); } } diff --git a/src/io_engine/utils.rs b/src/io_engine/utils.rs @@ fn read_blocks(&self, buffers: &mut [&mut [u8]], mut pos: u64) -> Result<Vec<Result<()>>> { + if buffers.is_empty() { + return Ok(Vec::new()); + } let block_size = buffers[0].len(); ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use