[PATCH] accel/tcg: Properly unlink a TB linked to itself

Richard Henderson posted 1 patch 4 days, 12 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20250924020735.3909216-3-richard.henderson@linaro.org
Maintainers: Richard Henderson <richard.henderson@linaro.org>, Paolo Bonzini <pbonzini@redhat.com>, Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <dbarboza@ventanamicro.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
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
[PATCH] accel/tcg: Properly unlink a TB linked to itself
Posted by Richard Henderson 4 days, 12 hours ago
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: 李威威 <liweiwei@kubuds.cn>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 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