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
© 2016 - 2026 Red Hat, Inc.