From fbcf2fd6b0badcde33b183886c3d35f5310d0025 Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Fri, 24 Apr 2026 17:05:27 -0700 Subject: [PATCH] virtio-devices: block: cap submit-loop iterations to virtqueue size process_queue_submit's drain loop builds a fresh queue.iter() per iteration, which re-reads the guest avail index on every call and has no per-call cap (the per-iter gap check in virtio-queue only protects against avail_idx jumping more than queue_size between two reads). In theory, a malicous or buggy guest could keep adding descriptors and cause this loop to overflow the iouring submit queue. Cap a single drain at queue_size. A spec-compliant driver never produces more than queue_size outstanding entries simultaneously, so the cap is invisible to well-behaved guests. Signed-off-by: Dylan Reid --- virtio-devices/src/block.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index 60ffade93..09d5d9692 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -235,10 +235,18 @@ Setting device status to 'NEEDS_RESET' and stopping processing queues until rese return Ok(()); } let queue = &mut self.queue; + let queue_size = queue.size(); let mut batch_requests = Vec::new(); let mut batch_inflight_requests = Vec::new(); + let mut processed = 0; loop { + // Cap a single drain at the virtqueue size. A compliant driver won't submit more that + // queue_size, but a buggy or malicious one can keep adding as the VMM is reading. + if processed >= queue_size { + break; + } + processed += 1; let mut desc_chain = match queue.iter(self.mem.memory()) { Ok(mut iter) => match iter.next() { Some(c) => c,