[PATCH v2] qemu: Fix crash in qemuProcessStop due to NULL eventThread

Pritam Srichandan Sahoo via Devel posted 1 patch 4 days, 20 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/libvirt tags/patchew/20260909071537.758416-1-PritamSrichandan.Sahoo@windriver.com
src/qemu/qemu_process.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
[PATCH v2] qemu: Fix crash in qemuProcessStop due to NULL eventThread
Posted by Pritam Srichandan Sahoo via Devel 4 days, 20 hours ago
When a VM shuts down, the monitor EOF event is queued to the worker
thread pool. If another thread enters qemuProcessStop() first and
frees priv->eventThread via g_steal_pointer(), the second call from
the EOF event worker thread hits a NULL dereference when calling
virEventThreadStop(priv->eventThread).

The existing 'if (priv->eventThread)' guard is insufficient because
the VM object is unlocked between the check and the call to
virEventThreadStop(), allowing a concurrent thread to clear the
pointer in the meantime.

Fix this by taking a GObject reference to eventThread before
unlocking the VM. This ensures the pointer remains valid for
virEventThreadStop() even if another thread clears
priv->eventThread concurrently.

Crash backtrace:
    #0 virEventThreadStop (evt=0x0)
    #1 qemuProcessStop (reason=VIR_DOMAIN_SHUTOFF_SHUTDOWN, asyncJob=VIR_ASYNC_JOB_NONE)
    #2 processMonitorEOFEvent (driver=..., vm=...)
    #3 qemuProcessEventHandler (data=..., opaque=...)
    #4 virThreadPoolWorker (opaque=...)

Signed-off-by: Pritam Srichandan Sahoo <PritamSrichandan.Sahoo@windriver.com>
---
v2: Resend as an inline plain-text patch. The v1 posting was delivered
      as an email attachment which is harder to review; no code changes
      from v1.

 src/qemu/qemu_process.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 0d9b8bcb93..642e18eeb1 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -8850,13 +8850,15 @@ void qemuProcessStop(virQEMUDriver *driver,
      * the global domain object list code depends on it (and it can't actually
      * check 'priv->beingDestroyed as that's private). */
     if (priv->eventThread) {
+        virEventThread *eventThread = g_object_ref(priv->eventThread);
         /* Explicitly set priv->beingDestroyed. While it's done in
          * qemuProcessBeginStopJob(), qemuProcessStop() is called from places
          * where stop job is not acquired. */
         priv->beingDestroyed = true;
         virObjectUnlock(vm);
-        virEventThreadStop(priv->eventThread);
+        virEventThreadStop(eventThread);
         virObjectLock(vm);
+        g_object_unref(eventThread);
     }
 
     if (priv->agent) {
-- 
2.53.0