[libvirt] [PATCH] Memory Leak Fix: Check def->info->alias before assigning

On running the command make -C tests valgrind, there used to be a bunch of memory leaks shown by valgrind. Specifically, one can check it by running: libtool --mode=execute valgrind --quiet --leak-check=full --suppressions=./.valgrind.supp qemuhotplugtest The issue was that def->info->alias was already malloc'ed by xmlStrndup in virDomainDeviceInfoParseXML (domain_conf.c:3439). The new alias was being assigned again without freeing the old one in qemuAssignDeviceAliases(). This patch checks if the entity exists, and frees accordingly, hence making valgrind cry lesser. --- src/qemu/qemu_command.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 763417f..bbec1d4 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -979,6 +979,8 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) size_t i; for (i = 0; i < def->ndisks; i++) { + if (def->disks[i]->info.alias) + VIR_FREE(def->disks[i]->info.alias); if (qemuAssignDeviceDiskAlias(def, def->disks[i], qemuCaps) < 0) return -1; } @@ -988,6 +990,9 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) /* type='hostdev' interfaces are also on the hostdevs list, * and will have their alias assigned with other hostdevs. */ + if (def->nets[i]->info.alias) + VIR_FREE(def->nets[i]->info.alias); + if (virDomainNetGetActualType(def->nets[i]) != VIR_DOMAIN_NET_TYPE_HOSTDEV && qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0) { @@ -1000,70 +1005,104 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) return 0; for (i = 0; i < def->nfss; i++) { + if (def->fss[i]->info.alias) + VIR_FREE(def->fss[i]->info.alias); if (virAsprintf(&def->fss[i]->info.alias, "fs%zu", i) < 0) return -1; } for (i = 0; i < def->nsounds; i++) { + if (def->sounds[i]->info.alias) + VIR_FREE(def->sounds[i]->info.alias); if (virAsprintf(&def->sounds[i]->info.alias, "sound%zu", i) < 0) return -1; } for (i = 0; i < def->nhostdevs; i++) { + if (def->hostdevs[i]->info->alias) + VIR_FREE(def->hostdevs[i]->info->alias); if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0) return -1; } for (i = 0; i < def->nredirdevs; i++) { + if (def->redirdevs[i]->info.alias) + VIR_FREE(def->redirdevs[i]->info.alias); if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0) return -1; } for (i = 0; i < def->nvideos; i++) { + if (def->videos[i]->info.alias) + VIR_FREE(def->videos[i]->info.alias); if (virAsprintf(&def->videos[i]->info.alias, "video%zu", i) < 0) return -1; } for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->info.alias) + VIR_FREE(def->controllers[i]->info.alias); if (qemuAssignDeviceControllerAlias(def->controllers[i]) < 0) return -1; } for (i = 0; i < def->ninputs; i++) { + if (def->inputs[i]->info.alias) + VIR_FREE(def->inputs[i]->info.alias); if (virAsprintf(&def->inputs[i]->info.alias, "input%zu", i) < 0) return -1; } for (i = 0; i < def->nparallels; i++) { + if (def->parallels[i]->info.alias) + VIR_FREE(def->parallels[i]->info.alias); if (qemuAssignDeviceChrAlias(def, def->parallels[i], i) < 0) return -1; } for (i = 0; i < def->nserials; i++) { + if (def->serials[i]->info.alias) + VIR_FREE(def->serials[i]->info.alias); if (qemuAssignDeviceChrAlias(def, def->serials[i], i) < 0) return -1; } for (i = 0; i < def->nchannels; i++) { + if (def->channels[i]->info.alias) + VIR_FREE(def->channels[i]->info.alias); if (qemuAssignDeviceChrAlias(def, def->channels[i], i) < 0) return -1; } for (i = 0; i < def->nconsoles; i++) { + if (def->consoles[i]->info.alias) + VIR_FREE(def->consoles[i]->info.alias); if (qemuAssignDeviceChrAlias(def, def->consoles[i], i) < 0) return -1; } for (i = 0; i < def->nhubs; i++) { + if (def->hubs[i]->info.alias) + VIR_FREE(def->hubs[i]->info.alias); if (virAsprintf(&def->hubs[i]->info.alias, "hub%zu", i) < 0) return -1; } for (i = 0; i < def->nsmartcards; i++) { + if (def->smartcards[i]->info.alias) + VIR_FREE(def->smartcards[i]->info.alias); if (virAsprintf(&def->smartcards[i]->info.alias, "smartcard%zu", i) < 0) return -1; } if (def->watchdog) { + if (def->watchdog->info.alias) + VIR_FREE(def->watchdog->info.alias); if (virAsprintf(&def->watchdog->info.alias, "watchdog%d", 0) < 0) return -1; } if (def->memballoon) { + if (def->memballoon->info.alias) + VIR_FREE(def->memballoon->info.alias); if (virAsprintf(&def->memballoon->info.alias, "balloon%d", 0) < 0) return -1; } if (def->rng) { + if (def->rng->info.alias) + VIR_FREE(def->rng->info.alias); if (virAsprintf(&def->rng->info.alias, "rng%d", 0) < 0) return -1; } if (def->tpm) { + if (def->tpm->info.alias) + VIR_FREE(def->tpm->info.alias); if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0) return -1; } -- 1.8.1.4

On 27/11/13 06:31, Nehal J Wani wrote:
On running the command make -C tests valgrind, there used to be a bunch of memory leaks shown by valgrind. Specifically, one can check it by running: libtool --mode=execute valgrind --quiet --leak-check=full --suppressions=./.valgrind.supp qemuhotplugtest The issue was that def->info->alias was already malloc'ed by xmlStrndup in virDomainDeviceInfoParseXML (domain_conf.c:3439). The new alias was being assigned again without freeing the old one in qemuAssignDeviceAliases(). This patch checks if the entity exists, and frees accordingly, hence making valgrind cry lesser.
--- src/qemu/qemu_command.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 763417f..bbec1d4 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -979,6 +979,8 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) size_t i;
for (i = 0; i < def->ndisks; i++) { + if (def->disks[i]->info.alias) + VIR_FREE(def->disks[i]->info.alias);
Instead of free'ing it, it should be used to avoid calculate/strdup the alias again. Since the "alias" from virDomainDeviceInfoParseXML only works for active domain's XML: <snip> if (alias == NULL && !(flags & VIR_DOMAIN_XML_INACTIVE) && xmlStrEqual(cur->name, BAD_CAST "alias")) { alias = cur; </snip> And I don't think using the "alias" existing in the XML could cause any conflicts for an active domain. Regards, Osier

On Wed, Nov 27, 2013 at 04:01:45AM +0530, Nehal J Wani wrote:
On running the command make -C tests valgrind, there used to be a bunch of memory leaks shown by valgrind. Specifically, one can check it by running: libtool --mode=execute valgrind --quiet --leak-check=full --suppressions=./.valgrind.supp qemuhotplugtest The issue was that def->info->alias was already malloc'ed by xmlStrndup in virDomainDeviceInfoParseXML (domain_conf.c:3439). The new alias was being assigned again without freeing the old one in qemuAssignDeviceAliases(). This patch checks if the entity exists, and frees accordingly, hence making valgrind cry lesser.
The 'alias' attribute should *not* be parsed from the XML provided by the user. It should only be parsed in the live state XML. In the latter case no codepath should take us to qemuAssignDeviceAliases. So this is certainly not the right fix. It sounds like the test case is flawed to me. 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 :|

The 'alias' attribute should *not* be parsed from the XML provided by the user. It should only be parsed in the live state XML. In the latter case no codepath should take us to qemuAssignDeviceAliases. So this is certainly not the right fix. It sounds like the test case is flawed to me.
Does the above hold true for qemuDomainAssignPCIAddresses as well? Nehal J Wani

On Wed, Nov 27, 2013 at 03:08:18PM +0530, Nehal J Wani wrote:
The 'alias' attribute should *not* be parsed from the XML provided by the user. It should only be parsed in the live state XML. In the latter case no codepath should take us to qemuAssignDeviceAliases. So this is certainly not the right fix. It sounds like the test case is flawed to me.
Does the above hold true for qemuDomainAssignPCIAddresses as well?
No, it is valid for the user to set PCI addresses 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 :|

The 'alias' attribute should *not* be parsed from the XML provided by the user. It should only be parsed in the live state XML. In the latter case no codepath should take us to qemuAssignDeviceAliases. So this is certainly not the right fix. It sounds like the test case is flawed to me. So, I gave it another round to test the remaining error thrown by valgrind. Here is my finding:
One of the errors received while running valgrind is (not fixed by the patch given before): ==3915== 9 bytes in 1 blocks are definitely lost in loss record 31 of 129 ==3915== at 0x4A0887C: malloc (vg_replace_malloc.c:270) ==3915== by 0x34268AF275: xmlStrndup (in /usr/lib64/libxml2.so.2.9.1) ==3915== by 0x4CCD393: virDomainDeviceInfoParseXML.isra.31 (domain_conf.c:3439) ==3915== by 0x4CD6A49: virDomainChrDefParseXML (domain_conf.c:7258) ==3915== by 0x4CEC0C9: virDomainDeviceDefParse (domain_conf.c:9616) ==3915== by 0x41D14F: testQemuHotplug (qemuhotplugtest.c:247) ==3915== by 0x41E421: virtTestRun (testutils.c:139) ==3915== by 0x41C6E3: mymain (qemuhotplugtest.c:428) ==3915== by 0x41EAB2: virtTestMain (testutils.c:600) ==3915== by 0x341F421A04: (below main) (libc-start.c:225) The corresponding fix is: diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index ec3b958..b7d5390 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -5384,7 +5384,7 @@ qemuMonitorJSONAttachCharDev(qemuMonitorPtr mon, goto cleanup; } - if (VIR_STRDUP(chr->data.file.path, path) < 0) + if (!chr->data.file.path && VIR_STRDUP(chr->data.file.path, path) < 0) goto cleanup; } But I can't confirm whether the testcase is wrong in this case too. -- Nehal J Wani

On 11/27/2013 08:34 AM, Nehal J Wani wrote:
The 'alias' attribute should *not* be parsed from the XML provided by the user. It should only be parsed in the live state XML. In the latter case no codepath should take us to qemuAssignDeviceAliases. So this is certainly not the right fix. It sounds like the test case is flawed to me. So, I gave it another round to test the remaining error thrown by valgrind. Here is my finding:
One of the errors received while running valgrind is (not fixed by the patch given before):
==3915== 9 bytes in 1 blocks are definitely lost in loss record 31 of 129 ==3915== at 0x4A0887C: malloc (vg_replace_malloc.c:270) ==3915== by 0x34268AF275: xmlStrndup (in /usr/lib64/libxml2.so.2.9.1) ==3915== by 0x4CCD393: virDomainDeviceInfoParseXML.isra.31 (domain_conf.c:3439) ==3915== by 0x4CD6A49: virDomainChrDefParseXML (domain_conf.c:7258) ==3915== by 0x4CEC0C9: virDomainDeviceDefParse (domain_conf.c:9616) ==3915== by 0x41D14F: testQemuHotplug (qemuhotplugtest.c:247) ==3915== by 0x41E421: virtTestRun (testutils.c:139) ==3915== by 0x41C6E3: mymain (qemuhotplugtest.c:428) ==3915== by 0x41EAB2: virtTestMain (testutils.c:600) ==3915== by 0x341F421A04: (below main) (libc-start.c:225)
The corresponding fix is:
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index ec3b958..b7d5390 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -5384,7 +5384,7 @@ qemuMonitorJSONAttachCharDev(qemuMonitorPtr mon, goto cleanup; }
- if (VIR_STRDUP(chr->data.file.path, path) < 0) + if (!chr->data.file.path && VIR_STRDUP(chr->data.file.path, path) < 0)
Better might be: VIR_FREE(chr->data.file.path); if (VIR_STRDUP(chr->data.file.path, path) < 0) to just unconditionally set the path to the learned value, no matter what code path previously populated it. But I'm not sure that doing the VIR_FREE in qemu_monitor_json is correct; if it is just the test case that is passing in a pre-populated chr->data.file.path, then we should figure out why the test case is doing that, when all other code paths pass in NULL when expecting the monitor to populate the field. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Wed, Nov 27, 2013 at 09:04:17PM +0530, Nehal J Wani wrote:
The 'alias' attribute should *not* be parsed from the XML provided by the user. It should only be parsed in the live state XML. In the latter case no codepath should take us to qemuAssignDeviceAliases. So this is certainly not the right fix. It sounds like the test case is flawed to me. So, I gave it another round to test the remaining error thrown by valgrind. Here is my finding:
One of the errors received while running valgrind is (not fixed by the patch given before):
==3915== 9 bytes in 1 blocks are definitely lost in loss record 31 of 129 ==3915== at 0x4A0887C: malloc (vg_replace_malloc.c:270) ==3915== by 0x34268AF275: xmlStrndup (in /usr/lib64/libxml2.so.2.9.1) ==3915== by 0x4CCD393: virDomainDeviceInfoParseXML.isra.31 (domain_conf.c:3439) ==3915== by 0x4CD6A49: virDomainChrDefParseXML (domain_conf.c:7258) ==3915== by 0x4CEC0C9: virDomainDeviceDefParse (domain_conf.c:9616) ==3915== by 0x41D14F: testQemuHotplug (qemuhotplugtest.c:247) ==3915== by 0x41E421: virtTestRun (testutils.c:139) ==3915== by 0x41C6E3: mymain (qemuhotplugtest.c:428) ==3915== by 0x41EAB2: virtTestMain (testutils.c:600) ==3915== by 0x341F421A04: (below main) (libc-start.c:225)
The corresponding fix is:
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index ec3b958..b7d5390 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -5384,7 +5384,7 @@ qemuMonitorJSONAttachCharDev(qemuMonitorPtr mon, goto cleanup; }
- if (VIR_STRDUP(chr->data.file.path, path) < 0) + if (!chr->data.file.path && VIR_STRDUP(chr->data.file.path, path) < 0) goto cleanup; }
But I can't confirm whether the testcase is wrong in this case too.
The stack trace indicates that the path came from the XML document parser. This code snippet you show here though only runs when if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { For the PTY type chardevs, it is not valid to specify or parse a pty path from the XML. This is an output-only attribute, not under user control. So I think it looks like the parsing code is wrong, not this monitor code. 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 :|
participants (4)
-
Daniel P. Berrange
-
Eric Blake
-
Nehal J Wani
-
Osier Yang