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