[pkg-boost-devel] Bug#538946: Valgrind reports access to freed memory from Boost.Test.

Daniel Burrows dburrows at debian.org
Tue Jul 28 04:21:39 UTC 2009


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
-------------- next part --------------
==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.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: boost-test-valgrind-fix.patch
Type: text/x-diff
Size: 679 bytes
Desc: not available
URL: <http://lists.alioth.debian.org/pipermail/pkg-boost-devel/attachments/20090727/1ab07d33/attachment.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test.cc
Type: text/x-c++src
Size: 106 bytes
Desc: not available
URL: <http://lists.alioth.debian.org/pipermail/pkg-boost-devel/attachments/20090727/1ab07d33/attachment.cc>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: Digital signature
URL: <http://lists.alioth.debian.org/pipermail/pkg-boost-devel/attachments/20090727/1ab07d33/attachment.pgp>


More information about the pkg-boost-devel mailing list