Package: libboost-test1.38.0 Version: 1.38.0-7 Severity: normal Tags: patch
While I haven't seen any actual problems from this, Valgrind says that Boost.Test is accessing freed memory, and running a Boost test suite under libefence produces a segfault. A typical valgrind session is attached. After peering at it and at the Boost.Test source code for a while, I came to the conclusion that the problem is due to the fact that the clear() method in framework_impl deletes the stored test cases by accessing an entry in its container of tests: test_unit_store::value_type const& tu = *m_test_units.begin(); // ... delete (test_case const*)tu.second; Here tu.second is a member of the value that's stored in the container. But if you look at the Valgrind backtrace and/or trace into the code, you'll discover that ~test_case actually deletes this container element. I'm not sure why this causes an access to freed memory, but my guess is that the "delete" operator as implemented in g++ doesn't make a temporary copy of the pointer. It's probably referencing the pointer a second time to actually free the memory after running the destructor. This is surprising to me and I don't know the C++ standard well enough to say if it's correct, but it's easy to defensively code around (see below). A simple fix/workaround for this (I'm not sure if Boost.Test or the GNU runtime is at fault) is to copy the pointer to a stack variable and delete that stack variable. const test_unit * const tu_ptr = tu.second; // ... delete (test_case const*)tu_ptr; A full patch is attached; it fixes the valgrind errors for me. Daniel -- System Information: Debian Release: squeeze/sid APT prefers unstable APT policy: (500, 'unstable'), (1, 'experimental') Architecture: i386 (i686) Kernel: Linux 2.6.30-1-686 (SMP w/2 CPU cores) Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/bash Versions of packages libboost-test1.38.0 depends on: ii libc6 2.9-21 GNU C Library: Shared libraries ii libgcc1 1:4.4.1-1 GCC support library ii libstdc++6 4.4.1-1 The GNU Standard C++ Library v3 libboost-test1.38.0 recommends no packages. libboost-test1.38.0 suggests no packages. -- no debconf information
==4802== Memcheck, a memory error detector. ==4802== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al. ==4802== Using LibVEX rev 1884, a library for dynamic binary translation. ==4802== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP. ==4802== Using valgrind-3.4.1-Debian, a dynamic binary instrumentation framework. ==4802== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al. ==4802== For more details, rerun with: -v ==4802== Running 1 test case... *** No errors detected ==4802== Invalid read of size 4 ==4802== at 0x807E350: boost::unit_test::framework_impl::clear() (framework.ipp:133) ==4802== by 0x807E485: boost::unit_test::framework_impl::~framework_impl() (framework.ipp:122) ==4802== by 0x41B8588: exit (in /lib/i686/cmov/libc-2.9.so) ==4802== by 0x419E7AC: (below main) (in /lib/i686/cmov/libc-2.9.so) ==4802== Address 0x42e7b74 is 20 bytes inside a block of size 24 free'd ==4802== at 0x40249DA: operator delete(void*) (vg_replace_malloc.c:342) ==4802== by 0x80700DA: __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::deallocate(std::_Rb_tree_node<std::pair<unsigned long const, boost::unit_test::test_unit*> >*, unsigned int) (new_allocator.h:98) ==4802== by 0x80700FF: std::_Rb_tree<unsigned long, std::pair<unsigned long const, boost::unit_test::test_unit*>, std::_Select1st<std::pair<unsigned long const, boost::unit_test::test_unit*> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::_M_put_node(std::_Rb_tree_node<std::pair<unsigned long const, boost::unit_test::test_unit*> >*) (stl_tree.h:361) ==4802== by 0x807014F: std::_Rb_tree<unsigned long, std::pair<unsigned long const, boost::unit_test::test_unit*>, std::_Select1st<std::pair<unsigned long const, boost::unit_test::test_unit*> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::_M_destroy_node(std::_Rb_tree_node<std::pair<unsigned long const, boost::unit_test::test_unit*> >*) (stl_tree.h:391) ==4802== by 0x8070197: std::_Rb_tree<unsigned long, std::pair<unsigned long const, boost::unit_test::test_unit*>, std::_Select1st<std::pair<unsigned long const, boost::unit_test::test_unit*> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::_M_erase(std::_Rb_tree_node<std::pair<unsigned long const, boost::unit_test::test_unit*> >*) (stl_tree.h:943) ==4802== by 0x80701C6: std::_Rb_tree<unsigned long, std::pair<unsigned long const, boost::unit_test::test_unit*>, std::_Select1st<std::pair<unsigned long const, boost::unit_test::test_unit*> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::clear() (stl_tree.h:697) ==4802== by 0x807DE9A: std::_Rb_tree<unsigned long, std::pair<unsigned long const, boost::unit_test::test_unit*>, std::_Select1st<std::pair<unsigned long const, boost::unit_test::test_unit*> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::erase(std::_Rb_tree_iterator<std::pair<unsigned long const, boost::unit_test::test_unit*> >, std::_Rb_tree_iterator<std::pair<unsigned long const, boost::unit_test::test_unit*> >) (stl_tree.h:1356) ==4802== by 0x807DF2C: std::_Rb_tree<unsigned long, std::pair<unsigned long const, boost::unit_test::test_unit*>, std::_Select1st<std::pair<unsigned long const, boost::unit_test::test_unit*> >, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::erase(unsigned long const&) (stl_tree.h:1345) ==4802== by 0x807DF59: std::map<unsigned long, boost::unit_test::test_unit*, std::less<unsigned long>, std::allocator<std::pair<unsigned long const, boost::unit_test::test_unit*> > >::erase(unsigned long const&) (stl_map.h:538) ==4802== by 0x805D4A6: boost::unit_test::framework::deregister_test_unit(boost::unit_test::test_unit*) (framework.ipp:326) ==4802== by 0x805D554: boost::unit_test::test_unit::~test_unit() (unit_test_suite.ipp:65) ==4802== by 0x807E25E: boost::unit_test::test_case::~test_case() (unit_test_suite_impl.hpp:110) ==4802== ==4802== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 17 from 1) ==4802== malloc/free: in use at exit: 0 bytes in 0 blocks. ==4802== malloc/free: 88 allocs, 88 frees, 35,789 bytes allocated. ==4802== For counts of detected errors, rerun with: -v ==4802== All heap blocks were freed -- no leaks are possible.
--- framework.ipp.orig 2009-07-27 20:49:13.186975084 -0700 +++ framework.ipp 2009-07-27 21:12:07.002974583 -0700 @@ -125,12 +125,13 @@ { while( !m_test_units.empty() ) { test_unit_store::value_type const& tu = *m_test_units.begin(); + const test_unit * const tu_ptr = tu.second; // the delete will erase this element from map if( test_id_2_unit_type( tu.second->p_id ) == tut_suite ) - delete (test_suite const*)tu.second; + delete (test_suite const*)tu_ptr; else - delete (test_case const*)tu.second; + delete (test_case const*)tu_ptr; } }
#define BOOST_TEST_MAIN #include <boost/test/included/unit_test.hpp> BOOST_AUTO_TEST_CASE(testNull) { }
signature.asc
Description: Digital signature