In vfio_listener_region_del(), when dirty tracking is active and a
writable RAM section is deleted, a synthetic IOMMUTLBEntry is built to
flush dirty pages. Setting translated_addr to the IOVA (GPA) is only
correct for identity-mapped regions where GPA == ram_addr_t.
For RAM sections with GPA far above main RAM (e.g., nested VT-d interrupt
remapping table at 58 TB), translated_addr is too large, causing a crash
in physical_memory_set_dirty_lebitmap() when indexing beyond the allocated
dirty memory blocks array :
bitmap_set_atomic(map=NULL, start=1, nr=1)
physical_memory_set_dirty_range(start=0x380004040000, length=4096)
physical_memory_set_dirty_lebitmap(start=0x380004040000, pages=3)
vfio_container_query_dirty_bitmap(translated_addr=0x380004040000)
vfio_legacy_dma_unmap_one(iova=0x380004040000, size=12288)
vfio_listener_region_del()
Fix this by setting translated_addr to the ram_addr_t of the section, which
is consistent with other vfio dirty tracking code:
translated_addr = memory_region_get_ram_addr(section->mr) +
section->offset_within_region;
Cc: Zhenzhong Duan <[email protected]>
Fixes: 6e360c06176c ("vfio/listener: Add missing dirty tracking in region_del")
Link: https://lore.kernel.org/qemu-devel/[email protected]
Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Reviewed-by: Zhenzhong Duan <[email protected]>
Tested-by: Zhenzhong Duan <[email protected]>
Signed-off-by: Cédric Le Goater <[email protected]>
---
hw/vfio/listener.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c
index
109b5d61af4a09cf22f0ebbb9f3fc4ca87295b38..c19600e980a8d02217b5d9df4aca8f879ab2c5d5
100644
--- a/hw/vfio/listener.c
+++ b/hw/vfio/listener.c
@@ -731,7 +731,7 @@ static void vfio_listener_region_del(MemoryListener
*listener,
}
/*
- * Fake an IOTLB entry for writable identity mapping which is needed
+ * Fake an IOTLB entry for writable RAM sections which is needed
* by dirty tracking when switch out of PT domain. In fact, in
* unmap_bitmap, only translated_addr field is used to set dirty
* bitmap.
@@ -746,7 +746,8 @@ static void vfio_listener_region_del(MemoryListener
*listener,
if (global_dirty_tracking && memory_region_is_ram(section->mr) &&
!section->readonly) {
entry.iova = iova;
- entry.translated_addr = iova;
+ entry.translated_addr = memory_region_get_ram_addr(section->mr) +
+ section->offset_within_region;
iotlb = &entry;
}
--
2.54.0