From: Manish Honap <[email protected]>

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 <[email protected]>
Assisted-by: LLM
Signed-off-by: Manish Honap <[email protected]>
---
 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


Reply via email to