Skip to content

Commit 4731f89

Browse files
cotastsquad
authored andcommitted
cpu: free cpu->tb_jmp_cache with RCU
Fixes the appended use-after-free. The root cause is that during tb invalidation we use CPU_FOREACH, and therefore to safely free a vCPU we must wait for an RCU grace period to elapse. $ x86_64-linux-user/qemu-x86_64 tests/tcg/x86_64-linux-user/munmap-pthread ================================================================= ==1800604==ERROR: AddressSanitizer: heap-use-after-free on address 0x62d0005f7418 at pc 0x5593da6704eb bp 0x7f4961a7ac70 sp 0x7f4961a7ac60 READ of size 8 at 0x62d0005f7418 thread T2 #0 0x5593da6704ea in tb_jmp_cache_inval_tb ../accel/tcg/tb-maint.c:244 #1 0x5593da6704ea in do_tb_phys_invalidate ../accel/tcg/tb-maint.c:290 #2 0x5593da670631 in tb_phys_invalidate__locked ../accel/tcg/tb-maint.c:306 #3 0x5593da670631 in tb_invalidate_phys_page_range__locked ../accel/tcg/tb-maint.c:542 #4 0x5593da67106d in tb_invalidate_phys_range ../accel/tcg/tb-maint.c:614 #5 0x5593da6a64d4 in target_munmap ../linux-user/mmap.c:766 #6 0x5593da6dba05 in do_syscall1 ../linux-user/syscall.c:10105 #7 0x5593da6f564c in do_syscall ../linux-user/syscall.c:13329 #8 0x5593da49e80c in cpu_loop ../linux-user/x86_64/../i386/cpu_loop.c:233 #9 0x5593da6be28c in clone_func ../linux-user/syscall.c:6633 #10 0x7f496231cb42 in start_thread nptl/pthread_create.c:442 #11 0x7f49623ae9ff (/lib/x86_64-linux-gnu/libc.so.6+0x1269ff) 0x62d0005f7418 is located 28696 bytes inside of 32768-byte region [0x62d0005f0400,0x62d0005f8400) freed by thread T148 here: #0 0x7f49627b6460 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52 #1 0x5593da5ac057 in cpu_exec_unrealizefn ../cpu.c:180 #2 0x5593da81f851 (/home/cota/src/qemu/build/qemu-x86_64+0x484851) Signed-off-by: Emilio Cota <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Message-Id: <[email protected]> Signed-off-by: Alex Bennée <[email protected]> Message-Id: <[email protected]>
1 parent d54c6d3 commit 4731f89

File tree

3 files changed

+12
-3
lines changed

3 files changed

+12
-3
lines changed

accel/tcg/cpu-exec.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1064,13 +1064,12 @@ void tcg_exec_realizefn(CPUState *cpu, Error **errp)
10641064
/* undo the initializations in reverse order */
10651065
void tcg_exec_unrealizefn(CPUState *cpu)
10661066
{
1067-
qemu_plugin_vcpu_exit_hook(cpu);
10681067
#ifndef CONFIG_USER_ONLY
10691068
tcg_iommu_free_notifier_list(cpu);
10701069
#endif /* !CONFIG_USER_ONLY */
10711070

10721071
tlb_destroy(cpu);
1073-
g_free(cpu->tb_jmp_cache);
1072+
g_free_rcu(cpu->tb_jmp_cache, rcu);
10741073
}
10751074

10761075
#ifndef CONFIG_USER_ONLY

accel/tcg/tb-jmp-cache.h

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* a load_acquire/store_release to 'tb'.
1919
*/
2020
struct CPUJumpCache {
21+
struct rcu_head rcu;
2122
struct {
2223
TranslationBlock *tb;
2324
#if TARGET_TB_PCREL

cpu.c

+10-1
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,20 @@ void cpu_exec_unrealizefn(CPUState *cpu)
176176
vmstate_unregister(NULL, &vmstate_cpu_common, cpu);
177177
}
178178
#endif
179+
180+
/* Call the plugin hook before clearing cpu->cpu_index in cpu_list_remove */
179181
if (tcg_enabled()) {
180-
tcg_exec_unrealizefn(cpu);
182+
qemu_plugin_vcpu_exit_hook(cpu);
181183
}
182184

183185
cpu_list_remove(cpu);
186+
/*
187+
* Now that the vCPU has been removed from the RCU list, we can call
188+
* tcg_exec_unrealizefn, which may free fields using call_rcu.
189+
*/
190+
if (tcg_enabled()) {
191+
tcg_exec_unrealizefn(cpu);
192+
}
184193
}
185194

186195
/*

0 commit comments

Comments
 (0)