Skip to content

Commit 47fc365

Browse files
MichalTarnackiweb-flow
authored andcommitted
fix(virtio/mem): reject BAR regions overlapping TD private DRAM
Virtio PCI capability parsing built MemoryRegion instances directly from BAR base, offset, and length values, all of which are controlled by the untrusted VMM. MemoryRegion::new only guarded against integer overflow and never compared the resulting [base, base + length) range against the TD's private DRAM. A malicious host could therefore program a BAR so that a common/notify/device-config region or the MSI-X table pointed into guest-private memory. In a cfg(not(tdcall)) build the subsequent read/write_volatile would then read or corrupt TD-private secrets (INV-4 / RC-066). Add region_overlaps_private_dram() in the pci crate, which uses the existing E820 memory map to detect overlap with platform RAM and rejects any region below 1 MiB. Enforce it in MemoryRegion::new so that all four BAR-derived call sites in VirtioPciTransport::init fail closed with VirtioError::InvalidParameter. The check is purely arithmetic and applies regardless of the tdcall feature; it is skipped only under the fuzz feature, which is not a security boundary. Signed-off-by: Michal Tarnacki <michal.tarnacki@intel.com> Co-authored-by: GitHub Copilot <noreply@github.com>
1 parent f815df8 commit 47fc365

2 files changed

Lines changed: 39 additions & 0 deletions

File tree

src/devices/pci/src/mmio.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,39 @@ pub fn init_mmio() {
3838
*MMIO64.lock() = MMIO64_START;
3939
}
4040

41+
/// Device MMIO is never allowed to live below 1 MiB. That range is reserved
42+
/// for legacy low memory and is not a valid MMIO aperture.
43+
pub const MMIO_LOW_LIMIT: u64 = 0x10_0000;
44+
45+
/// Returns `true` if `[base, base + length)` hits RAM or the low 1 MiB.
46+
/// Defense in depth for host-controlled BARs.
47+
pub fn region_overlaps_private_dram(base: u64, length: u64) -> bool {
48+
let end = match base.checked_add(length) {
49+
Some(end) => end,
50+
None => return true,
51+
};
52+
53+
if base < MMIO_LOW_LIMIT {
54+
return true;
55+
}
56+
57+
let memory_map = MEMORY_MAP.lock();
58+
for region in memory_map.iter() {
59+
let region_end = match region.addr.checked_add(region.size) {
60+
Some(region_end) => region_end,
61+
None => return true,
62+
};
63+
64+
// Overlap test for the half-open intervals [base, end) and
65+
// [region.addr, region_end).
66+
if base < region_end && end > region.addr {
67+
return true;
68+
}
69+
}
70+
71+
false
72+
}
73+
4174
#[cfg(feature = "fuzz")]
4275
pub fn alloc_mmio32(size: u32) -> Result<u32> {
4376
let cur = *MMIO_OFFSET.lock();

src/devices/virtio/src/mem.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ impl MemoryRegion {
3333
pub fn new(base: u64, length: u64) -> Option<MemoryRegion> {
3434
let _ = base.checked_add(length)?;
3535

36+
// Reject host-controlled BARs that hit private DRAM or the low 1 MiB.
37+
#[cfg(not(feature = "fuzz"))]
38+
if pci::region_overlaps_private_dram(base, length) {
39+
return None;
40+
}
41+
3642
Some(MemoryRegion { base, length })
3743
}
3844

0 commit comments

Comments
 (0)