Leak introduced in commit 0436d32. If we allocate an actions array,
but fail early enough to never consume it with the qemu monitor
transaction call, we leaked memory.
But our semantics of making the transaction command free the caller's
memory is awkward; avoiding the memory leak requires making every
intermediate function in the call chain check for error. It is much
easier to fix things so that the function that allocates also frees,
while the call chain leaves the caller's data intact. To do that,
I had to hack our JSON data structure to make it easy to protect a
portion of an arbitrary JSON tree from being freed.
* src/util/json.h (VIR_JSON_TYPE_PROTECT): New marker.
* src/util/json.c (virJSONValueFree): Use it to protect a portion
of an array.
(virJSONParserInsertValue, virJSONValueToStringOne): Sanity
checking.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONTransaction): Avoid
freeing caller's data.
* src/qemu/qemu_driver.c (qemuDomainSnapshotCreateDiskActive):
Free actions array on failure.
---
v2: fix the problem correctly (v1 still had a latent leak in
qemu_monitor.c:qemuMonitorTransaction() if it were ever called with
a text monitor)
Okay, so v1 was minimal, and v2 is 18 times more lines of diff,
but I like this version better, as it makes it so I don't have
to think as hard in the future when I add more code that uses
transaction.
src/qemu/qemu_driver.c | 1 +
src/qemu/qemu_monitor_json.c | 11 ++++++-----
src/util/json.c | 17 +++++++++++++----
src/util/json.h | 7 ++++---
4 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index dd79973..0456b34 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -10127,6 +10127,7 @@ qemuDomainSnapshotCreateDiskActive(virConnectPtr conn,
if (actions) {
if (ret == 0)
ret = qemuMonitorTransaction(priv->mon, actions);
+ virJSONValueFree(actions);
if (ret < 0) {
/* Transaction failed; undo the changes to vm. */
bool need_unlink = !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 7093e1d..e1803bf 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -3157,22 +3157,19 @@ cleanup:
return ret;
}
-/* Note that this call frees actions regardless of whether the call
- * succeeds. */
int
qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
+ int type;
cmd = qemuMonitorJSONMakeCommand("transaction",
"a:actions", actions,
NULL);
- if (!cmd) {
- virJSONValueFree(actions);
+ if (!cmd)
return -1;
- }
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
@@ -3180,7 +3177,11 @@ qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr
actions)
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
+ /* We do NOT want to free actions when recursively freeing cmd. */
+ type = actions->type;
+ actions->type = VIR_JSON_TYPE_PROTECT;
virJSONValueFree(cmd);
+ actions->type = type;
virJSONValueFree(reply);
return ret;
}
diff --git a/src/util/json.c b/src/util/json.c
index a85f580..2e1295a 100644
--- a/src/util/json.c
+++ b/src/util/json.c
@@ -1,7 +1,7 @@
/*
* json.c: JSON object parsing/formatting
*
- * Copyright (C) 2009-2010 Red Hat, Inc.
+ * Copyright (C) 2009-2010, 2012 Red Hat, Inc.
* Copyright (C) 2009 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
@@ -70,7 +70,7 @@ void virJSONValueFree(virJSONValuePtr value)
if (!value)
return;
- switch (value->type) {
+ switch ((virJSONType) value->type) {
case VIR_JSON_TYPE_OBJECT:
for (i = 0 ; i < value->data.object.npairs; i++) {
VIR_FREE(value->data.object.pairs[i].key);
@@ -89,6 +89,11 @@ void virJSONValueFree(virJSONValuePtr value)
case VIR_JSON_TYPE_NUMBER:
VIR_FREE(value->data.number);
break;
+ case VIR_JSON_TYPE_BOOLEAN:
+ case VIR_JSON_TYPE_NULL:
+ break;
+ case VIR_JSON_TYPE_PROTECT:
+ return;
}
VIR_FREE(value);
@@ -633,6 +638,10 @@ int virJSONValueObjectIsNull(virJSONValuePtr object, const char
*key)
static int virJSONParserInsertValue(virJSONParserPtr parser,
virJSONValuePtr value)
{
+ if (value->type == VIR_JSON_TYPE_PROTECT) {
+ VIR_DEBUG("invalid value to insert");
+ return -1;
+ }
if (!parser->head) {
parser->head = value;
} else {
@@ -968,7 +977,7 @@ static int virJSONValueToStringOne(virJSONValuePtr object,
VIR_DEBUG("object=%p type=%d gen=%p", object, object->type, g);
- switch (object->type) {
+ switch ((virJSONType) object->type) {
case VIR_JSON_TYPE_OBJECT:
if (yajl_gen_map_open(g) != yajl_gen_status_ok)
return -1;
@@ -1017,7 +1026,7 @@ static int virJSONValueToStringOne(virJSONValuePtr object,
return -1;
break;
- default:
+ case VIR_JSON_TYPE_PROTECT:
return -1;
}
diff --git a/src/util/json.h b/src/util/json.h
index 4572654..88c9ab4 100644
--- a/src/util/json.h
+++ b/src/util/json.h
@@ -1,8 +1,8 @@
/*
* json.h: JSON object parsing/formatting
*
+ * Copyright (C) 2009, 2012 Red Hat, Inc.
* Copyright (C) 2009 Daniel P. Berrange
- * Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -27,14 +27,15 @@
# include "internal.h"
-enum {
+typedef enum {
VIR_JSON_TYPE_OBJECT,
VIR_JSON_TYPE_ARRAY,
VIR_JSON_TYPE_STRING,
VIR_JSON_TYPE_NUMBER,
VIR_JSON_TYPE_BOOLEAN,
VIR_JSON_TYPE_NULL,
-};
+ VIR_JSON_TYPE_PROTECT,
+} virJSONType;
typedef struct _virJSONValue virJSONValue;
typedef virJSONValue *virJSONValuePtr;
--
1.7.7.6