From 5f7464524d0fb2c25c9bacfb550df92bef9bb3bf Mon Sep 17 00:00:00 2001 From: Keqian Zhu Date: Tue, 26 Mar 2024 14:11:05 +0800 Subject: [PATCH] system/physmem: Fix possible double free when destroy cpu as address_space_destroy() and g_free_rcu() both operate cpuas->as at rcu thread context asynchronously, each one is a rcu task that have different callback (the first callback is do_address_ space_destroy() and the second callback is g_free()). It's possible that while the first task is pending and the second task overwrites the rcu callback (as the second task operates on the same object). Then the g_free will be called twice on cpuas->as. Signed-off-by: Keqian Zhu --- include/exec/memory.h | 1 + system/memory.c | 3 +++ system/physmem.c | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index e131c2682c..91c42c9a6a 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1114,6 +1114,7 @@ struct AddressSpace { struct rcu_head rcu; char *name; MemoryRegion *root; + bool free_in_rcu; /* Accessed via RCU. */ struct FlatView *current_map; diff --git a/system/memory.c b/system/memory.c index 798b6c0a17..fb817e54bc 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3130,6 +3130,9 @@ static void do_address_space_destroy(AddressSpace *as) g_free(as->name); g_free(as->ioeventfds); memory_region_unref(as->root); + if (as->free_in_rcu) { + g_free(as); + } } void address_space_destroy(AddressSpace *as) diff --git a/system/physmem.c b/system/physmem.c index 299174ad91..cbe838f203 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -788,8 +788,8 @@ void cpu_address_space_destroy(CPUState *cpu, int asidx) memory_listener_unregister(&cpuas->tcg_as_listener); } + cpuas->as->free_in_rcu = true; address_space_destroy(cpuas->as); - g_free_rcu(cpuas->as, rcu); if (cpu->cpu_ases_ref_count == 1) { g_free(cpu->cpu_ases); -- 2.27.0