From 0597e729740a4213eed2c726c1481e8d97c6ba45 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Thu, 7 May 2026 14:42:41 +0200 Subject: [PATCH] virtio-devices: block: Surface virtqueue iterator errors to the worker queue.iter() errors used to be swallowed by handle_queue_iterator_error(), which marked the device as NEEDS_RESET and returned Ok so the worker kept running while disabled. spawn_virtio_thread now does the NEEDS_RESET marking when the worker exits with an error. Propagate the iterator error as Error::QueueIterator and escalate it to EpollHelperError::HandleEvent in process_queue_submit_and_signal so the worker exits. Per request errors stay logged. Drop the now unused handle_queue_iterator_error helper. No functional change for the guest. NEEDS_RESET is still set and the config interrupt is still raised on virtqueue corruption. Signed-off-by: Anatol Belski --- virtio-devices/src/block.rs | 49 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index 196d4d7c2..a0faa7cd4 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -50,7 +50,7 @@ use super::{ }; use crate::seccomp_filters::Thread; use crate::thread_helper::spawn_virtio_thread; -use crate::{GuestMemoryMmap, VirtioInterrupt, device_needs_reset, mark_device_needs_reset}; +use crate::{GuestMemoryMmap, VirtioInterrupt, device_needs_reset}; const SECTOR_SHIFT: u8 = 9; pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT; @@ -207,20 +207,6 @@ impl BlockEpollHandler { Ok(()) } - fn handle_queue_iterator_error(&mut self, err: &virtio_queue::Error) { - // The guest submitted a corrupted VirtQ request, and the error - // was logged during queue processing. Ignoring it would let the - // guest keep spamming the VMM with bad requests and trigger - // excessive error logging, so mark the device as NEEDS_RESET to - // stop request processing (see self.needs_reset() usage) until - // the guest resets and reactivates the device. - mark_device_needs_reset( - &self.device_status, - self.interrupt_cb.as_ref(), - format_args!("virtqueue error: {err:?}"), - ); - } - fn process_queue_submit(&mut self) -> Result<()> { if self.needs_reset() { return Ok(()); @@ -238,15 +224,13 @@ impl BlockEpollHandler { break; } processed += 1; - let mut desc_chain = match queue.iter(self.mem.memory()) { - Ok(mut iter) => match iter.next() { - Some(c) => c, - None => break, - }, - Err(err) => { - self.handle_queue_iterator_error(&err); - return Ok(()); - } + let mut desc_chain = match queue + .iter(self.mem.memory()) + .map_err(Error::QueueIterator)? + .next() + { + Some(c) => c, + None => break, }; let mut request = Request::parse(&mut desc_chain, self.access_platform.as_deref()) .map_err(Error::RequestParsing)?; @@ -408,9 +392,20 @@ impl BlockEpollHandler { } fn process_queue_submit_and_signal(&mut self) -> result::Result<(), EpollHelperError> { - // Per-request errors are logged but non-device fatal. - if let Err(e) = self.process_queue_submit() { - warn!("Failed to process queue (submit): {e:?}"); + match self.process_queue_submit() { + Ok(()) => {} + Err(e @ Error::QueueIterator(_)) => { + // Iterator errors mean the guest virtqueue itself is corrupted. + // Surface the failure so the worker exits and spawn_virtio_thread + // marks the device as NEEDS_RESET. + return Err(EpollHelperError::HandleEvent(anyhow!( + "Failed to process queue (submit): {e:?}" + ))); + } + Err(e) => { + // Per-request errors are logged but non-device fatal. + warn!("Failed to process queue (submit): {e:?}"); + } } self.try_signal_used_queue()