qemuAutostartDomain() starts domains with conn=NULL as there is no client connection during driver initialisation. Since the managed save restore path of qemuDomainObjStart() was switched to qemuDomainRestoreInternal(), which obtains the driver by dereferencing conn->privateData, any domain with both a managed save image and the autostart (or autostart-once) flag crashes virtqemud/libvirtd with SIGSEGV on daemon startup. The session daemon's auto-shutdown feature creates exactly this combination on logout with a running VM (auto_shutdown_try_save saves the domain and auto_shutdown_restore marks it autostart-once), so the first daemon start of every subsequent boot segfaults and clients such as GNOME Boxes fail with "Cannot recv data: Connection reset by peer", showing an empty machine list. Pass the driver as an explicit parameter, as qemuDomainObjRestore() did before it was merged into qemuDomainRestoreInternal(), and keep @conn only for use by the ACL check callbacks, which are not used in the autostart code path. Fixes: 0e2ab427782c ("qemu: driver: Merge 'qemuDomainRestoreInternal' and 'qemuDomainObjRestore'") Signed-off-by: Daniel Golle <daniel@makrotopia.org> --- Observed with libvirt 12.5.0 and QEMU 11.0.2 on Arch Linux; the code in current git master is identical. Reproducer (any session domain, regular autostart takes the same path): $ virsh -c qemu:///session managedsave DOMAIN $ virsh -c qemu:///session autostart DOMAIN --once # simulate the first daemon start of a boot: $ rm "$XDG_RUNTIME_DIR/libvirt/qemu/run/autostarted" $ pkill -x virtqemud $ virsh -c qemu:///session list --all error: failed to connect to the hypervisor error: Cannot recv data: Connection reset by peer Backtrace of the crashing thread (12.5.0, distro debug symbols): #0 qemuDomainRestoreInternal (conn=conn@entry=0x0, ...) at ../src/qemu/qemu_driver.c:5940 #1 qemuDomainObjStart.constprop.0 (conn=conn@entry=0x0, ...) at ../src/qemu/qemu_driver.c:6509 #2 qemuAutostartDomain at ../src/qemu/qemu_driver.c:185 #3 virDomainDriverAutoStartOne at ../src/hypervisor/domain_driver.c:700 #7 virDomainDriverAutoStart at ../src/hypervisor/domain_driver.c:728 #8 qemuStateInitialize at ../src/qemu/qemu_driver.c:962 #9 virStateInitialize at ../src/libvirt.c:667 Tested on current master in an isolated session environment with a minimal diskless domain: unpatched, the daemon reproducibly segfaults on startup after the steps above; patched, it starts cleanly and restores the domain from its managed save image. The explicit restore APIs (virDomainRestore*) behave as before. src/qemu/qemu_driver.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3d57de38..d478d383 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5901,7 +5901,8 @@ qemuNodeGetSecurityModel(virConnectPtr conn, /** * qemuDomainRestoreInternal: - * @conn: connection object + * @driver: QEMU driver + * @conn: connection object (optional if @ensureACL is NULL) * @vmRestore: Domain object (optional; see below) * @path: path to the save image file * @unlink_corrupt: remove corrupted save image file @path @@ -5926,7 +5927,8 @@ qemuNodeGetSecurityModel(virConnectPtr conn, * corrupted image was removed 1 is returned. */ static int -qemuDomainRestoreInternal(virConnectPtr conn, +qemuDomainRestoreInternal(virQEMUDriver *driver, + virConnectPtr conn, virDomainObj *vmRestore, const char *path, bool unlink_corrupt, @@ -5937,7 +5939,6 @@ qemuDomainRestoreInternal(virConnectPtr conn, int (*ensureACL)(virConnectPtr, virDomainDef *), virDomainAsyncJob asyncJob) { - virQEMUDriver *driver = conn->privateData; qemuDomainObjPrivate *priv = NULL; g_autoptr(virDomainDef) def = NULL; virDomainObj *vm = NULL; @@ -6096,7 +6097,8 @@ qemuDomainRestoreFlags(virConnectPtr conn, { virCheckFlags(QEMU_DOMAIN_RESTORE_FLAGS, -1); - return qemuDomainRestoreInternal(conn, NULL, path, false, dxml, NULL, 0, + return qemuDomainRestoreInternal(conn->privateData, conn, NULL, path, + false, dxml, NULL, 0, flags, virDomainRestoreFlagsEnsureACL, VIR_ASYNC_JOB_START); } @@ -6105,7 +6107,8 @@ static int qemuDomainRestore(virConnectPtr conn, const char *path) { - return qemuDomainRestoreInternal(conn, NULL, path, false, NULL, NULL, 0, + return qemuDomainRestoreInternal(conn->privateData, conn, NULL, path, + false, NULL, NULL, 0, 0, virDomainRestoreEnsureACL, VIR_ASYNC_JOB_START); } @@ -6141,7 +6144,8 @@ qemuDomainRestoreParams(virConnectPtr conn, return -1; } - ret = qemuDomainRestoreInternal(conn, NULL, path, false, dxml, params, nparams, + ret = qemuDomainRestoreInternal(conn->privateData, conn, NULL, path, + false, dxml, params, nparams, flags, virDomainRestoreParamsEnsureACL, VIR_ASYNC_JOB_START); return ret; @@ -6506,7 +6510,7 @@ qemuDomainObjStart(virConnectPtr conn, restore_flags |= bypass_cache ? VIR_DOMAIN_SAVE_BYPASS_CACHE : 0; restore_flags |= reset_nvram ? VIR_DOMAIN_SAVE_RESET_NVRAM : 0; - ret = qemuDomainRestoreInternal(conn, vm, managed_save, true, NULL, NULL, 0, + ret = qemuDomainRestoreInternal(driver, conn, vm, managed_save, true, NULL, NULL, 0, restore_flags, NULL, asyncJob); -- 2.55.0