From 3bf94535d5e5214502434f3f4b7e13725b81a9fd Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Fri, 24 Apr 2026 16:10:15 -0700 Subject: [PATCH] virtio-devices: block: reject duplicate in-flight head_index A malicious or buggy guest can violate virtio by making the same descriptor head available twice before the first chain has been placed on the used ring. The submit path pushed both chains onto the VecDeque-backed inflight_requests keyed by head_index, and on completion find_inflight_request() returned the first linear match. That Request's complete_async() freed its bounce buffer while the other chain's io_uring op was still targeting it, producing a use-after-free the kernel could then scribble into. Signed-off-by: Dylan Reid (cherry picked from commit 544fa4aa764abae9d7cbe53c69598521570360a8) --- virtio-devices/src/block.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index b927d75c8..524af407e 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -211,6 +211,10 @@ impl BlockEpollHandler { Setting device status to 'NEEDS_RESET' and stopping processing queues until reset." ); + self.set_needs_reset(); + } + + fn set_needs_reset(&mut self) { self.device_status .fetch_or(crate::DEVICE_NEEDS_RESET as u8, Ordering::SeqCst); @@ -220,6 +224,17 @@ Setting device status to 'NEEDS_RESET' and stopping processing queues until rese } } + // A spec-compliant driver never reuses a virtqueue head_index while the + // corresponding chain is still available (virtio 1.x ยง2.7.13.4). + // Double check the guest driver is behaving. + fn is_head_in_flight( + inflight: &VecDeque<(u16, Request)>, + batch: &[(u16, Request)], + head: u16, + ) -> bool { + batch.iter().any(|(h, _)| *h == head) || inflight.iter().any(|(h, _)| *h == head) + } + fn process_queue_submit(&mut self) -> Result<()> { if self.needs_reset() { return Ok(()); @@ -239,6 +254,14 @@ Setting device status to 'NEEDS_RESET' and stopping processing queues until rese return Ok(()); } }; + + let head = desc_chain.head_index(); + if Self::is_head_in_flight(&self.inflight_requests, &batch_inflight_requests, head) { + warn!("Guest reused virtio-blk head_index {head} while the chain was used"); + self.set_needs_reset(); + return Ok(()); + } + let mut request = Request::parse(&mut desc_chain, self.access_platform.as_deref()) .map_err(Error::RequestParsing)?;