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

Daniel P. Berrange berrange at redhat.com
Thu Apr 14 12:43:04 UTC 2011


On Wed, Apr 13, 2011 at 05:23:41PM -0600, Eric Blake wrote:
> 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.
> > 
> 
> > @@ -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()?

virNetError() is what my RPC series of patches uses throughout.
Also it will change VIR_FROM_REMOTE to VIR_FROM_RPC

> 
> > @@ -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?

Other files still use it, but it will be gone with the
RPC rewrite.

> 
> > @@ -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.

Opps, yes missed one.

> 
> > @@ -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?

I'm not sure what you mean here ?  If  rv == 0, then 'ret' is expected
to have the data present, and the caller will use xdr_free() to release
it. If rv == -1, then 'ret' is expected to have not been initialized,
hence this code must free that buffer.

> > -  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.

Yes, nice bug :-)

> > @@ -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.

I didn't mean to touch any of these SASL functions, since
they're rather special. Will remove those chunks.

> 
> > @@ -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.

Yes, will do


Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list