Commit cbbd4d74eb for qemu.org

commit cbbd4d74eb298e2b407018185a40cb813435527e
Author: Shivang Upadhyay <shivangu@linux.ibm.com>
Date:   Wed Sep 23 17:44:44 2026 +0530

    target/ppc: Stop vCPU thread before calling parent_unrealize

    During CPU hot-unplug (e.g. via dynamic reconfiguration unplug),
    ppc_cpu_unrealize() invoked pcc->parent_unrealize(dev) before calling
    cpu_remove_sync(CPU(cpu)).

    pcc->parent_unrealize() calls cpu_common_unrealize(), which triggers
    accel_cpu_common_unrealize() -> tcg_exec_unrealizefn() -> tlb_destroy().
    This immediately frees the CPU's TLB tables and structures. Because the
    vCPU thread had not yet been stopped and joined via cpu_remove_sync(),
    the vCPU thread was still actively running its event loop and processing
    queued CPU work (such as tcg_commit_cpu / tlb_flush).

    This resulted in a race where the running vCPU thread accessed and freed
    already-destroyed TLB tables concurrently with tlb_destroy(), leading to
    Segfault (due to heap corruption).

    AddressSanitizer build reported a double-free:

    =================================================================
    ==121930==ERROR: AddressSanitizer: attempting double-free on 0x7ef8f3438800 in thread T14:
        #0 0x7fe8f74e5beb in free.part.0 (/lib64/libasan.so.8+0xe5beb)
        #1 0x7fe8f6cb8f84 in g_free (/lib64/libglib-2.0.so.0+0x41f84)
        #2 0x558bf6a391b1 in tlb_mmu_resize_locked accel/tcg/cputlb.c:249
        #3 0x558bf6a396b5 in tlb_flush_one_mmuidx_locked accel/tcg/cputlb.c:296
        #4 0x558bf6a39f91 in tlb_flush_by_mmuidx_async_work accel/tcg/cputlb.c:390
        #5 0x558bf6a3a200 in tlb_flush_by_mmuidx accel/tcg/cputlb.c:417
        #6 0x558bf6a3a22a in tlb_flush accel/tcg/cputlb.c:422
        #7 0x558bf73f31ac in tcg_commit_cpu system/physmem.c:3068
        #8 0x558bf6987c55 in process_queued_cpu_work cpu-common.c:378
        #9 0x558bf73a9913 in qemu_process_cpu_events_common system/cpus.c:402
        #10 0x558bf73a9a46 in qemu_process_cpu_events system/cpus.c:421
        #11 0x558bf6a65974 in mttcg_cpu_thread_fn accel/tcg/tcg-accel-ops-mttcg.c:90

    0x7ef8f3438800 is located 0 bytes inside of 65536-byte region [0x7ef8f3438800,0x7ef8f3448800)
    freed by thread T9 here:
        #0 0x7fe8f74e5beb in free.part.0 (/lib64/libasan.so.8+0xe5beb)
        #1 0x7fe8f6cb8f84 in g_free (/lib64/libglib-2.0.so.0+0x41f84)
        #2 0x558bf6a39a91 in tlb_destroy accel/tcg/cputlb.c:345
        #3 0x558bf6a16354 in tcg_exec_unrealizefn accel/tcg/cpu-exec.c:1094
        #4 0x558bf693d073 in accel_cpu_common_unrealize accel/accel-common.c:117
        #5 0x558bf6980e37 in cpu_common_unrealize hw/core/cpu-common.c:279
        #6 0x558bf6980dfa in cpu_common_unrealizefn hw/core/cpu-common.c:267
        #7 0x558bf763ef65 in ppc_cpu_unrealize target/ppc/cpu_init.c:6965
        #8 0x558bf7872199 in device_set_realized hw/core/qdev.c:618
        #14 0x558bf756068f in spapr_unrealize_vcpu hw/ppc/spapr_cpu_core.c:209

    Fix this by moving cpu_remove_sync() before pcc->parent_unrealize(dev)
    in ppc_cpu_unrealize(), ensuring the vCPU thread is stopped, has
    finished processing its events, and is joined before CPU resources
    and accelerator state are destroyed.

    Cc: qemu-stable@nongnu.org
    Signed-off-by: Shivang Upadhyay <shivangu@linux.ibm.com>
    Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
    Reviewed-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
    Reviewed-by: Amit Machhiwal <amachhiw@linux.ibm.com>
    Message-ID: <20260923121444.154175-1-shivangu@linux.ibm.com>
    Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>

diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 6c626843c9..b711f9c0a8 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -6962,10 +6962,10 @@ static void ppc_cpu_unrealize(DeviceState *dev)
     PowerPCCPU *cpu = POWERPC_CPU(dev);
     PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);

-    pcc->parent_unrealize(dev);
-
     cpu_remove_sync(CPU(cpu));

+    pcc->parent_unrealize(dev);
+
     destroy_ppc_opcodes(cpu);
 }