From 9156758828dd3774abbad245ce7fb8e2e9dc08db Mon Sep 17 00:00:00 2001 From: Saravanan D Date: Wed, 25 Mar 2026 21:44:33 -0700 Subject: [PATCH] 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 --- pci/src/vfio.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/pci/src/vfio.rs b/pci/src/vfio.rs index dbcf59fbe..f1d22ff63 100644 --- a/pci/src/vfio.rs +++ b/pci/src/vfio.rs @@ -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); + } } }