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,
openSUSE Build Service is sponsored by