[PATCH v6 10/31] vfio/pci: Refuse ioeventfds and dma-buf exports over excluded ranges

From: mhonap

Date: Fri Oct 16 2026 - 12:29:57 EST


From: Manish Honap <mhonap@xxxxxxxxxx>

vfio_pci_bar_rw() keeps the excluded ranges of a BAR off the device, but
two other paths reach BAR MMIO without it.

An ioeventfd writes the BAR directly when it fires, through
vfio_pci_core_iowriteN(). Refuse an ioeventfd whose range overlaps a
write-excluded range, as one over the MSI-X table is already refused.

A dma-buf export of a BAR range lets an importer map it for DMA, which
reaches a range that the BAR keeps off mmap. Refuse an export whose
ranges overlap an mmap-excluded range, so the dma-buf follows the same
rule as mmap.

Suggested-by: Alex Williamson <alex@xxxxxxxxxxx>
Assisted-by: LLM
Signed-off-by: Manish Honap <mhonap@xxxxxxxxxx>
---
drivers/vfio/pci/vfio_pci_core.c | 4 ++--
drivers/vfio/pci/vfio_pci_dmabuf.c | 14 ++++++++++++++
drivers/vfio/pci/vfio_pci_priv.h | 3 +++
drivers/vfio/pci/vfio_pci_rdwr.c | 9 +++++++++
4 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index f099a294af34..211fff6045e2 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -1107,8 +1107,8 @@ static void vfio_pci_free_excluded_ranges(struct vfio_pci_core_device *vdev)
}

/* True when [start, start + len) on @bar overlaps a range with any of @flags. */
-static bool vfio_pci_bar_range_excluded(struct vfio_pci_core_device *vdev,
- int bar, u64 start, u64 len, u32 flags)
+bool vfio_pci_bar_range_excluded(struct vfio_pci_core_device *vdev, int bar,
+ u64 start, u64 len, u32 flags)
{
struct vfio_pci_excluded_range *range;

diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
index c16f460c01d6..8606c15ab2fe 100644
--- a/drivers/vfio/pci/vfio_pci_dmabuf.c
+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
@@ -260,6 +260,20 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
if (ret)
goto err_free_ranges;

+ /*
+ * A DMA mapping built from the dma-buf would reach a range that the
+ * BAR keeps off mmap, so refuse such a range here as mmap does.
+ */
+ for (u32 i = 0; i < get_dma_buf.nr_ranges; i++) {
+ if (vfio_pci_bar_range_excluded(vdev, get_dma_buf.region_index,
+ dma_ranges[i].offset,
+ dma_ranges[i].length,
+ VFIO_PCI_EXCLUDE_MMAP)) {
+ ret = -EINVAL;
+ goto err_free_ranges;
+ }
+ }
+
priv = kzalloc_obj(*priv);
if (!priv) {
ret = -ENOMEM;
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index 53544e0cbedf..09a68c99b43c 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -52,6 +52,9 @@ struct vfio_pci_excluded_range {
u32 flags;
};

+bool vfio_pci_bar_range_excluded(struct vfio_pci_core_device *vdev, int bar,
+ u64 start, u64 len, u32 flags);
+
#ifdef CONFIG_VFIO_PCI_VGA
ssize_t vfio_pci_vga_rw(struct vfio_pci_core_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index 93823c6391f6..ef098f0b1401 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -476,6 +476,15 @@ int vfio_pci_ioeventfd(struct vfio_pci_core_device *vdev, loff_t offset,
pos >= vdev->msix_offset + vdev->msix_size))
return -EINVAL;

+ /*
+ * The ioeventfd fire path writes the BAR directly, without the
+ * excluded-range filter of vfio_pci_bar_rw(), so refuse a write-excluded
+ * range here.
+ */
+ if (vfio_pci_bar_range_excluded(vdev, bar, pos, count,
+ VFIO_PCI_EXCLUDE_WRITE))
+ return -EINVAL;
+
if (count == 8)
return -EINVAL;

--
2.53.0