On Wed, Sep 24, 2025 at 12:09 PM Richard Henderson <[email protected]> wrote: > > When we remove dest from orig's links, we lose the link > that we rely on later to reset links. This can lead to > failure to release from spinlock with self-modifying code. > > Reported-by: 李威威 <[email protected]> > Signed-off-by: Richard Henderson <[email protected]>
Reviewed-by: Alistair Francis <[email protected]> Alistair > --- > accel/tcg/tb-maint.c | 8 +++++ > tests/tcg/riscv64/tb-link.c | 60 +++++++++++++++++++++++++++++++ > tests/tcg/riscv64/Makefile.target | 1 + > 3 files changed, 69 insertions(+) > create mode 100644 tests/tcg/riscv64/tb-link.c > > diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c > index 0048316f99..e6d45c9c12 100644 > --- a/accel/tcg/tb-maint.c > +++ b/accel/tcg/tb-maint.c > @@ -836,6 +836,14 @@ static inline void > tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) > * We first acquired the lock, and since the destination pointer matches, > * we know for sure that @orig is in the jmp list. > */ > + if (dest == orig) { > + /* > + * In the case of a TB that links to itself, removing the entry > + * from the list means that it won't be present later during > + * tb_jmp_unlink -- unlink now. > + */ > + tb_reset_jump(orig, n_orig); > + } > pprev = &dest->jmp_list_head; > TB_FOR_EACH_JMP(dest, tb, n) { > if (tb == orig && n == n_orig) { > diff --git a/tests/tcg/riscv64/tb-link.c b/tests/tcg/riscv64/tb-link.c > new file mode 100644 > index 0000000000..b6fcca8668 > --- /dev/null > +++ b/tests/tcg/riscv64/tb-link.c > @@ -0,0 +1,60 @@ > +#include <assert.h> > +#include <string.h> > +#include <sys/mman.h> > +#include <pthread.h> > +#include <stdint.h> > +#include <unistd.h> > + > + > +int main() > +{ > + /* > + * ## 1. RISC-V machine code. > + * Assembly: > + * L: j L ; Jump to self (spin). > + * li a0, 42 ; Place 42 into the return value register a0. > + * ret ; Return to caller. > + */ > + static const uint32_t machine_code[] = { > + 0x0000006f, /* jal zero, #0 */ > + 0x02a00513, /* addi a0, zero, 42 */ > + 0x00008067 /* jalr zero, ra, 0 */ > + }; > + size_t code_size = sizeof(machine_code); > + int tmp; > + pthread_t thread_id; > + void *thread_return_value; > + uint32_t *buffer; > + > + /* ## 2. Allocate executable memory. */ > + buffer = mmap( > + NULL, > + code_size, > + PROT_READ | PROT_WRITE | PROT_EXEC, > + MAP_PRIVATE | MAP_ANONYMOUS, > + -1, 0 > + ); > + assert(buffer != MAP_FAILED); > + > + /* ## 3. Copy machine code into buffer. */ > + memcpy(buffer, machine_code, code_size); > + > + /* ## 4. Execute the code in a separate thread. */ > + tmp = pthread_create(&thread_id, NULL, (void *(*)(void *))buffer, NULL); > + assert(tmp == 0); > + > + /* > + * Wait a second and then try to patch the generated code to get the > + * runner thread to get unstuck by patching the spin jump. > + */ > + sleep(1); > + buffer[0] = 0x00000013; /* nop */ > + __builtin___clear_cache((char *)buffer, (char *)(buffer + 1)); > + > + tmp = pthread_join(thread_id, &thread_return_value); > + assert(tmp == 0); > + > + tmp = (intptr_t)thread_return_value; > + assert(tmp == 42); > + return 0; > +} > diff --git a/tests/tcg/riscv64/Makefile.target > b/tests/tcg/riscv64/Makefile.target > index 4da5b9a3b3..ba684616fd 100644 > --- a/tests/tcg/riscv64/Makefile.target > +++ b/tests/tcg/riscv64/Makefile.target > @@ -4,6 +4,7 @@ > VPATH += $(SRC_PATH)/tests/tcg/riscv64 > TESTS += test-div > TESTS += noexec > +TESTS += tb-link > > # Disable compressed instructions for test-noc > TESTS += test-noc > -- > 2.43.0 > >
