[libvirt] [PATCHv2] snapshot: fix memory leak on error

Daniel Veillard veillard at redhat.com
Fri Apr 6 03:45:33 UTC 2012


On Thu, Apr 05, 2012 at 04:01:03PM -0600, Eric Blake wrote:
> 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

  Okay, though the 'PROTECT' really ought to be a flag on top of the
type of the virJSONType (after all it's really about allocation stategy
rather than a type information) but as a temporary measure, ACK, but
please squash a comment in for the new enum type,

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list