]> git.ipfire.org Git - thirdparty/qemu.git/commitdiff
target/arm/hvf, target/i386/hvf: Pass MR-relative offset to memory_region_set_dirty()
authorScott J. Goldman <scottjgo@gmail.com>
Mon, 27 Apr 2026 23:21:15 +0000 (16:21 -0700)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 30 Apr 2026 13:50:18 +0000 (15:50 +0200)
Both the arm and i386 hvf accelerators have the same bug in their
dirty-page logging path: the address fed to memory_region_set_dirty()
is computed as "<ipa,gpa>_page + xlat", but memory_region_set_dirty()
expects an offset relative to the start of the resolved MemoryRegion.
address_space_translate() already returns that offset in xlat, while
ipa_page / gpa_page is the guest-physical (system address space)
address.

Adding the two together produces a bogus offset that for any non-
trivial RAM size walks well past the end of the MemoryRegion's dirty
bitmap. With dirty logging active (e.g. live migration on a guest
with several GB of RAM), this triggers an out-of-bounds atomic write
inside bitmap_set_atomic() and crashes the source QEMU as soon as the
guest writes to RAM:

    Thread .. 'CPU N/HVF', stop reason = EXC_BAD_ACCESS ...
      bitmap_set_atomic at bitmap.c:213
      physical_memory_set_dirty_range at physmem.c:1038
      memory_region_set_dirty at memory.c:2191
      hvf_handle_exception at hvf.c

Fix it by passing only the MR-relative offset xlat. ipa_page /
gpa_page is still the right argument to hvf_unprotect_dirty_range(),
which works on the guest-physical address space.

Signed-off-by: Scott J. Goldman <scottjgo@gmail.com>
Link: https://lore.kernel.org/r/20260427232116.50586-2-scottjgo@gmail.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
target/arm/hvf/hvf.c
target/i386/hvf/hvf.c

index 678afe5c8e1c96259c7af0e35401fcb2eef28308..2146c5c87c67ceb1db878b2ee13c0a0713585877 100644 (file)
@@ -2159,7 +2159,7 @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp)
                 assert(!mr->readonly);
 
                 if (memory_region_get_dirty_log_mask(mr)) {
-                    memory_region_set_dirty(mr, ipa_page + xlat, page_size);
+                    memory_region_set_dirty(mr, xlat, page_size);
                     hvf_unprotect_dirty_range(ipa_page, page_size);
                 }
 
index c0d028b1473c4a3e71c6e18b66681f6fadbe364c..cdc8bd195046773f8229a0af30b6909fe3c9a780 100644 (file)
@@ -146,7 +146,7 @@ static bool ept_emulation_fault(CPUState *cs, uint64_t gpa, uint64_t ept_qual)
     if (write && memory_region_get_dirty_log_mask(mr)) {
         uintptr_t page_size = qemu_real_host_page_size();
 
-        memory_region_set_dirty(mr, gpa_page + xlat, page_size);
+        memory_region_set_dirty(mr, xlat, page_size);
         hvf_unprotect_dirty_range(gpa_page, page_size);
     }