libxml++2.36: "double free or corruption" 如果抛出 std::exception
libxml++2.36: "double free or corruption" if a std::exception is thrown
嗨,
在我的代码中使用 libxml++-2.36-library 时,我发现当 sax-parser-callbacks 中抛出基本类型 std::exception 的异常时,该库会产生 "double free or corruption"-error。 (例如 on_start_document、on_end_document、on_...)
但它表现正常,这意味着,如果抛出基本类型 xmlpp::exception 的异常,则可以捕获异常。
有趣的是 xmlpp::exception 是基于 std::exception.
为了验证这一点,我创建了一个 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;
}
}
可以通过以下方式编译:
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
如果按原样执行文件,我会得到以下输出:
*** Error in `./main': double free or corruption (!prev): 0x0000000000b35b30 ***
如果我取消注释第二行并注释 on_start_document() 的第一行,我得到以下输出:
Caught exception: test-exception
编译器:g++ 4.9.2
libxml++: 2.6 - 2.36
有没有办法让基于 std::exception 的异常正常工作,而不创建特殊的基于 xmlpp::exception 的异常?
发生的事情是 _xmlSAXHandler
对象被释放了两次,一次被 delete
释放,另一次被 xmlFree() 释放。这可以通过在适当的 free() 标准 libc 调用上设置断点来看出。例如,在一台 Ubuntu 'trusty' 机器上,我在 _int_free
上设置了一个断点,看到同一个指针被释放了两次:
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
在这种情况下,0x610b80 对应于 SaxParser 在其 sax_handler_
auto_ptr 成员中持有的 _xmlSAXHandler
对象。它首先由 libxml2 的 xmlFreeParserCtxt() 例程释放。然后它被 std::auto_ptr<_xmlSAXHandler>
析构函数删除。
如果您查看 libxml++ 的 SaxParser class 的源代码,在 saxparser.cc
中,您会看到有几个 try..catch 语句。但是,只有 const exception&
被捕获。这个 const exception
不是 std::exception
而是 xmlpp::exception
.
当您在 on_start_document() 处理程序中抛出 std::runtime_error
时,它不会被 SaxParserCallback::start_document() 捕获。因此,随着堆栈展开,SaxParser::parse() 中恢复 _xmlParserCtxt
中原始 _xmlSAXHandler
指针的代码被跳过。
由此得出的结论是,您应该只在 SaxParser 处理程序方法中抛出源自 xmlpp::exception
的异常。
更新: https://bugzilla.gnome.org/show_bug.cgi?id=753570
UPDATE2: 已在版本 2.39.2 中修复。
嗨, 在我的代码中使用 libxml++-2.36-library 时,我发现当 sax-parser-callbacks 中抛出基本类型 std::exception 的异常时,该库会产生 "double free or corruption"-error。 (例如 on_start_document、on_end_document、on_...)
但它表现正常,这意味着,如果抛出基本类型 xmlpp::exception 的异常,则可以捕获异常。
有趣的是 xmlpp::exception 是基于 std::exception.
为了验证这一点,我创建了一个 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;
}
}
可以通过以下方式编译:
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
如果按原样执行文件,我会得到以下输出:
*** Error in `./main': double free or corruption (!prev): 0x0000000000b35b30 ***
如果我取消注释第二行并注释 on_start_document() 的第一行,我得到以下输出:
Caught exception: test-exception
编译器:g++ 4.9.2 libxml++: 2.6 - 2.36
有没有办法让基于 std::exception 的异常正常工作,而不创建特殊的基于 xmlpp::exception 的异常?
发生的事情是 _xmlSAXHandler
对象被释放了两次,一次被 delete
释放,另一次被 xmlFree() 释放。这可以通过在适当的 free() 标准 libc 调用上设置断点来看出。例如,在一台 Ubuntu 'trusty' 机器上,我在 _int_free
上设置了一个断点,看到同一个指针被释放了两次:
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
在这种情况下,0x610b80 对应于 SaxParser 在其 sax_handler_
auto_ptr 成员中持有的 _xmlSAXHandler
对象。它首先由 libxml2 的 xmlFreeParserCtxt() 例程释放。然后它被 std::auto_ptr<_xmlSAXHandler>
析构函数删除。
如果您查看 libxml++ 的 SaxParser class 的源代码,在 saxparser.cc
中,您会看到有几个 try..catch 语句。但是,只有 const exception&
被捕获。这个 const exception
不是 std::exception
而是 xmlpp::exception
.
当您在 on_start_document() 处理程序中抛出 std::runtime_error
时,它不会被 SaxParserCallback::start_document() 捕获。因此,随着堆栈展开,SaxParser::parse() 中恢复 _xmlParserCtxt
中原始 _xmlSAXHandler
指针的代码被跳过。
由此得出的结论是,您应该只在 SaxParser 处理程序方法中抛出源自 xmlpp::exception
的异常。
更新: https://bugzilla.gnome.org/show_bug.cgi?id=753570
UPDATE2: 已在版本 2.39.2 中修复。