Hy, while using the libxml++-2.36-library for my code I found out, that this library produces a "double free or corruption"-error, when exceptions of the basetype std::exception are thrown in the sax-parser-callbacks. (e.g. on_start_document, on_end_document, on_...)
But it behaves normally, this means, that the exception can be catched, if an exception of basetype xmlpp::exception is thrown.
Interestingly xmlpp::exception is based on std::exception.
To verify this, I have created a MWE:
#include <libxml++/libxml++.h>
#include <iostream>
class xml_parser : public xmlpp::SaxParser {
protected:
virtual void on_start_document() override;
};
void xml_parser::on_start_document() {
throw std::runtime_error("test-exception");
//throw xmlpp::internal_error("test-exception");
}
int main(void) {
xml_parser parser;
try {
parser.parse_memory(
u8"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
} catch (std::exception &e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
}
It can be compiled by:
g++ main.cpp -o main `pkg-config --cflags --libs libxml++-2.6` -Wall -pedantic -Wextra -Werror -Wcast-qual -Wcast-align -Wconversion -fdiagnostics-color=auto -g -O2 -std=c++11
If I execute the file as it is, I get the following output:
*** Error in `./main': double free or corruption (!prev): 0x0000000000b35b30 ***
If I uncomment the second line and comment the first line of the on_start_document(), I get the following output:
Caught exception: test-exception
Compiler: g++ 4.9.2 libxml++: 2.6 - 2.36
Is there a way, to get std::exception based exceptions working, without creating special xmlpp::exception-Based Exceptions?
What's happening is that an _xmlSAXHandler
object is being freed twice, once by delete
and another by xmlFree(). This can be seen by setting a break point on the appropriate free() standard libc call. For example, on an Ubuntu 'trusty' machine, I set a breakpoint on _int_free
and saw that the same pointer was being freed twice:
Breakpoint 2, _int_free (av=0x7ffff741f760 , p=0x610b80, have_lock=0) at malloc.c:3814 3814 in malloc.c (gdb) bt #0 _int_free (av=0x7ffff741f760 , p=0x610b80, have_lock=0) at malloc.c:3814 #1 0x00007ffff6d32a59 in xmlFreeParserCtxt () from /usr/lib/x86_64-linux-gnu/libxml2.so.2 #2 0x00007ffff7bc6be2 in xmlpp::Parser::release_underlying ( this=this@entry=0x7fffffffdd30) at libxml++/parsers/parser.cc:162 #3 0x00007ffff7bcc665 in xmlpp::SaxParser::release_underlying ( this=this@entry=0x7fffffffdd30) at libxml++/parsers/saxparser.cc:329 #4 0x00007ffff7bcc68c in xmlpp::SaxParser::~SaxParser (this=0x7fffffffdd30, __in_chrg=) at libxml++/parsers/saxparser.cc:83 #5 0x0000000000401d80 in ~xml_parser (this=0x7fffffffdd30, __in_chrg=) at so31969961_libxmlpp_double_free.cpp:7 #6 main () at so31969961_libxmlpp_double_free.cpp:24 ... Breakpoint 2, _int_free (av=0x7ffff741f760 , p=0x610b80, have_lock=0) at malloc.c:3814 3814 in malloc.c (gdb) bt #0 _int_free (av=0x7ffff741f760 , p=0x610b80, have_lock=0) at malloc.c:3814 #1 0x00007ffff7bcc69e in ~auto_ptr (this=0x7fffffffdd60, __in_chrg=) at /usr/include/c++/4.8/backward/auto_ptr.h:170 #2 xmlpp::SaxParser::~SaxParser (this=0x7fffffffdd30, __in_chrg=) at libxml++/parsers/saxparser.cc:81 #3 0x0000000000401d80 in ~xml_parser (this=0x7fffffffdd30, __in_chrg=) at so31969961_libxmlpp_double_free.cpp:7 #4 main () at so31969961_libxmlpp_double_free.cpp:24
In this case, 0x610b80 corresponds to the _xmlSAXHandler
object held by the SaxParser in its sax_handler_
auto_ptr member. It is first freed by libxml2's xmlFreeParserCtxt() routine. It is then deleted by the std::auto_ptr<_xmlSAXHandler>
destructor.
If you look at the source of libxml++'s SaxParser class, in saxparser.cc
, you will see that there are several try..catch statements. However, only const exception&
is caught. This const exception
is not std::exception
but rather xmlpp::exception
.
When you throw a std::runtime_error
in the on_start_document() handler, it is not caught by SaxParserCallback::start_document(). Consequently, as the stack is unwound, the code in SaxParser::parse() that restores the original _xmlSAXHandler
pointer in the _xmlParserCtxt
is skipped.
The takeaway from this is that you should only throw exceptions deriving from xmlpp::exception
within the SaxParser handler methods.
UPDATE: https://bugzilla.gnome.org/show_bug.cgi?id=753570
UPDATE2: Fixed in version 2.39.2.