[libvirt] [PATCH] Merge all returns paths from dispatcher into single path

Eric Blake eblake at redhat.com
Wed Apr 13 23:23:41 UTC 2011


On 04/13/2011 12:12 PM, Daniel P. Berrange wrote:
> The dispatcher functions have numerous places where they
> return to the caller. This leads to duplicated cleanup
> code, often resulting in memory leaks. It makes it harder
> to ensure that errors are dispatched before freeing objects,
> which may overwrite the original error.
> 

> * daemon/remote.c: Centralize all cleanup paths
> * daemon/stream.c: s/remoteDispatchConnError/remoteDispatchError/
> * daemon/dispatch.c, daemon/dispatch.h: Replace
>   remoteDispatchConnError with remoteDispatchError
>   removing unused virConnectPtr

> +++ b/daemon/dispatch.c
> @@ -112,8 +112,7 @@ void remoteDispatchOOMError (remote_error *rerr)
>  }
>  
>  
> -void remoteDispatchConnError (remote_error *rerr,
> -                              virConnectPtr conn ATTRIBUTE_UNUSED)
> +void remoteDispatchError(remote_error *rerr)

Two cleanups for the price of one (rename function to nuke unused
parameter, while adding goto cleanup); but I'm okay with lumping them in
the same commit.

> @@ -458,20 +467,25 @@ remoteDispatchSupportsFeature(struct qemud_server *server ATTRIBUTE_UNUSED,
>                                remote_error *rerr,
>                                remote_supports_feature_args *args, remote_supports_feature_ret *ret)
>  {
> +    int rv = -1;
>  
>      if (!conn) {
> -        remoteDispatchFormatError(rerr, "%s", _("connection not open"));
> -        return -1;
> +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));

Is virNetError really the best name for the new helper macro, especially
since VIR_FROM_THIS is VIR_FROM_REMOTE?  Should it instead be named
virRemoteError()?

> @@ -500,11 +514,16 @@ remoteDispatchGetType(struct qemud_server *server ATTRIBUTE_UNUSED,
>       */
>      ret->type = strdup(type);
>      if (!ret->type) {
> -        remoteDispatchOOMError(rerr);
> -        return -1;
> +        virReportOOMError();
> +        goto cleanup;
>      }
>  
> -    return 0;
> +    rv = 0;
> +
> +cleanup:
> +    if (rv < 0)
> +        remoteDispatchError(rerr);

That works.  I wonder if we can completely get rid of
remoteDispatchOOMError?

> @@ -837,51 +911,47 @@ remoteDispatchDomainGetSchedulerParameters(struct qemud_server *server ATTRIBUTE
>                                             remote_domain_get_scheduler_parameters_args *args,
>                                             remote_domain_get_scheduler_parameters_ret *ret)
>  {

>      if (VIR_ALLOC_N(params, nparams) < 0) {
> -        remoteDispatchOOMError(rerr);
> -        return -1;
> +        virReportOOMError();
> +        goto cleanup;

This could be goto no_memory, rather than open-coding the OOM call,
since you already have that label for other reasons in this function.

> @@ -1146,21 +1232,21 @@ remoteDispatchDomainBlockPeek(struct qemud_server *server ATTRIBUTE_UNUSED,
>                                remote_domain_block_peek_args *args,
>                                remote_domain_block_peek_ret *ret)
>  {

>      if (virDomainBlockPeek(dom, path, offset, size,
>                             ret->buffer.buffer_val, flags) == -1) {
> -        /* free(ret->buffer.buffer_val); - caller frees */
> -        remoteDispatchConnError(rerr, conn);
> -        virDomainFree(dom);
> -        return -1;
> +        goto cleanup;
>      }
> -    virDomainFree(dom);
>  
> -    return 0;
> +    rv = 0;
> +
> +cleanup:
> +    if (rv < 0) {
> +        remoteDispatchError(rerr);
> +        VIR_FREE(ret->buffer.buffer_val);

Can we also clean up the caller to quit freeing (as a separate patch),
now that we guarantee the cleanup here?  Should we have a 'make
syntax-check' rule that catches unadorned use of free() instead of VIR_FREE?

> @@ -1780,46 +1959,46 @@ remoteDispatchDomainGetSecurityLabel(struct qemud_server *server ATTRIBUTE_UNUSE
>                                       remote_domain_get_security_label_args *args,
>                                       remote_domain_get_security_label_ret *ret)
>  {
> -    virDomainPtr dom;
> +    virDomainPtr dom = NULL;
>      virSecurityLabelPtr seclabel;
> +    int rv = -1;
>  
>      if (!conn) {
> -        remoteDispatchFormatError(rerr, "%s", _("connection not open"));
> -        return -1;
> +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
> +        goto cleanup;

> +cleanup:
> +    if (rv < 0)
> +        remoteDispatchError(rerr);
> +    if (dom)
> +        virDomainFree(dom);
> +    VIR_FREE(seclabel);

Oops - freeing an uninitialized pointer.  Init seclabel to NULL.

> @@ -2730,37 +3039,37 @@ remoteDispatchDomainSetMemoryParameters(struct qemud_server *server
>                                          remote_domain_set_memory_parameters_args
>                                          * args, void *ret ATTRIBUTE_UNUSED)
>  {
> -    virDomainPtr dom;
> +    virDomainPtr dom = NULL;
>      int i, r, nparams;
>      virMemoryParameterPtr params;
>      unsigned int flags;
> +    int rv = -1;
>  
>      if (!conn) {
> -        remoteDispatchFormatError(rerr, "%s", _("connection not open"));
> -        return -1;
> +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
> +        goto cleanup;

> +cleanup:
> +    if (rv < 0)
> +        remoteDispatchError(rerr);
> +    VIR_FREE(params);

Oops - another free(uninitialized).

> @@ -2830,41 +3141,37 @@ remoteDispatchDomainGetMemoryParameters(struct qemud_server *server
>                                          remote_domain_get_memory_parameters_ret
>                                          * ret)
>  {
> -    virDomainPtr dom;
> +    virDomainPtr dom = NULL;
>      virMemoryParameterPtr params;
>      int i, r, nparams;
>      unsigned int flags;
> +    int rv = -1;
>  
>      if (!conn) {
> -        remoteDispatchFormatError(rerr, "%s", _("connection not open"));
> -        return -1;
> +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
> +        goto cleanup;

And again, params must be NULL.

> -  success:
> -    virDomainFree(dom);
> -    VIR_FREE(params);
> -
> -    return 0;
> +success:
> +    rv = 0;
>  
> -  oom:
> -    remoteDispatchOOMError(rerr);
> -  cleanup:
> -    virDomainFree(dom);
> -    for (i = 0; i < nparams; i++)
> -        VIR_FREE(ret->params.params_val[i].field);
> +no_memory:
> +    virReportOOMError();

Ouch.  You didn't mean for success: to fall through to no_memory:, but
to cleanup:.  Sink the OOM reporting down, and then jump back to cleanup.

> @@ -2951,37 +3260,37 @@ remoteDispatchDomainSetBlkioParameters(struct qemud_server *server
>                                          remote_domain_set_blkio_parameters_args
>                                          * args, void *ret ATTRIBUTE_UNUSED)
>  {
> -    virDomainPtr dom;
> +    virDomainPtr dom = NULL;
>      int i, r, nparams;
>      virBlkioParameterPtr params;

Same problem as SetMemoryParameters.

> @@ -3051,41 +3362,37 @@ remoteDispatchDomainGetBlkioParameters(struct qemud_server *server
>                                          remote_domain_get_blkio_parameters_ret
>                                          * ret)
>  {
> -    virDomainPtr dom;
> +    virDomainPtr dom = NULL;
>      virBlkioParameterPtr params;
>      int i, r, nparams;
>      unsigned int flags;

> +
> +no_memory:
> +    virReportOOMError();
> +    goto cleanup;
>  }

Same problem as GetMemoryParameters with params being initialized, but
at least the no_memory label worked this time.

> @@ -4287,7 +4814,7 @@ remoteDispatchAuthList(struct qemud_server *server,
>  static int
>  remoteDispatchAuthSaslInit(struct qemud_server *server,
>                             struct qemud_client *client,
> -                           virConnectPtr conn,
> +                           virConnectPtr conn ATTRIBUTE_UNUSED,
>                             remote_message_header *hdr ATTRIBUTE_UNUSED,
>                             remote_error *rerr,
>                             void *args ATTRIBUTE_UNUSED,

>      if ((localAddr = virSocketFormatAddrFull(&sa, true, ";")) == NULL) {
> -        remoteDispatchConnError(rerr, conn);
>          goto error;

Losing the error reporting here...

> @@ -4439,7 +4964,7 @@ authfail:
>  error:
>      PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_SASL);
>      virMutexUnlock(&client->lock);
> -    return -1;
> +    goto error;

...and replacing it with an infinite loop here.  I don't think that's
what you meant to do.

> @@ -4600,7 +5125,7 @@ remoteDispatchAuthSaslStart(struct qemud_server *server,
>      /* NB, distinction of NULL vs "" is *critical* in SASL */
>      if (serverout) {
>          if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) {
> -            remoteDispatchOOMError(rerr);
> +            virReportOOMError();
>              goto error;
>          }

This changes from remoteDispatchOOMError to virReportOOM, but the error:
label doesn't call remoteDispatchError to actually dispatch it.

> @@ -4701,7 +5226,7 @@ remoteDispatchAuthSaslStep(struct qemud_server *server,
>      /* NB, distinction of NULL vs "" is *critical* in SASL */
>      if (serverout) {
>          if (VIR_ALLOC_N(ret->data.data_val, serveroutlen) < 0) {
> -            remoteDispatchOOMError(rerr);
> +            virReportOOMError();
>              goto error;
>          }

Likewise.

> @@ -4878,7 +5403,7 @@ remoteDispatchAuthPolkit(struct qemud_server *server,
>      client->auth = REMOTE_AUTH_NONE;
>  
>      virMutexUnlock(&client->lock);
> -    return 0;
> +    rv = 0;
>  
>  authfail:
>      PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd,
REMOTE_AUTH_POLKIT);

Oops - that was unintended fallthrough.

> @@ -5010,7 +5535,7 @@ remoteDispatchAuthPolkit(struct qemud_server *server,
>      client->auth = REMOTE_AUTH_NONE;
>  
>      virMutexUnlock(&client->lock);
> -    return 0;
> +    rv = 0;
>  
>  authfail:
>      PROBE(CLIENT_AUTH_FAIL, "fd=%d, auth=%d", client->fd, REMOTE_AUTH_POLKIT);

As was that.

> @@ -6245,18 +6963,18 @@ remoteDispatchNodeDeviceGetParent(struct qemud_server *server ATTRIBUTE_UNUSED,
>                                    remote_node_device_get_parent_args *args,
>                                    remote_node_device_get_parent_ret *ret)
>  {
> -    virNodeDevicePtr dev;
> +    virNodeDevicePtr dev = NULL;
>      const char *parent;
> +    int rv = -1;
>  
>      if (!conn) {
> -        remoteDispatchFormatError(rerr, "%s", _("connection not open"));
> -        return -1;
> +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
> +        goto cleanup;

Oops, parent is uninitialized on this path...

>      }
>  
>      dev = virNodeDeviceLookupByName(conn, args->name);
>      if (dev == NULL) {
> -        remoteDispatchConnError(rerr, conn);
> -        return -1;
> +        goto cleanup;
>      }
>  
>      parent = virNodeDeviceGetParent(dev);

...and malloc'd on this path.

> @@ -6267,21 +6985,25 @@ remoteDispatchNodeDeviceGetParent(struct qemud_server *server ATTRIBUTE_UNUSED,
>          /* remoteDispatchClientRequest will free this. */
>          char **parent_p;
>          if (VIR_ALLOC(parent_p) < 0) {
> -            virNodeDeviceFree(dev);
> -            remoteDispatchOOMError(rerr);
> -            return -1;
> +            virReportOOMError();
> +            goto cleanup;
>          }
>          *parent_p = strdup(parent);

Rather than strdup()ing malloc's memory, this should be:

*parent_p = parent;
parent = NULL;

>          if (*parent_p == NULL) {
> -            virNodeDeviceFree(dev);
> -            remoteDispatchOOMError(rerr);
> -            return -1;
> +            virReportOOMError();
> +            goto cleanup;
>          }
>          ret->parent = parent_p;
>      }
>  
> -    virNodeDeviceFree(dev);
> -    return 0;
> +    rv = 0;
> +
> +cleanup:
> +    if (rv < 0)
> +        remoteDispatchError(rerr);
> +    if (dev)
> +        virNodeDeviceFree(dev);

...and add unconditional VIR_FREE(parent) here.  I'm surprised we
haven't noticed that leak before.

> @@ -7784,32 +8672,36 @@ remoteDispatchDomainSnapshotCurrent(struct qemud_server *server ATTRIBUTE_UNUSED
>                                      remote_domain_snapshot_current_ret *ret)
>  {
>      virDomainSnapshotPtr snapshot;
> -    virDomainPtr domain;
> +    virDomainPtr domain = NULL;
> +    int rv = -1;
>  
>      if (!conn) {
> -        remoteDispatchFormatError(rerr, "%s", _("connection not open"));
> -        return -1;
> +        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
> +        goto cleanup;

> +cleanup:
> +    if (rv < 0)
> +        remoteDispatchError(rerr);
> +    if (snapshot)
> +        virDomainSnapshotFree(snapshot);

Oops, freeing uninitialized variable.

All the rest look fine.

-- 
Eric Blake   eblake at redhat.com    +1-801-349-2682
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 619 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20110413/4a56dd8e/attachment-0001.sig>


More information about the libvir-list mailing list