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


Reply via email to