
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@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org