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

