mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: bounds-check the cumulative descriptor length
Request::execute and Request::execute_async checked each data descriptor against `disk_nsectors` using the request's fixed start sector. With sector = disk_nsectors-1 and N descriptors of 512 bytes each, every descriptor passed (top = disk_nsectors) but the vectored I/O collectively read/wrote N*512 bytes starting at the last sector — N-1 sectors past EOF. For the io_uring/aio raw backends this lets the guest extend the host disk image beyond its provisioned size, exhausting the host filesystem. For fixed-VHD images (footer at end of file) the same chain overwrites the footer with guest-controlled bytes, corrupting the disk image. Replace the per-descriptor check with a chain-wide check_data_bounds(). Pre-validating the entire request before beginning the operation avoids having to unroll a partial submit. Signed-off-by: Dylan Reid <dgreid@fb.com>
This commit is contained in:
@@ -194,21 +194,12 @@ impl Request {
|
||||
mem: &vm_memory::GuestMemoryMmap<B>,
|
||||
serial: &[u8],
|
||||
) -> Result<u32, ExecuteError> {
|
||||
self.check_data_bounds(disk_nsectors)?;
|
||||
|
||||
disk.seek(SeekFrom::Start(self.sector << SECTOR_SHIFT))
|
||||
.map_err(ExecuteError::Seek)?;
|
||||
let mut len = 0;
|
||||
for (data_addr, data_len) in &self.data_descriptors {
|
||||
let mut top: u64 = u64::from(*data_len) / SECTOR_SIZE;
|
||||
if u64::from(*data_len) % SECTOR_SIZE != 0 {
|
||||
top += 1;
|
||||
}
|
||||
top = top
|
||||
.checked_add(self.sector)
|
||||
.ok_or(ExecuteError::BadRequest(Error::InvalidOffset))?;
|
||||
if top > disk_nsectors {
|
||||
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
||||
}
|
||||
|
||||
match self.request_type {
|
||||
RequestType::In => {
|
||||
let mut buf = vec![0u8; *data_len as usize];
|
||||
@@ -264,6 +255,8 @@ impl Request {
|
||||
let offset = (sector << SECTOR_SHIFT) as libc::off_t;
|
||||
let alignment = disk_image.alignment();
|
||||
|
||||
self.check_data_bounds(disk_nsectors)?;
|
||||
|
||||
let mut iovecs: SmallVec<[libc::iovec; DEFAULT_DESCRIPTOR_VEC_SIZE]> =
|
||||
SmallVec::with_capacity(self.data_descriptors.len());
|
||||
for &(data_addr, data_len) in &self.data_descriptors {
|
||||
@@ -275,17 +268,7 @@ impl Request {
|
||||
if data_len == 0 {
|
||||
continue;
|
||||
}
|
||||
let mut top: u64 = u64::from(data_len) / SECTOR_SIZE;
|
||||
if u64::from(data_len) % SECTOR_SIZE != 0 {
|
||||
top += 1;
|
||||
}
|
||||
let data_len = data_len as usize;
|
||||
top = top
|
||||
.checked_add(sector)
|
||||
.ok_or(ExecuteError::BadRequest(Error::InvalidOffset))?;
|
||||
if top > disk_nsectors {
|
||||
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
||||
}
|
||||
|
||||
let origin_ptr = mem
|
||||
.get_slice(data_addr, data_len)
|
||||
@@ -595,4 +578,30 @@ impl Request {
|
||||
pub fn request_type(&self) -> RequestType {
|
||||
self.request_type
|
||||
}
|
||||
|
||||
/// For In and Out requests, checks that the descriptors collectively fit in a backing disk of
|
||||
/// the given size. Returns `Ok(())` if they fit, or `ExecuteError::BadRequest` otherwise.
|
||||
fn check_data_bounds(&self, disk_nsectors: u64) -> Result<(), ExecuteError> {
|
||||
if !matches!(self.request_type, RequestType::In | RequestType::Out) {
|
||||
return Ok(());
|
||||
}
|
||||
let mut total_bytes: u64 = 0;
|
||||
for (_, data_len) in &self.data_descriptors {
|
||||
total_bytes = total_bytes
|
||||
.checked_add(u64::from(*data_len))
|
||||
.ok_or(ExecuteError::BadRequest(Error::InvalidOffset))?;
|
||||
}
|
||||
if total_bytes == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let total_sectors = total_bytes.div_ceil(SECTOR_SIZE);
|
||||
let end_sector = self
|
||||
.sector
|
||||
.checked_add(total_sectors)
|
||||
.ok_or(ExecuteError::BadRequest(Error::InvalidOffset))?;
|
||||
if end_sector > disk_nsectors {
|
||||
return Err(ExecuteError::BadRequest(Error::InvalidOffset));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user