In a serial context, tb_gen_code() flushes a full code buffer and
retries in place. cpu_exec_loop() then chains its last_tb, now freed,
into the new TB. When that TB is invalidated, tb_jmp_unlink() follows
the stale entry and qemu crashes inside its SIGSEGV handler.

Drop last_tb when tb_gen_code() has flushed.

User-only builds hit this easily since cross-page goto_tb chaining,
e.g. qemu-sparc64 -tb-size 1 on a program writing next to its PLT.

Fixes: ebf7a5d294a3 ("accel/tcg: Improve buffer overflow in tb_gen_code")
Fixes: 25e5c6a320db ("accel/tcg: Allow cross-page goto_tb chaining in user-only 
builds")
Signed-off-by: Stian Halseth <[email protected]>
---
25e5c6a320db is not in a release yet (v11.1.x does not have it), so the
crash is new in the 11.2 cycle.

Reproducers available on request: on x86_64 the stale chain happens
too but has stayed harmless; on sparc64 it crashes every time.

 accel/tcg/cpu-exec.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 5b4bca5084..600e8c5fa8 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -968,11 +968,17 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
             if (tb == NULL) {
                 CPUJumpCache *jc;
                 uint32_t h;
+                unsigned flush_count = qatomic_read(&tb_ctx.tb_flush_count);
 
                 mmap_lock();
                 tb = tb_gen_code(cpu, s);
                 mmap_unlock();
 
+                /* tb_gen_code may have flushed, freeing last_tb. */
+                if (flush_count != qatomic_read(&tb_ctx.tb_flush_count)) {
+                    last_tb = NULL;
+                }
+
                 /*
                  * We add the TB in the virtual pc hash table
                  * for the fast lookup
-- 
2.43.0


Reply via email to