system/memory: add RamDiscardManager reference counting and cleanup
Listeners now hold a reference to the RamDiscardManager, ensuring it stays alive while listeners are registered. The RDM is eagerly freed when the last source and listener are removed, and also unreffed during MemoryRegion finalization as a safety net. This completes the TODO left in the previous commit and prevents both use-after-free and memory leaks of the RamDiscardManager. Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Link: https://lore.kernel.org/r/20260604-rdm5-v5-9-5768e6a0943d@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com>
Marc-André Lureau committed
Jun 4, 2026 at 17:43 UTC
a947cf84c5ae1663569a2df09ebafe3210ad3474
2 files changed
+13
-3
system/memory.c
+11
-3
@@ -1771,6 +1771,7 @@ static void memory_region_finalize(Object *obj)
1771
memory_region_clear_coalescing(mr);
1772
g_free((char *)mr->name);
1773
g_free(mr->ioeventfds);
1774
+ object_unref(mr->rdm);
1775
}
1776
1777
Object *memory_region_owner(const MemoryRegion *mr)
@@ -2084,11 +2085,18 @@ int memory_region_add_ram_discard_source(MemoryRegion *mr,
2085
int memory_region_del_ram_discard_source(MemoryRegion *mr,
2086
RamDiscardSource *source)
2087
{
2088
+ int ret;
2089
g_assert(mr->rdm);
2090
2089
- return ram_discard_manager_del_source(mr->rdm, source);
2090
-
2091
- /* if there is no source and no listener left, we could free rdm */
2091
+ ret = ram_discard_manager_del_source(mr->rdm, source);
2092
+ if (ret != 0) {
2093
+ return ret;
2094
+ }
2095
+ if (QLIST_EMPTY(&mr->rdm->source_list) && QLIST_EMPTY(&mr->rdm->rdl_list)) {
2096
+ object_unref(mr->rdm);
2097
+ mr->rdm = NULL;
2098
+ }
2099
+ return 0;
2100
}
2101
2102
/* Called with rcu_read_lock held. */
system/ram-discard-manager.c
+2
@@ -549,6 +549,7 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm,
549
550
g_assert(section->mr == rdm->mr);
551
552
+ object_ref(rdm);
553
rdl->section = memory_region_section_new_copy(section);
554
QLIST_INSERT_HEAD(&rdm->rdl_list, rdl, next);
555
@@ -570,6 +571,7 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
571
memory_region_section_free_copy(rdl->section);
572
rdl->section = NULL;
573
QLIST_REMOVE(rdl, next);
574
+ object_unref(rdm);
575
}
576
577
int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *rdm)