From: Marc-André Lureau 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. Signed-off-by: Marc-André Lureau --- system/memory.c | 7 +++++-- system/ram-discard-manager.c | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/system/memory.c b/system/memory.c index 8a4cb7b59ac..664d24109ab 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1817,6 +1817,7 @@ static void memory_region_finalize(Object *obj) memory_region_clear_coalescing(mr); g_free((char *)mr->name); g_free(mr->ioeventfds); + object_unref(mr->rdm); } Object *memory_region_owner(MemoryRegion *mr) @@ -2123,8 +2124,10 @@ void memory_region_del_ram_discard_source(MemoryRegion *mr, g_assert(mr->rdm); ram_discard_manager_del_source(mr->rdm, source); - - /* if there is no source and no listener left, we could free rdm */ + if (QLIST_EMPTY(&mr->rdm->source_list) && QLIST_EMPTY(&mr->rdm->rdl_list)) { + object_unref(mr->rdm); + mr->rdm = NULL; + } } /* Called with rcu_read_lock held. */ diff --git a/system/ram-discard-manager.c b/system/ram-discard-manager.c index 5592bfd3486..904a98cbef1 100644 --- a/system/ram-discard-manager.c +++ b/system/ram-discard-manager.c @@ -549,6 +549,7 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm, g_assert(section->mr == rdm->mr); + object_ref(rdm); rdl->section = memory_region_section_new_copy(section); QLIST_INSERT_HEAD(&rdm->rdl_list, rdl, next); @@ -570,6 +571,7 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, memory_region_section_free_copy(rdl->section); rdl->section = NULL; QLIST_REMOVE(rdl, next); + object_unref(rdm); } int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *rdm) -- 2.53.0