File 2a326c41-CVE-2025-12748-p5.patch of Package libvirt.42084
commit dc366707c25716515cfb3785aef4ab7facac0dd5
Author: Martin Kletzander <mkletzan@redhat.com>
Date: Thu Nov 6 14:33:41 2025 +0100
qemu: Check ACLs before parsing the whole domain XML
Utilise the new virDomainDefIDsParseString() for that.
This is one of the more complex ones since there is also a function that
reads relevant metadata from a save image XML. In order _not_ to extract
the parsing out of the function (and make the function basically trivial
and all callers more complex) add a callback to the function which will
be used to check the ACLs.
Fixes: CVE-2025-12748
References: bsc#1253278
Reported-by: Святослав Терешин <s.tereshin@fobos-nt.ru>
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
(cherry picked from commit 2a326c415a7e1cdd49989cc7e46b88d9ca90dd97)
Signed-off-by: Jim Fehlig <jfehlig@suse.com>
Index: libvirt-7.1.0/src/qemu/qemu_driver.c
===================================================================
--- libvirt-7.1.0.orig/src/qemu/qemu_driver.c
+++ libvirt-7.1.0/src/qemu/qemu_driver.c
@@ -1749,13 +1749,19 @@ static virDomainPtr qemuDomainCreateXML(
virNWFilterReadLockFilterUpdates();
- if (!(def = virDomainDefParseString(xml, driver->xmlopt,
- NULL, parse_flags)))
+ /* Avoid parsing the whole domain definition for ACL checks */
+ if (!(def = virDomainDefIDsParseString(xml, driver->xmlopt, parse_flags)))
goto cleanup;
if (virDomainCreateXMLEnsureACL(conn, def) < 0)
goto cleanup;
+ g_clear_pointer(&def, virDomainDefFree);
+
+ if (!(def = virDomainDefParseString(xml, driver->xmlopt,
+ NULL, parse_flags)))
+ goto cleanup;
+
if (!(vm = virDomainObjListAdd(driver->domains, def,
driver->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
@@ -5997,15 +6003,13 @@ qemuDomainRestoreFlags(virConnectPtr con
virNWFilterReadLockFilterUpdates();
- fd = qemuSaveImageOpen(driver, NULL, path, &def, &data,
+ fd = qemuSaveImageOpen(driver, NULL, path, virDomainRestoreFlagsEnsureACL,
+ conn, &def, &data,
(flags & VIR_DOMAIN_SAVE_BYPASS_CACHE) != 0,
&wrapperFd, false, false);
if (fd < 0)
goto cleanup;
- if (virDomainRestoreFlagsEnsureACL(conn, def) < 0)
- goto cleanup;
-
if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
int hookret;
@@ -6092,15 +6096,14 @@ qemuDomainSaveImageGetXMLDesc(virConnect
virCheckFlags(VIR_DOMAIN_SAVE_IMAGE_XML_SECURE, NULL);
- fd = qemuSaveImageOpen(driver, NULL, path, &def, &data,
+ fd = qemuSaveImageOpen(driver, NULL, path,
+ virDomainSaveImageGetXMLDescEnsureACL,
+ conn, &def, &data,
false, NULL, false, false);
if (fd < 0)
goto cleanup;
- if (virDomainSaveImageGetXMLDescEnsureACL(conn, def) < 0)
- goto cleanup;
-
ret = qemuDomainDefFormatXML(driver, NULL, def, flags);
cleanup:
@@ -6129,15 +6132,14 @@ qemuDomainSaveImageDefineXML(virConnectP
else if (flags & VIR_DOMAIN_SAVE_PAUSED)
state = 0;
- fd = qemuSaveImageOpen(driver, NULL, path, &def, &data,
+ fd = qemuSaveImageOpen(driver, NULL, path,
+ virDomainSaveImageDefineXMLEnsureACL,
+ conn, &def, &data,
false, NULL, true, false);
if (fd < 0)
goto cleanup;
- if (virDomainSaveImageDefineXMLEnsureACL(conn, def) < 0)
- goto cleanup;
-
if (STREQ(data->xml, dxml) &&
(state < 0 || state == data->header.was_running)) {
/* no change to the XML */
@@ -6211,7 +6213,8 @@ qemuDomainManagedSaveGetXMLDesc(virDomai
goto cleanup;
}
- if ((fd = qemuSaveImageOpen(driver, priv->qemuCaps, path, &def, &data,
+ if ((fd = qemuSaveImageOpen(driver, priv->qemuCaps, path,
+ NULL, NULL, &def, &data,
false, NULL, false, false)) < 0)
goto cleanup;
@@ -6275,7 +6278,7 @@ qemuDomainObjRestore(virConnectPtr conn,
virQEMUSaveDataPtr data = NULL;
virFileWrapperFdPtr wrapperFd = NULL;
- fd = qemuSaveImageOpen(driver, NULL, path, &def, &data,
+ fd = qemuSaveImageOpen(driver, NULL, path, NULL, NULL, &def, &data,
bypass_cache, &wrapperFd, false, true);
if (fd < 0) {
if (fd == -3)
@@ -6680,9 +6683,18 @@ qemuDomainDefineXMLFlags(virConnectPtr c
if (flags & VIR_DOMAIN_DEFINE_VALIDATE)
parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
+ /* Avoid parsing the whole domain definition for ACL checks */
+ if (!(def = virDomainDefIDsParseString(xml, driver->xmlopt, parse_flags)))
+ return NULL;
+
+ if (virDomainDefineXMLFlagsEnsureACL(conn, def) < 0)
+ goto cleanup;
+
+ g_clear_pointer(&def, virDomainDefFree);
+
if (!(def = virDomainDefParseString(xml, driver->xmlopt,
NULL, parse_flags)))
- return NULL;
+ goto cleanup;
if (virXMLCheckIllegalChars("name", def->name, "\n") < 0)
goto cleanup;
@@ -11193,10 +11205,9 @@ qemuDomainMigratePrepareTunnel(virConnec
return -1;
}
- if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname)))
- return -1;
-
- if (virDomainMigratePrepareTunnelEnsureACL(dconn, def) < 0)
+ if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname,
+ dconn,
+ virDomainMigratePrepareTunnelEnsureACL)))
return -1;
return qemuMigrationDstPrepareTunnel(driver, dconn,
@@ -11247,10 +11258,9 @@ qemuDomainMigratePrepare2(virConnectPtr
return -1;
}
- if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname)))
- return -1;
-
- if (virDomainMigratePrepare2EnsureACL(dconn, def) < 0)
+ if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname,
+ dconn,
+ virDomainMigratePrepare2EnsureACL)))
return -1;
/* Do not use cookies in v2 protocol, since the cookie
@@ -11470,10 +11480,9 @@ qemuDomainMigratePrepare3(virConnectPtr
QEMU_MIGRATION_DESTINATION)))
return -1;
- if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname)))
- return -1;
-
- if (virDomainMigratePrepare3EnsureACL(dconn, def) < 0)
+ if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname,
+ dconn,
+ virDomainMigratePrepare3EnsureACL)))
return -1;
return qemuMigrationDstPrepareDirect(driver, dconn,
@@ -11582,10 +11591,9 @@ qemuDomainMigratePrepare3Params(virConne
return -1;
}
- if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname)))
- return -1;
-
- if (virDomainMigratePrepare3ParamsEnsureACL(dconn, def) < 0)
+ if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname,
+ dconn,
+ virDomainMigratePrepare3ParamsEnsureACL)))
return -1;
return qemuMigrationDstPrepareDirect(driver, dconn,
@@ -11627,10 +11635,9 @@ qemuDomainMigratePrepareTunnel3(virConne
QEMU_MIGRATION_DESTINATION)))
return -1;
- if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname)))
- return -1;
-
- if (virDomainMigratePrepareTunnel3EnsureACL(dconn, def) < 0)
+ if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname,
+ dconn,
+ virDomainMigratePrepareTunnel3EnsureACL)))
return -1;
return qemuMigrationDstPrepareTunnel(driver, dconn,
@@ -11679,10 +11686,9 @@ qemuDomainMigratePrepareTunnel3Params(vi
QEMU_MIGRATION_DESTINATION)))
return -1;
- if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname)))
- return -1;
-
- if (virDomainMigratePrepareTunnel3ParamsEnsureACL(dconn, def) < 0)
+ if (!(def = qemuMigrationAnyPrepareDef(driver, NULL, dom_xml, dname, &origname,
+ dconn,
+ virDomainMigratePrepareTunnel3ParamsEnsureACL)))
return -1;
return qemuMigrationDstPrepareTunnel(driver, dconn,
Index: libvirt-7.1.0/src/qemu/qemu_migration.c
===================================================================
--- libvirt-7.1.0.orig/src/qemu/qemu_migration.c
+++ libvirt-7.1.0/src/qemu/qemu_migration.c
@@ -3318,7 +3318,9 @@ qemuMigrationAnyPrepareDef(virQEMUDriver
virQEMUCapsPtr qemuCaps,
const char *dom_xml,
const char *dname,
- char **origname)
+ char **origname,
+ virConnectPtr sconn,
+ int (*ensureACL)(virConnectPtr, virDomainDefPtr))
{
virDomainDefPtr def;
char *name = NULL;
@@ -3329,6 +3331,24 @@ qemuMigrationAnyPrepareDef(virQEMUDriver
return NULL;
}
+ if (ensureACL) {
+ g_autoptr(virDomainDef) aclDef = NULL;
+
+ /* Avoid parsing the whole domain definition for ACL checks */
+ if (!(aclDef = virDomainDefIDsParseString(dom_xml, driver->xmlopt,
+ VIR_DOMAIN_DEF_PARSE_INACTIVE)))
+ return NULL;
+
+ if (dname) {
+ VIR_FREE(aclDef->name);
+ aclDef->name = g_strdup(dname);
+ }
+
+ if (ensureACL(sconn, aclDef) < 0) {
+ return NULL;
+ }
+ }
+
if (!(def = virDomainDefParseString(dom_xml, driver->xmlopt,
qemuCaps,
VIR_DOMAIN_DEF_PARSE_INACTIVE |
@@ -4055,6 +4075,7 @@ qemuMigrationSrcRun(virQEMUDriverPtr dri
if (!(persistDef = qemuMigrationAnyPrepareDef(driver,
priv->qemuCaps,
persist_xml,
+ NULL, NULL,
NULL, NULL)))
goto error;
} else {
Index: libvirt-7.1.0/src/qemu/qemu_migration.h
===================================================================
--- libvirt-7.1.0.orig/src/qemu/qemu_migration.h
+++ libvirt-7.1.0/src/qemu/qemu_migration.h
@@ -120,7 +120,9 @@ qemuMigrationAnyPrepareDef(virQEMUDriver
virQEMUCapsPtr qemuCaps,
const char *dom_xml,
const char *dname,
- char **origname);
+ char **origname,
+ virConnectPtr sconn,
+ int (*ensureACL)(virConnectPtr, virDomainDefPtr));
int
qemuMigrationDstPrepareTunnel(virQEMUDriverPtr driver,
Index: libvirt-7.1.0/src/qemu/qemu_saveimage.c
===================================================================
--- libvirt-7.1.0.orig/src/qemu/qemu_saveimage.c
+++ libvirt-7.1.0/src/qemu/qemu_saveimage.c
@@ -418,6 +418,8 @@ qemuSaveImageGetCompressionProgram(const
* @driver: qemu driver data
* @qemuCaps: pointer to qemuCaps if the domain is running or NULL
* @path: path of the save image
+ * @ensureACL: ACL callback to check against the definition or NULL
+ * @conn: parameter for the @ensureACL callback
* @ret_def: returns domain definition created from the XML stored in the image
* @ret_data: returns structure filled with data from the image header
* @bypass_cache: bypass cache when opening the file
@@ -433,6 +435,8 @@ int
qemuSaveImageOpen(virQEMUDriverPtr driver,
virQEMUCapsPtr qemuCaps,
const char *path,
+ int (*ensureACL)(virConnectPtr, virDomainDefPtr),
+ virConnectPtr conn,
virDomainDefPtr *ret_def,
virQEMUSaveDataPtr *ret_data,
bool bypass_cache,
@@ -448,6 +452,8 @@ qemuSaveImageOpen(virQEMUDriverPtr drive
int oflags = open_write ? O_RDWR : O_RDONLY;
size_t xml_len;
size_t cookie_len;
+ unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
+ VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE;
if (bypass_cache) {
int directFlag = virFileDirectFdFlag();
@@ -553,10 +559,19 @@ qemuSaveImageOpen(virQEMUDriverPtr drive
}
}
+ if (ensureACL) {
+ /* Parse only the IDs for ACL checks */
+ g_autoptr(virDomainDef) aclDef = virDomainDefIDsParseString(data->xml,
+ driver->xmlopt,
+ parse_flags);
+
+ if (!aclDef || ensureACL(conn, aclDef) < 0)
+ return -1;
+ }
+
/* Create a domain from this XML */
if (!(def = virDomainDefParseString(data->xml, driver->xmlopt, qemuCaps,
- VIR_DOMAIN_DEF_PARSE_INACTIVE |
- VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
+ parse_flags)))
return -1;
*ret_def = g_steal_pointer(&def);
Index: libvirt-7.1.0/src/qemu/qemu_saveimage.h
===================================================================
--- libvirt-7.1.0.orig/src/qemu/qemu_saveimage.h
+++ libvirt-7.1.0/src/qemu/qemu_saveimage.h
@@ -76,13 +76,15 @@ int
qemuSaveImageOpen(virQEMUDriverPtr driver,
virQEMUCapsPtr qemuCaps,
const char *path,
+ int (*ensureACL)(virConnectPtr, virDomainDefPtr),
+ virConnectPtr conn,
virDomainDefPtr *ret_def,
virQEMUSaveDataPtr *ret_data,
bool bypass_cache,
virFileWrapperFdPtr *wrapperFd,
bool open_write,
bool unlink_corrupt)
- ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
+ ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(6);
int
qemuSaveImageGetCompressionProgram(const char *imageFormat,