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)
{
}

Attachment: signature.asc
Description: Digital signature

Reply via email to