hw/vfio/container.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-)
vfio_container_dma_map() uses dma_map_file() whenever a RAMBlock has an
fd and the VFIO IOMMU backend supports file-based DMA mapping. That is
not correct for private file-backed guest RAM.
dma_map_file() resolves PFNs from the backing file, but private guest
RAM mappings (MAP_PRIVATE) can run on different PFNs than the file
because they are subject to copy-on-write (COW) anomalies. As a result,
using dma_map_file() on a privately mapped RAMBlock can program DMA
against pages that do not back QEMU's actual guest memory.
Fix this by using dma_map_file() only for shared mapped RAMBlocks
(MAP_SHARED) or RAM device regions.
Fixes: fb32965b6dd8 ("vfio/iommufd: use IOMMU_IOAS_MAP_FILE")
Reported-by: Farrah Chen <farrah.chen@intel.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220776
Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
Suggested-by: Cédric Le Goater <clg@redhat.com>
Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
---
Changes in v2:
- Extract the dma_map_file check into a helper.
- Add the missing RAM device case which is also allowed by dma_map_file.
---
hw/vfio/container.c | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/hw/vfio/container.c b/hw/vfio/container.c
index 4c2816b574..56bd9ac009 100644
--- a/hw/vfio/container.c
+++ b/hw/vfio/container.c
@@ -74,15 +74,43 @@ void vfio_address_space_insert(VFIOAddressSpace *space,
bcontainer->space = space;
}
+static bool vfio_container_can_dma_map_file(VFIOContainer *bcontainer,
+ MemoryRegion *mr, int *fd)
+{
+ VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
+ RAMBlock *rb = mr->ram_block;
+
+ if (!vioc->dma_map_file || !rb) {
+ return false;
+ }
+
+ *fd = qemu_ram_get_fd(rb);
+ if (*fd < 0) {
+ return false;
+ }
+
+ /*
+ * We can use IOMMU DMA mapping (IOMMU_IOAS_MAP_FILE) for :
+ *
+ * 1) Guest RAM blocks explicitly configured as shared (MAP_SHARED)
+ * 2) RAM device sub-regions (MMIO BARs)
+ *
+ * Private RAM mappings (MAP_PRIVATE) are strictly excluded. Because
+ * they are subject to copy-on-write (COW) anomalies, their underlying
+ * PFNs can permanently diverge from the backing file
+ */
+ return qemu_ram_is_shared(rb) || memory_region_is_ram_device(mr);
+}
+
int vfio_container_dma_map(VFIOContainer *bcontainer,
hwaddr iova, uint64_t size,
void *vaddr, bool readonly, MemoryRegion *mr)
{
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
- RAMBlock *rb = mr->ram_block;
- int mfd = rb ? qemu_ram_get_fd(rb) : -1;
+ int mfd;
- if (mfd >= 0 && vioc->dma_map_file) {
+ if (vfio_container_can_dma_map_file(bcontainer, mr, &mfd)) {
+ RAMBlock *rb = mr->ram_block;
unsigned long start = vaddr - qemu_ram_get_host_addr(rb);
unsigned long offset = qemu_ram_get_fd_offset(rb);
--
2.43.5
On 27.05.2026 13:11, Chenyi Qiang wrote:
> vfio_container_dma_map() uses dma_map_file() whenever a RAMBlock has an
> fd and the VFIO IOMMU backend supports file-based DMA mapping. That is
> not correct for private file-backed guest RAM.
>
> dma_map_file() resolves PFNs from the backing file, but private guest
> RAM mappings (MAP_PRIVATE) can run on different PFNs than the file
> because they are subject to copy-on-write (COW) anomalies. As a result,
> using dma_map_file() on a privately mapped RAMBlock can program DMA
> against pages that do not back QEMU's actual guest memory.
>
> Fix this by using dma_map_file() only for shared mapped RAMBlocks
> (MAP_SHARED) or RAM device regions.
>
> Fixes: fb32965b6dd8 ("vfio/iommufd: use IOMMU_IOAS_MAP_FILE")
> Reported-by: Farrah Chen <farrah.chen@intel.com>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220776
> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
> Suggested-by: Cédric Le Goater <clg@redhat.com>
> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
This looks like a qemu-stable material to me (10.2.x & 11.0.x).
I'm picking this up. Please let me know if I shouldn't.
Thanks,
/mjt
On 5/27/26 12:11, Chenyi Qiang wrote:
> vfio_container_dma_map() uses dma_map_file() whenever a RAMBlock has an
> fd and the VFIO IOMMU backend supports file-based DMA mapping. That is
> not correct for private file-backed guest RAM.
>
> dma_map_file() resolves PFNs from the backing file, but private guest
> RAM mappings (MAP_PRIVATE) can run on different PFNs than the file
> because they are subject to copy-on-write (COW) anomalies. As a result,
> using dma_map_file() on a privately mapped RAMBlock can program DMA
> against pages that do not back QEMU's actual guest memory.
>
> Fix this by using dma_map_file() only for shared mapped RAMBlocks
> (MAP_SHARED) or RAM device regions.
>
> Fixes: fb32965b6dd8 ("vfio/iommufd: use IOMMU_IOAS_MAP_FILE")
> Reported-by: Farrah Chen <farrah.chen@intel.com>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220776
> Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
> Suggested-by: Cédric Le Goater <clg@redhat.com>
> Signed-off-by: Chenyi Qiang <chenyi.qiang@intel.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
>
> ---
> Changes in v2:
> - Extract the dma_map_file check into a helper.
> - Add the missing RAM device case which is also allowed by dma_map_file.
> ---
> hw/vfio/container.c | 34 +++++++++++++++++++++++++++++++---
> 1 file changed, 31 insertions(+), 3 deletions(-)
>
> diff --git a/hw/vfio/container.c b/hw/vfio/container.c
> index 4c2816b574..56bd9ac009 100644
> --- a/hw/vfio/container.c
> +++ b/hw/vfio/container.c
> @@ -74,15 +74,43 @@ void vfio_address_space_insert(VFIOAddressSpace *space,
> bcontainer->space = space;
> }
>
> +static bool vfio_container_can_dma_map_file(VFIOContainer *bcontainer,
> + MemoryRegion *mr, int *fd)
> +{
> + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
> + RAMBlock *rb = mr->ram_block;
> +
> + if (!vioc->dma_map_file || !rb) {
> + return false;
> + }
> +
> + *fd = qemu_ram_get_fd(rb);
> + if (*fd < 0) {
> + return false;
> + }
> +
> + /*
> + * We can use IOMMU DMA mapping (IOMMU_IOAS_MAP_FILE) for :
> + *
> + * 1) Guest RAM blocks explicitly configured as shared (MAP_SHARED)
> + * 2) RAM device sub-regions (MMIO BARs)
> + *
> + * Private RAM mappings (MAP_PRIVATE) are strictly excluded. Because
> + * they are subject to copy-on-write (COW) anomalies, their underlying
> + * PFNs can permanently diverge from the backing file
> + */
> + return qemu_ram_is_shared(rb) || memory_region_is_ram_device(mr);
> +}
> +
> int vfio_container_dma_map(VFIOContainer *bcontainer,
> hwaddr iova, uint64_t size,
> void *vaddr, bool readonly, MemoryRegion *mr)
> {
> VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
> - RAMBlock *rb = mr->ram_block;
> - int mfd = rb ? qemu_ram_get_fd(rb) : -1;
> + int mfd;
>
> - if (mfd >= 0 && vioc->dma_map_file) {
> + if (vfio_container_can_dma_map_file(bcontainer, mr, &mfd)) {
> + RAMBlock *rb = mr->ram_block;
> unsigned long start = vaddr - qemu_ram_get_host_addr(rb);
> unsigned long offset = qemu_ram_get_fd_offset(rb);
>
© 2016 - 2026 Red Hat, Inc.