Subject: | libxml2 optimization in xmlAddSibling leads to a double free in DESTROY. |
Date: | Mon, 24 Mar 2014 13:28:30 -0400 |
To: | bug-XML-LibXML [...] rt.cpan.org |
From: | Jeff <jeff [...] jefftrout.com> |
Been hitting a bug that randomly occurs, but always from the guts of XML::LibXML. however, I’ve never been able to reliably reproduce it until today. After debugging I was able to get to the root of the problem:
In libxml2’s tree.c xmlAddSibling has an optimization where if the existing node is text and you are appendSibling a text node it will instead append the text onto the existing sibling then xmlFree the new sibling. When done via XML::LibXML this effectively free’s our xmlNode out from under us, then when DESTROY is run we end up with a double free, which can lead to all sorts of neat things. (In my case, it would sometimes lock up in the deep down in free())
Code to induce it is pretty simple:
use strict;
use XML::LibXML;
my $orig = new XML::LibXML::Text("Double ");
$orig->addSibling(new XML::LibXML::Text("Free"));
valgrind output confirms:
jeff@debian:~/insiderscore/site$ valgrind perl doublefree.pl
==13673== Memcheck, a memory error detector
==13673== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==13673== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==13673== Command: perl doublefree.pl
==13673==
==13673== Invalid read of size 8
==13673== at 0x6C798C8: PmmREFCNT_dec (perl-libxml-mm.c:448)
==13673== by 0x6C5DB3E: XS_XML__LibXML__Node_DESTROY (LibXML.xs:4147)
==13673== by 0x4EE764B: Perl_pp_entersub (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x4E7AAD0: Perl_call_sv (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x4EEDB18: Perl_sv_clear (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x4EEE1D1: Perl_sv_free2 (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x4F139BF: Perl_free_tmps (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x4E80633: perl_run (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x400F88: main (in /usr/bin/perl)
==13673== Address 0x7701380 is 0 bytes inside a block of size 120 free'd
==13673== at 0x4C27D4E: free (vg_replace_malloc.c:427)
==13673== by 0x6EF0CC2: xmlAddSibling (in /usr/lib/x86_64-linux-gnu/libxml2.so.2.8.0)
==13673== by 0x6C5ACE5: XS_XML__LibXML__Node_addSibling (LibXML.xs:5041)
==13673== by 0x4EE764B: Perl_pp_entersub (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x4EDEC25: Perl_runops_standard (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x4E80754: perl_run (in /usr/lib/libperl.so.5.14.2)
==13673== by 0x400F88: main (in /usr/bin/perl)
==13673==
I’ve updated my application to detect this scenario and turn it into an append rather than addSibling, however XML::LibXML may want to add a check for that as well.
--
Jeff Trout <jeff@jefftrout.com>