Commit 666dd3c9b1 for qemu.org
commit 666dd3c9b10a70eea4aae3c09b0f945175a7cb8e
Author: Cam Miller <cam@linux.ibm.com>
Date: Mon Sep 14 10:39:40 2026 -0400
system/ram-discard-manager: fix offset_within_address_space in replay_by_populated_state()
Fix bug inside replay_by_populated_state() that forgets to initialize
MemoryRegionSection field offset_within_address_space. Follow the
established pattern of calling memory_region_section_intersect_range()
to accomplish this task.
Prior to commit cc9c77f4ddf0 ("system/memory: implement
RamDiscardManager multi-source aggregation"),
replay_by_populated_state() had called
memory_region_section_intersect_range() in order to initialize
interdependent fields offset_within_address_space, offset_within_region,
and size together, as shown below.
s->offset_within_address_space += start - s->offset_within_region;
s->offset_within_region = start;
s->size = int128_sub(end, int128_make64(start));
cc9c77f4ddf0 reimplements replay_by_populated_state() initializing the
fields of the given MemoryRegionSection instance by hand instead of via
memory_region_section_intersect_range(). In doing so, it leaves
offset_within_address_space uninitialized for some reason, as you can
see below.
MemoryRegionSection subsection = {
.mr = section->mr,
.offset_within_region = offset,
.size = int128_make64(MIN(granularity, end_offset - offset)),
};
Consequently offset_within_address_space defaults to GPA 0x0, which is
incorrect. For example, on s390x, base RAM begins at GPA 0x0 and it is
problematic to report that a virtio-iommu MR section lives there
instead.
cc9c77f4ddf0 deliberately calls memory_region_section_intersect_range()
from other related code paths inside the same file, namely
replay_source_by_state() and rdl_populate_cb()/rdl_discard_cb(). It is
unclear why the new replay_by_populated_state() implementation does not
conform to this same pattern.
The effects of the bug include qemu crashes on multiple architectures.
The following assertion failure occurs when driving the
guest_phys_blocks_append() code path, for guests with virtio-mem device
that has some memory plugged.
DBG: guest_phys_block_add_section: predecessor->target_end=280000000 target_start=0
**
ERROR:../system/memory_mapping.c:222:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)
Bail out! ERROR:../system/memory_mapping.c:222:guest_phys_block_add_section: assertion failed: (predecessor->target_end <= target_start)
2026-09-11 16:03:57.405+0000: shutting down, reason=crashed
This crash can be triggered on x86 via the dump-guest-memory QMP
command. The same crash can be triggered on s390x by restoring VM State
that has been migrated to a local file. (I used libvirt to manage this
migration restore operation, namely command virsh managedsave then virsh
start.) Applying the fix resolved the crash on both platforms.
Cc: qemu-stable <qemu-stable@nongnu.org>
Fixes: cc9c77f4ddf0 ("system/memory: implement RamDiscardManager multi-source aggregation")
Reported-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
Signed-off-by: Cam Miller <cam@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/r/20260914-vmem_fix-v1-1-f69ef04a07b0@linux.ibm.com
Signed-off-by: Peter Xu <peterx@redhat.com>
diff --git a/system/ram-discard-manager.c b/system/ram-discard-manager.c
index 4e8816e5a2..e9a609e5cd 100644
--- a/system/ram-discard-manager.c
+++ b/system/ram-discard-manager.c
@@ -238,14 +238,15 @@ static int replay_by_populated_state(const RamDiscardManager *rdm,
}
} else {
if (in_run) {
- MemoryRegionSection run_section = {
- .mr = section->mr,
- .offset_within_region = run_start,
- .size = int128_make64(offset - run_start),
- };
- ret = replay_fn(&run_section, user_opaque);
- if (ret) {
- return ret;
+ MemoryRegionSection run_section = *section;
+
+ if (memory_region_section_intersect_range(&run_section,
+ run_start,
+ offset - run_start)) {
+ ret = replay_fn(&run_section, user_opaque);
+ if (ret) {
+ return ret;
+ }
}
in_run = false;
}
@@ -257,12 +258,12 @@ static int replay_by_populated_state(const RamDiscardManager *rdm,
}
if (in_run) {
- MemoryRegionSection run_section = {
- .mr = section->mr,
- .offset_within_region = run_start,
- .size = int128_make64(end_offset - run_start),
- };
- ret = replay_fn(&run_section, user_opaque);
+ MemoryRegionSection run_section = *section;
+
+ if (memory_region_section_intersect_range(&run_section, run_start,
+ end_offset - run_start)) {
+ ret = replay_fn(&run_section, user_opaque);
+ }
}
return ret;