pci: clamp sparse mmap holes to physical BAR

For VFIO devices with non page aligned MSI-X offsets,
fixup_msix_region() relocates MSI-X table and PBA offsets
into an enlarged virtual BAR by mutating msix.cap in place.
generate_sparse_areas() later reads those relocated offsets
to carve mmap holes, but receives the physical BAR size as
region_size. The relocated offsets exceed the physical BAR
boundary, and the kernel rejects the mmap with EINVAL.

Guard inter_ranges insertion with an offset < region_size
check so relocated entries are skipped. The full physical
BAR is mmapped as a single region. The relocated MSI-X in
the upper half of the virtual BAR remains trapped because
it has no mmap backing.

Linux kernel commit a32295c612c5 ("vfio-pci: Allow mapping
MSIX BAR") allows mmapping the entire BAR including the
MSI-X region when VFIO_REGION_INFO_CAP_MSIX_MAPPABLE is
advertised. The actual security guarantees come from IOMMU
isolation and interrupt remapping, not from filtering
MSI-X table accesses. QEMU follows the same pattern,
mmapping the entire physical BAR when MsixMappable is
present.

Fixes: #7898

Signed-off-by: Saravanan D <saravanand@crusoe.ai>
This commit is contained in:
Saravanan D
2026-03-25 21:44:33 -07:00
committed by Rob Bradford
parent 1bc49758a0
commit 9156758828

View File

@@ -1562,13 +1562,33 @@ impl VfioPciDevice {
let (offset, size) = msix.cap.table_range();
let offset = align_page_size_down(offset);
let size = align_page_size_up(size);
inter_ranges.insert(offset, size);
// MSI-X mmap region safety: when a device has a non page
// aligned MSI-X offset, fixup_msix_region() relocates MSI-X
// to the upper half of an enlarged virtual BAR, causing the
// offsets in msix.cap to exceed the physical BAR size. This
// check skips carving a hole, preventing invalid offsets from
// reaching the mmap path. With no holes,
// generate_sparse_areas() returns a single sparse region
// covering the entire physical BAR. The relocated MSI-X in
// the virtual BAR remains trapped because its upper half has
// no mmap backing. Exposing the physical MSI-X region through
// mmap is safe when the kernel advertises
// VFIO_REGION_INFO_CAP_MSIX_MAPPABLE. When MSI-X offsets are
// already page aligned, fixup_msix_region() does not relocate
// and this check is satisfied, so a hole is carved at the
// intended offset as before.
if offset < region_size {
inter_ranges.insert(offset, size);
}
}
if region_index == msix.cap.pba_bir() {
let (offset, size) = msix.cap.pba_range();
let offset = align_page_size_down(offset);
let size = align_page_size_up(size);
inter_ranges.insert(offset, size);
// See MSI-X mmap safety comment above.
if offset < region_size {
inter_ranges.insert(offset, size);
}
}
}