9f9eae
From 9617e31b5349b193469874706abcbcb013e6a6fd Mon Sep 17 00:00:00 2001
9f9eae
Message-Id: <9617e31b5349b193469874706abcbcb013e6a6fd.1407860168.git.crobinso@redhat.com>
9f9eae
In-Reply-To: <2151695a5119a8d7f44d416c730df50a1e42695a.1407860168.git.crobinso@redhat.com>
9f9eae
References: <2151695a5119a8d7f44d416c730df50a1e42695a.1407860168.git.crobinso@redhat.com>
9f9eae
From: Eric Blake <eblake@redhat.com>
9f9eae
Date: Wed, 6 Aug 2014 14:06:23 -0600
9f9eae
Subject: [PATCH 3/3] blockjob: fix use-after-free in blockcopy
9f9eae
9f9eae
Commit febf84c2 tried to delay in-memory modification of the actual
9f9eae
domain disk structure until after the qemu event was received.
9f9eae
However, I missed that the code for block pivot had been temporarily
9f9eae
setting disk->src = disk->mirror prior to the qemu command, in order
9f9eae
to label the backing chain of a reused external blockcopy disk;
9f9eae
and calls into qemu while still in that state before finally undoing
9f9eae
things at the cleanup label.  Since the qemu event handler then does:
9f9eae
 virStorageSourceFree(disk->src);
9f9eae
 disk->src = disk->mirror;
9f9eae
we have the sad race that a fast enough qemu event can cause a leak of
9f9eae
the original disk->src, as well as a use-after-free of the disk->mirror
9f9eae
contents, bad enough to crash libvirtd in some of my test runs, even
9f9eae
though the common case of the qemu event being much later won't trip
9f9eae
the race.
9f9eae
9f9eae
I'll go wear the brown paper bag of shame, for introducing a crasher
9f9eae
in between rc1 and rc2 of the freeze for 1.2.7 :(  My only
9f9eae
consolation is that virDomainBlockJobAbort requires the domain:write
9f9eae
ACL, so it is not a CVE.
9f9eae
9f9eae
The valgrind report when the race occurs looks like:
9f9eae
9f9eae
==25612== Invalid read of size 4
9f9eae
==25612==    at 0x50E7C90: virStorageSourceGetActualType (virstoragefile.c:1948)
9f9eae
==25612==    by 0x209C0B18: qemuDomainDetermineDiskChain (qemu_domain.c:2473)
9f9eae
==25612==    by 0x209D7F6A: qemuProcessHandleBlockJob (qemu_process.c:1087)
9f9eae
==25612==    by 0x209F40C9: qemuMonitorEmitBlockJob (qemu_monitor.c:1357)
9f9eae
...
9f9eae
==25612==  Address 0xe4b5610 is 0 bytes inside a block of size 200 free'd
9f9eae
==25612==    at 0x4A07577: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
9f9eae
==25612==    by 0x50839E9: virFree (viralloc.c:582)
9f9eae
==25612==    by 0x50E7E51: virStorageSourceFree (virstoragefile.c:2015)
9f9eae
==25612==    by 0x209D7EFF: qemuProcessHandleBlockJob (qemu_process.c:1073)
9f9eae
==25612==    by 0x209F40C9: qemuMonitorEmitBlockJob (qemu_monitor.c:1357)
9f9eae
9f9eae
* src/qemu/qemu_driver.c (qemuDomainBlockPivot): Don't corrupt
9f9eae
disk->src, and only label chain for blockcopy.
9f9eae
9f9eae
Signed-off-by: Eric Blake <eblake@redhat.com>
9f9eae
(cherry picked from commit 265680c58ebbee30bb70369e7d9905a599afbd6a)
9f9eae
---
9f9eae
 src/qemu/qemu_driver.c | 40 +++++++++++++++++++++++++---------------
9f9eae
 1 file changed, 25 insertions(+), 15 deletions(-)
9f9eae
9f9eae
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
9f9eae
index 57cc913..a050dbc 100644
9f9eae
--- a/src/qemu/qemu_driver.c
9f9eae
+++ b/src/qemu/qemu_driver.c
9f9eae
@@ -14888,23 +14888,33 @@ qemuDomainBlockPivot(virConnectPtr conn,
9f9eae
         }
9f9eae
     }
9f9eae
 
9f9eae
-    /* We previously labeled only the top-level image; but if the
9f9eae
-     * image includes a relative backing file, the pivot may result in
9f9eae
-     * qemu needing to open the entire backing chain, so we need to
9f9eae
-     * label the entire chain.  This action is safe even if the
9f9eae
-     * backing chain has already been labeled; but only necessary when
9f9eae
-     * we know for sure that there is a backing chain.  */
9f9eae
-    oldsrc = disk->src;
9f9eae
-    disk->src = disk->mirror;
9f9eae
+    /* For active commit, the mirror is part of the already labeled
9f9eae
+     * chain.  For blockcopy, we previously labeled only the top-level
9f9eae
+     * image; but if the user is reusing an external image that
9f9eae
+     * includes a backing file, the pivot may result in qemu needing
9f9eae
+     * to open the entire backing chain, so we need to label the
9f9eae
+     * entire chain.  This action is safe even if the backing chain
9f9eae
+     * has already been labeled; but only necessary when we know for
9f9eae
+     * sure that there is a backing chain.  */
9f9eae
+    if (disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY) {
9f9eae
+        oldsrc = disk->src;
9f9eae
+        disk->src = disk->mirror;
9f9eae
+
9f9eae
+        if (qemuDomainDetermineDiskChain(driver, vm, disk, false) < 0)
9f9eae
+            goto cleanup;
9f9eae
 
9f9eae
-    if (qemuDomainDetermineDiskChain(driver, vm, disk, false) < 0)
9f9eae
-        goto cleanup;
9f9eae
+        if (disk->mirror->format &&
9f9eae
+            disk->mirror->format != VIR_STORAGE_FILE_RAW &&
9f9eae
+            (virDomainLockDiskAttach(driver->lockManager, cfg->uri, vm,
9f9eae
+                                     disk) < 0 ||
9f9eae
+             qemuSetupDiskCgroup(vm, disk) < 0 ||
9f9eae
+             virSecurityManagerSetDiskLabel(driver->securityManager, vm->def,
9f9eae
+                                            disk) < 0))
9f9eae
+            goto cleanup;
9f9eae
 
9f9eae
-    if (disk->mirror->format && disk->mirror->format != VIR_STORAGE_FILE_RAW &&
9f9eae
-        (virDomainLockDiskAttach(driver->lockManager, cfg->uri, vm, disk) < 0 ||
9f9eae
-         qemuSetupDiskCgroup(vm, disk) < 0 ||
9f9eae
-         virSecurityManagerSetDiskLabel(driver->securityManager, vm->def, disk) < 0))
9f9eae
-        goto cleanup;
9f9eae
+        disk->src = oldsrc;
9f9eae
+        oldsrc = NULL;
9f9eae
+    }
9f9eae
 
9f9eae
     /* Attempt the pivot.  Record the attempt now, to prevent duplicate
9f9eae
      * attempts; but the actual disk change will be made when emitting
9f9eae
-- 
9f9eae
1.9.3
9f9eae