libxml ++ 2.36:如果抛出std :: exception,则为“double free or corruption”

时间:2015-08-12 15:47:31

标签: c++11 c++14 libxml2

HY, 当我的代码使用libxml ++ - 2.36-library时,我发现,当在sax-parser-callbacks中抛出basetype std :: exception的异常时,这个库会产生“双重释放或损坏”错误。 (例如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 ::基于异常的异常?

1 个答案:

答案 0 :(得分:1)

正在发生的事情是_xmlSAXHandler对象被释放两次,一次由delete释放,另一次由xmlFree()释放。通过在适当的free()标准libc调用上设置断点可以看出这一点。例如,在Ubuntu&#39; trusty&#39;机器,我在_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在其_xmlSAXHandler auto_ptr成员中持有的sax_handler_对象。它首先由libxml2的xmlFreeParserCtxt()例程释放。然后由std::auto_ptr<_xmlSAXHandler>析构函数删除它。

如果你看一下libxml ++的SaxParser类的来源,在saxparser.cc中,你会看到有几个try..catch语句。但是,只有const exception&被捕获。此const exception不是std::exception,而是xmlpp::exception

当您在on_start_document()处理程序中抛出std::runtime_error时,它不会被SaxParserCallback :: start_document()捕获。因此,当堆栈展开时,将跳过恢复_xmlSAXHandler中原始_xmlParserCtxt指针的SaxParser :: parse()中的代码。

这样做的一点是,你应该只在SaxParser处理程序方法中抛出源自xmlpp::exception的异常。

更新: https://bugzilla.gnome.org/show_bug.cgi?id=753570

UPDATE2:已在2.39.2版中修复。