mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: vhdx: reject overflowing region table entries
The region table overlap fix in RegionInfo::new computes each entry's end offset as `file_offset + length`. Both values are taken verbatim from the image, so a crafted or corrupt VHDX can set a file offset near u64::MAX and make that addition wrap. A wrapped end offset compares as a small value, which can slip a genuinely overlapping region past the half-open interval check that #8483 added. Use checked_add for the end offset and return a new RegionEntryOverflow error when it wraps, so a malformed entry is rejected instead of being folded into a valid-looking range. The computed end is now reused for the region_entries map so the bound is only calculated once. Add a regression test for a wrapping entry. Signed-off-by: Sayed Kaif <metsw24@gmail.com>
This commit is contained in:
@@ -72,6 +72,8 @@ pub enum VhdxHeaderError {
|
||||
ReadRegionTableHeader(#[source] io::Error),
|
||||
#[error("Failed to read region entries")]
|
||||
RegionEntryCollectionFailed,
|
||||
#[error("Region entry file offset ({0}) and length ({1}) overflow u64")]
|
||||
RegionEntryOverflow(u64 /* start */, usize /* length */),
|
||||
#[error("Overlapping regions found")]
|
||||
RegionOverlap,
|
||||
#[error("Reserved region has non-zero value")]
|
||||
@@ -264,7 +266,9 @@ impl RegionInfo {
|
||||
|
||||
offset += size_of::<RegionTableEntry>();
|
||||
let start = entry.file_offset;
|
||||
let end = start + entry.length as u64;
|
||||
let end = start.checked_add(entry.length as u64).ok_or(
|
||||
VhdxHeaderError::RegionEntryOverflow(start, entry.length as usize),
|
||||
)?;
|
||||
|
||||
for (region_ent_start, region_ent_end) in region_entries.iter() {
|
||||
if ranges_overlap(start, end, *region_ent_start, *region_ent_end) {
|
||||
@@ -272,7 +276,7 @@ impl RegionInfo {
|
||||
}
|
||||
}
|
||||
|
||||
region_entries.insert(entry.file_offset, entry.file_offset + entry.length as u64);
|
||||
region_entries.insert(start, end);
|
||||
|
||||
if entry.guid == BAT_GUID {
|
||||
if bat_entry.is_none() {
|
||||
@@ -560,4 +564,26 @@ mod tests {
|
||||
"expected RegionOverlap for an overlapping region table"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_region_info_rejects_overflowing_region() {
|
||||
// A region whose file offset plus length wraps past u64::MAX must be
|
||||
// rejected rather than silently producing a small end offset that
|
||||
// could mask a genuine overlap.
|
||||
let region_start = REGION_TABLE_1_START;
|
||||
let entries_at = region_start + size_of::<RegionTableHeader>() as u64;
|
||||
|
||||
let temp = TempFile::new().unwrap();
|
||||
let f = temp.into_file();
|
||||
f.set_len(entries_at + 64 * 1024).unwrap();
|
||||
f.write_all_at(®ion_entry(BAT_GUID, u64::MAX, 0x1000), entries_at)
|
||||
.unwrap();
|
||||
|
||||
let af = AlignedFile::new(f, false);
|
||||
let res = RegionInfo::new(&af, region_start, 1);
|
||||
assert!(
|
||||
matches!(res, Err(VhdxHeaderError::RegionEntryOverflow(..))),
|
||||
"expected RegionEntryOverflow for a wrapping region entry"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user