Stian Halseth <[email protected]> writes:

> 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.

I wonder how much:

        /* flush must be done */
        if (cpu_in_serial_context(cpu)) {
            trace_tb_gen_code_buffer_overflow("tcg_tb_alloc");
            tb_flush__exclusive_or_serial();
            goto buffer_overflow;
        }

actually saves? Because we are skipping both cleanup in cpu_loop_exit
(the mem helpers) and cpu_exec_longjmp_cleanup which we get to when we
jump out.

>
> 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

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro

Reply via email to