Hi,
The attached patch fixes a NULL pointer dereference crash in the QEMU driver that was observed .
Issue:
libvirtd crashes with SIGSEGV in virEventThreadStop(evt=0x0) during VM shutdown. The crash occurs in qemuProcessStop() when it is invoked from the QEMU monitor EOF event worker thread.
Root Cause:
There is a race condition when two threads enter qemuProcessStop() concurrently for the same domain:
- Thread A (shutdown path) frees priv->eventThread via g_steal_pointer().
- Thread B (EOF event worker) passes the "if (priv->eventThread)" NULL check while the pointer is still valid, but the VM object is then unlocked before virEventThreadStop() is called. In that window, Thread A clears priv->eventThread, so by the time Thread
B calls virEventThreadStop(priv->eventThread), the pointer is already NULL, causing the SIGSEGV.
The existing "if (priv->eventThread)" guard is insufficient because the VM object lock is released between the check and the actual call to virEventThreadStop().
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=...)
Fix:
Take a GObject reference on priv->eventThread before unlocking the VM object, use the local reference for virEventThreadStop(), then release it with g_object_unref() afterwards. This ensures the pointer remains valid for virEventThreadStop() even if another
thread clears priv->eventThread concurrently.
The affected code is present in current master (introduced in commit 0888784f387, 2024-07-25):
https://gitlab.com/libvirt/libvirt/-/commit/0888784f387 (https://gitlab.com/libvirt/libvirt/-/commit/0888784f387)
The crash is not reproducible on demand due to the narrow timing window of the race, but it is deterministic once the window is hit — virEventThreadStop(evt=0x0) will always SIGSEGV.
The fix has been build-tested.
Thanks,
Pritam Srichandan Sahoo