The qemudDomainSaveFlag method will call EndJob on the 'vm'
object it is passed in. This can result in the 'vm' object
being free'd if the last reference is removed. Thus no caller
of 'qemudDomainSaveFlag' must *ever* reference 'vm' again
upon return.
Unfortunately qemudDomainSave and qemuDomainManagedSave
both call 'virDomainObjUnlock', which can result in a
crash. This is non-deterministic since it involves a race
with the monitor I/O thread.
Fix this by making qemudDomainSaveFlag responsible for
calling virDomainObjUnlock instead.
* src/qemu/qemu_driver.c: Fix potential use after free
when saving guests
---
src/qemu/qemu_driver.c | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index d63f57d..363a361 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2091,7 +2091,10 @@ qemuCompressProgramName(int compress)
}
/* This internal function expects the driver lock to already be held on
- * entry and the vm must be active.
+ * entry and the vm must be active + locked. Vm will be unlocked and
+ * potentially free'd after this returns (eg transient VMs are freed
+ * shutdown). So 'vm' must not be referenced by the caller after
+ * this returns (whether returning success or failure).
*/
static int qemudDomainSaveFlag(struct qemud_driver *driver, virDomainPtr dom,
virDomainObjPtr vm, const char *path,
@@ -2318,6 +2321,8 @@ cleanup:
unlink(path);
if (event)
qemuDomainEventQueue(driver, event);
+ if (vm)
+ virDomainObjUnlock(vm);
return ret;
}
@@ -2380,6 +2385,7 @@ static int qemudDomainSave(virDomainPtr dom, const char *path)
}
ret = qemudDomainSaveFlag(driver, dom, vm, path, compressed);
+ vm = NULL;
cleanup:
if (vm)
@@ -2436,6 +2442,7 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags)
compressed = QEMUD_SAVE_FORMAT_RAW;
ret = qemudDomainSaveFlag(driver, dom, vm, name, compressed);
+ vm = NULL;
cleanup:
if (vm)
--
1.7.4.4