From 4192f5101b45a72263e71238903a06c6821c7cc3 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Wed, 13 May 2026 14:31:48 +0200 Subject: [PATCH] block: Validate descriptor buffer ranges via checked iterator Switch Request::parse over to the CheckedDescriptorIter helper from vm-virtio so the block crate validates each descriptor's translated (addr, len) range against guest memory through the same centralized path used by virtio-devices. Any descriptor whose buffer is not fully backed by guest RAM is now rejected before any I/O is set up against it. Signed-off-by: Anatol Belski --- block/src/io/request.rs | 45 ++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/block/src/io/request.rs b/block/src/io/request.rs index b7529e922..0db5e710f 100644 --- a/block/src/io/request.rs +++ b/block/src/io/request.rs @@ -27,7 +27,8 @@ use vm_memory::{ Address as _, Bytes as _, GuestAddress, GuestMemory as _, GuestMemoryError, GuestMemoryLoadGuard, }; -use vm_virtio::{AccessPlatform, Translatable as _}; +use vm_virtio::AccessPlatform; +use vm_virtio::checked_descriptor::DescriptorChainExt; use crate::async_io::{ AsyncIo, AsyncIoCompletion, AsyncIoOperation, GuestMemoryTarget, OwnedIoBuffer, @@ -84,10 +85,11 @@ impl Request { access_platform: Option<&dyn AccessPlatform>, ) -> Result { let hdr_desc = desc_chain - .next() - .ok_or(Error::DescriptorChainTooShort) - .inspect_err(|_| { + .next_checked(access_platform) + .map_err(|addr| Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(addr)))? + .ok_or_else(|| { error!("Missing head descriptor"); + Error::DescriptorChainTooShort })?; // The head contains the request type which MUST be readable. @@ -95,10 +97,7 @@ impl Request { return Err(Error::UnexpectedWriteOnlyDescriptor); } - let hdr_desc_addr = hdr_desc - .addr() - .translate_gva(access_platform, hdr_desc.len() as usize) - .map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?; + let hdr_desc_addr = hdr_desc.addr(); let mut req = Request { request_type: request_type(desc_chain.memory(), hdr_desc_addr)?, @@ -111,10 +110,11 @@ impl Request { let status_desc; let mut desc = desc_chain - .next() - .ok_or(Error::DescriptorChainTooShort) - .inspect_err(|_| { + .next_checked(access_platform) + .map_err(|addr| Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(addr)))? + .ok_or_else(|| { error!("Only head descriptor present: request = {req:?}"); + Error::DescriptorChainTooShort })?; if desc.has_next() { @@ -136,17 +136,15 @@ impl Request { return Err(Error::UnexpectedReadOnlyDescriptor); } - req.data_descriptors.push(( - desc.addr() - .translate_gva(access_platform, desc.len() as usize) - .map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?, - desc.len(), - )); + req.data_descriptors.push((desc.addr(), desc.len())); desc = desc_chain - .next() - .ok_or(Error::DescriptorChainTooShort) - .inspect_err(|_| { + .next_checked(access_platform) + .map_err(|addr| { + Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(addr)) + })? + .ok_or_else(|| { error!("DescriptorChain corrupted: request = {req:?}"); + Error::DescriptorChainTooShort })?; } status_desc = desc; @@ -164,14 +162,11 @@ impl Request { return Err(Error::UnexpectedReadOnlyDescriptor); } - if status_desc.len() < 1 { + if status_desc.is_empty() { return Err(Error::DescriptorLengthTooSmall); } - req.status_addr = status_desc - .addr() - .translate_gva(access_platform, status_desc.len() as usize) - .map_err(|e| Error::GuestMemory(GuestMemoryError::IOError(e)))?; + req.status_addr = status_desc.addr(); Ok(req) }