GNOME Bugzilla – Bug 768404
Memory leak when using remove_attribute
Last modified: 2016-07-06 14:46:41 UTC
Hi! LeakSanitizer told me that there is a memory leak in libxml++. Test Program: ___________________________________________________________________ #include <libxml++/libxml++.h> int main(int , char **) { xmlpp::Document document; auto nodeRoot = document.create_root_node("exampleroot"); auto nodeChild = nodeRoot->add_child_element("examplechild"); nodeChild->set_attribute("woswasidenn", "bla"); nodeChild->remove_attribute("woswasidenn"); return 0; } ___________________________________________________________________ Command Line to compile the program: g++ -g -fsanitize=address -std=c++11 $(pkg-config --cflags libxml++-3.0) main.cpp $(pkg-config --libs libxml++-3.0) -lz Running this prodram produces the following output: ___________________________________________________________________ ================================================================= ==4877==ERROR: LeakSanitizer: detected memory leaks Direct leak of 16 byte(s) in 1 object(s) allocated from: #0 0x7ff3de780452 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99452) #1 0x40a509 in xmlpp::Node::create_wrapper(_xmlNode*) nodes/node.cc:534 #2 0x6e6e64 (<unknown module>) SUMMARY: AddressSanitizer: 16 byte(s) leaked in 1 allocation(s). ___________________________________________________________________ I created the following patch to correct the problem: ___________________________________________________________________ diff -Naur libxml++.orig-3.0.0/libxml++/nodes/element.cc libxml++-3.0.0/libxml++/nodes/element.cc --- libxml++.orig-3.0.0/libxml++/nodes/element.cc 2015-11-05 11:04:56.000000000 +0100 +++ libxml++-3.0.0/libxml++/nodes/element.cc 2016-04-18 13:29:39.250433896 +0200 @@ -134,12 +134,35 @@ void Element::remove_attribute(const Glib::ustring& name, const Glib::ustring& ns_prefix) { if (ns_prefix.empty()) + { + xmlAttr *attr = xmlHasProp(cobj(), (const xmlChar*)name.c_str()); + if (!attr) + return; + if (attr->type != XML_ATTRIBUTE_DECL) + { + // *this has an attribute with the same name as the imported attribute. + // xmlAddChild() will delete the existing attribute. + // Delete the C++ wrapper before the call to xmlAddChild(). + Node::free_wrappers(reinterpret_cast<xmlNode*>(attr)); + } xmlUnsetProp(cobj(), (const xmlChar*)name.c_str()); + } else { auto ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str()); - if (ns) - xmlUnsetNsProp(cobj(), ns, (const xmlChar*)name.c_str()); + if (!ns) + return; + xmlAttr *attr = xmlHasNsProp(cobj(), (const xmlChar*)name.c_str(), (const xmlChar*)ns_prefix.c_str()); + if (!attr) + return; + if (attr->type != XML_ATTRIBUTE_DECL) + { + // *this has an attribute with the same name as the imported attribute. + // xmlAddChild() will delete the existing attribute. + // Delete the C++ wrapper before the call to xmlAddChild(). + Node::free_wrappers(reinterpret_cast<xmlNode*>(attr)); + } + xmlUnsetNsProp(cobj(), ns, (const xmlChar*)name.c_str()); } } ___________________________________________________________________ I guess this patch will not meet your standards, but it shows the problematic bits of code. regards
Thanks. I've pushed a patch similar to your patch. https://git.gnome.org/browse/libxml++/commit/?id=c75f521cefe1d6ded3e6d67397ecbbdde2ce3b48 I made mainly two changes: 1. An obvious flaw in your patch is that the comment that you copied from Node::import_node() does not fit here. 2. A much less obvious flaw is that you used xmlHasProp(). It finds an attribute with a specified name, regardless of the attribute's namespace, while xmlUnsetProp() only removes an attribute with no namespace. Unfortunately that's not clear from the documentation of xmlHasProp().