From 7ff49868cf6a80d14f83a064b6524de233f5b6b0 Mon Sep 17 00:00:00 2001
From: Jonathon Jongsma <jjongsma@redhat.com>
Date: Tue, 16 Mar 2021 17:27:25 -0500
Subject: [PATCH 090/108] nodedev: Don't crash when exiting before init is done

If libvirtd is terminated before the node driver finishes
initialization, it can crash with a backtrace similar to the following:

    Stack trace of thread 1922933:
    #0  0x00007f8515178774 g_hash_table_find (libglib-2.0.so.0)
    #1  0x00007f851593ea98 virHashSearch (libvirt.so.0)
    #2  0x00007f8515a1dd83 virNodeDeviceObjListSearch (libvirt.so.0)
    #3  0x00007f84cceb40a1 udevAddOneDevice (libvirt_driver_nodedev.so)
    #4  0x00007f84cceb5fae nodeStateInitializeEnumerate (libvirt_driver_nodedev.so)
    #5  0x00007f85159840cb virThreadHelper (libvirt.so.0)
    #6  0x00007f8511c7d14a start_thread (libpthread.so.0)
    #7  0x00007f851442bdb3 __clone (libc.so.6)

    Stack trace of thread 1922863:
    #0  0x00007f851442651d syscall (libc.so.6)
    #1  0x00007f85159842d4 virThreadSelfID (libvirt.so.0)
    #2  0x00007f851594e240 virLogFormatString (libvirt.so.0)
    #3  0x00007f851596635d vir_object_finalize (libvirt.so.0)
    #4  0x00007f8514efe8e9 g_object_unref (libgobject-2.0.so.0)
    #5  0x00007f85159667f8 virObjectUnref (libvirt.so.0)
    #6  0x00007f851517755f g_hash_table_remove_all_nodes.part.0 (libglib-2.0.so.0)
    #7  0x00007f8515177e62 g_hash_table_unref (libglib-2.0.so.0)
    #8  0x00007f851596637e vir_object_finalize (libvirt.so.0)
    #9  0x00007f8514efe8e9 g_object_unref (libgobject-2.0.so.0)
    #10 0x00007f85159667f8 virObjectUnref (libvirt.so.0)
    #11 0x00007f84cceb2b42 nodeStateCleanup (libvirt_driver_nodedev.so)
    #12 0x00007f8515b37950 virStateCleanup (libvirt.so.0)
    #13 0x00005648085348e8 main (libvirtd)
    #14 0x00007f8514352493 __libc_start_main (libc.so.6)
    #15 0x00005648085350fe _start (libvirtd)

This is because the initial population of the device list is done in a
separate initialization thread. If we attempt to exit libvirtd before
this init thread has completed, we'll try to free the device list while
accessing it from the other thread. In order to guarantee that this
init thread is not accessing the device list when we're cleaning up the
nodedev driver, make it joinable and wait for it to finish before
proceding with the cleanup. This is similar to how we handle the udev
event handler thread.

The separate initialization thread was added in commit
9f0ae0b1.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1836865

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
(cherry picked from commit caf23cdc9b628ae3acd89478fbace093b23649b8)
---
 src/node_device/node_device_udev.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index e65979c1b6..94e8469dcb 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -66,6 +66,9 @@ struct _udevEventData {
     virCond threadCond;
     bool threadQuit;
     bool dataReady;
+
+    /* init thread */
+    virThread initThread;
 };
 
 static virClassPtr udevEventDataClass;
@@ -1467,6 +1470,7 @@ nodeStateCleanup(void)
         priv->threadQuit = true;
         virCondSignal(&priv->threadCond);
         virObjectUnlock(priv);
+        virThreadJoin(&priv->initThread);
         virThreadJoin(&priv->th);
     }
 
@@ -1799,7 +1803,6 @@ nodeStateInitialize(bool privileged,
 {
     udevEventDataPtr priv = NULL;
     struct udev *udev = NULL;
-    virThread enumThread;
 
     if (root != NULL) {
         virReportError(VIR_ERR_INVALID_ARG, "%s",
@@ -1908,7 +1911,7 @@ nodeStateInitialize(bool privileged,
     if (udevSetupSystemDev() != 0)
         goto cleanup;
 
-    if (virThreadCreateFull(&enumThread, false, nodeStateInitializeEnumerate,
+    if (virThreadCreateFull(&priv->initThread, true, nodeStateInitializeEnumerate,
                             "nodedev-init", false, udev) < 0) {
         virReportSystemError(errno, "%s",
                              _("failed to create udev enumerate thread"));
-- 
2.33.0

