mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: vhdx: validate buffer length against logical sector size
Reject buf.len() that isn't sector_count * logical_sector_size in io::read/write (VhdxIoError::InvalidBufferLength), and reject buf.len() not a multiple of the sector size in Vhdx's Read/Write impl, before it's converted to a sector count/index. Signed-off-by: Alexander Lvov <alexander.lvov.git@gmail.com>
This commit is contained in:
committed by
Rob Bradford
parent
8d6a38770f
commit
013604319d
@@ -19,6 +19,8 @@ pub enum VhdxIoError {
|
||||
InvalidBatEntryState,
|
||||
#[error("Invalid BAT entry count")]
|
||||
InvalidBatIndex,
|
||||
#[error("Buffer length does not match the requested sector count")]
|
||||
InvalidBufferLength,
|
||||
#[error("Invalid disk size")]
|
||||
InvalidDiskSize,
|
||||
#[error("Failed reading sector blocks from file {0}")]
|
||||
@@ -95,6 +97,12 @@ pub(super) fn read(
|
||||
if disk_spec.has_parent {
|
||||
return Err(VhdxIoError::UnsupportedMode);
|
||||
}
|
||||
let expected_len = sector_count
|
||||
.checked_mul(disk_spec.logical_sector_size as u64)
|
||||
.ok_or(VhdxIoError::InvalidBufferLength)?;
|
||||
if buf.len() as u64 != expected_len {
|
||||
return Err(VhdxIoError::InvalidBufferLength);
|
||||
}
|
||||
|
||||
let mut read_count: usize = 0;
|
||||
while sector_count > 0 {
|
||||
@@ -147,6 +155,12 @@ pub(super) fn write(
|
||||
if disk_spec.has_parent {
|
||||
return Err(VhdxIoError::UnsupportedMode);
|
||||
}
|
||||
let expected_len = sector_count
|
||||
.checked_mul(disk_spec.logical_sector_size as u64)
|
||||
.ok_or(VhdxIoError::InvalidBufferLength)?;
|
||||
if buf.len() as u64 != expected_len {
|
||||
return Err(VhdxIoError::InvalidBufferLength);
|
||||
}
|
||||
|
||||
let mut write_count: usize = 0;
|
||||
while sector_count > 0 {
|
||||
@@ -274,4 +288,24 @@ mod tests {
|
||||
f.file().read_exact_at(&mut readback, DATA_OFFSET).unwrap();
|
||||
assert_eq!(readback, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_short_buffer_is_rejected() {
|
||||
let (f, disk_spec, bat) = fixture();
|
||||
|
||||
let mut buf = vec![0u8; SECTOR_SIZE as usize - 1];
|
||||
let err = read(&f, &mut buf, &disk_spec, &bat, 0, 1).unwrap_err();
|
||||
|
||||
assert!(matches!(err, VhdxIoError::InvalidBufferLength));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_short_buffer_is_rejected() {
|
||||
let (f, mut disk_spec, mut bat) = fixture();
|
||||
|
||||
let data = vec![0xCDu8; SECTOR_SIZE as usize - 1];
|
||||
let err = write(&f, &data, &mut disk_spec, 0, &mut bat, 0, 1).unwrap_err();
|
||||
|
||||
assert!(matches!(err, VhdxIoError::InvalidBufferLength));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,18 @@ impl Read for Vhdx {
|
||||
/// Wrapper function to satisfy Read trait implementation for VHDx disk.
|
||||
/// Convert the offset to sector index and buffer length to sector count.
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||
let sector_count = (buf.len() as u64).div_ceil(self.disk_spec.logical_sector_size as u64);
|
||||
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
||||
let sector_size = self.disk_spec.logical_sector_size as u64;
|
||||
if !(buf.len() as u64).is_multiple_of(sector_size) {
|
||||
return Err(IoError::new(
|
||||
IoErrorKind::InvalidInput,
|
||||
format!(
|
||||
"Read buffer length {} is not a multiple of the {sector_size}-byte logical sector size",
|
||||
buf.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
let sector_count = buf.len() as u64 / sector_size;
|
||||
let sector_index = self.current_offset / sector_size;
|
||||
|
||||
let result = io::read(
|
||||
&self.aligned,
|
||||
@@ -129,8 +139,18 @@ impl Write for Vhdx {
|
||||
/// Wrapper function to satisfy Write trait implementation for VHDx disk.
|
||||
/// Convert the offset to sector index and buffer length to sector count.
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
|
||||
let sector_count = (buf.len() as u64).div_ceil(self.disk_spec.logical_sector_size as u64);
|
||||
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
||||
let sector_size = self.disk_spec.logical_sector_size as u64;
|
||||
if !(buf.len() as u64).is_multiple_of(sector_size) {
|
||||
return Err(IoError::new(
|
||||
IoErrorKind::InvalidInput,
|
||||
format!(
|
||||
"Write buffer length {} is not a multiple of the {sector_size}-byte logical sector size",
|
||||
buf.len()
|
||||
),
|
||||
));
|
||||
}
|
||||
let sector_count = buf.len() as u64 / sector_size;
|
||||
let sector_index = self.current_offset / sector_size;
|
||||
|
||||
if self.first_write {
|
||||
self.first_write = false;
|
||||
@@ -305,4 +325,42 @@ mod tests {
|
||||
assert_eq!(vhdx.read(&mut readback).unwrap(), readback.len());
|
||||
assert_eq!(readback, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_misaligned_buffer_is_rejected() {
|
||||
let Some(tf) = create_dynamic_vhdx(16) else {
|
||||
eprintln!("skipping read_misaligned_buffer_is_rejected: qemu-img unavailable");
|
||||
return;
|
||||
};
|
||||
|
||||
let file = fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(tf.as_path())
|
||||
.unwrap();
|
||||
let mut vhdx = Vhdx::new(file, false).unwrap();
|
||||
|
||||
let mut buf = vec![0u8; vhdx.disk_spec.logical_sector_size as usize - 1];
|
||||
let err = vhdx.read(&mut buf).unwrap_err();
|
||||
assert_eq!(err.kind(), IoErrorKind::InvalidInput);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_misaligned_buffer_is_rejected() {
|
||||
let Some(tf) = create_dynamic_vhdx(16) else {
|
||||
eprintln!("skipping write_misaligned_buffer_is_rejected: qemu-img unavailable");
|
||||
return;
|
||||
};
|
||||
|
||||
let file = fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(tf.as_path())
|
||||
.unwrap();
|
||||
let mut vhdx = Vhdx::new(file, false).unwrap();
|
||||
|
||||
let buf = vec![0u8; vhdx.disk_spec.logical_sector_size as usize - 1];
|
||||
let err = vhdx.write(&buf).unwrap_err();
|
||||
assert_eq!(err.kind(), IoErrorKind::InvalidInput);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user