Skip to content

debugallocator.hh: `operator new` not well-behaved; causes test failures with LTO

dune-common 2.8.0 fails to build in Debian with link-time optimization enabled due to failures in testdebugallocator.

A backtrace from gdb shows:

Program received signal SIGFPE, Arithmetic exception.
0x0000555555556cf1 in Dune::DebugMemory::AllocationManager::allocate<char> (this=<optimized out>, n=<optimized out>) at ./dune/common/debugallocator.hh:124
124	        ai.page_ptr = mmap(NULL, ai.pages * page_size,
(gdb) bt
#0  0x0000555555556cf1 in Dune::DebugMemory::AllocationManager::allocate<char> (this=<optimized out>, 
    n=<optimized out>) at ./dune/common/debugallocator.hh:124
#1  operator new (size=size@entry=64) at ./dune/common/debugallocator.hh:322
#2  0x00007ffff7fb0e82 in __gnu_cxx::new_allocator<unsigned long>::allocate (this=<optimized out>, 
    __n=8) at /usr/include/c++/11/ext/new_allocator.h:127
#3  std::allocator_traits<std::allocator<bool*> >::allocate (__n=8, __a=<synthetic pointer>...)
    at /usr/include/c++/11/bits/alloc_traits.h:464
#4  std::_Deque_base<bool, std::allocator<bool> >::_M_allocate_map (__n=8, 
    this=0x7ffff7fc36f8 <Dune::dvverb+24>) at /usr/include/c++/11/bits/stl_deque.h:576
#5  std::_Deque_base<bool, std::allocator<bool> >::_M_initialize_map (__num_elements=0, 
    this=0x7ffff7fc36f8 <Dune::dvverb+24>) at /usr/include/c++/11/bits/stl_deque.h:625
#6  std::_Deque_base<bool, std::allocator<bool> >::_Deque_base (this=0x7ffff7fc36f8 <Dune::dvverb+24>)
    at /usr/include/c++/11/bits/stl_deque.h:439
#7  std::deque<bool, std::allocator<bool> >::deque (this=0x7ffff7fc36f8 <Dune::dvverb+24>)
    at /usr/include/c++/11/bits/stl_deque.h:834
#8  std::stack<bool, std::deque<bool, std::allocator<bool> > >::stack<std::deque<bool, std::allocator<bool> >, void> (this=<optimized out>, this=<optimized out>) at /usr/include/c++/11/bits/stl_stack.h:163
#9  0x00007ffff7fb0f42 in _sub_I_65535_0.0 ()
   from /build/dune-common-GdfBws/dune-common-2.8.0/build/lib/libdunecommon.so.2.8.0
#10 0x00007ffff7fdc0ce in ?? () from /lib64/ld-linux-x86-64.so.2
#11 0x00007ffff7fdc1b0 in ?? () from /lib64/ld-linux-x86-64.so.2
#12 0x00007ffff7fcd08a in ?? () from /lib64/ld-linux-x86-64.so.2
#13 0x0000000000000001 in ?? ()
#14 0x00007fffffffeccd in ?? ()
#15 0x0000000000000000 in ?? ()

This is during the construction of global objects, in particular some member of Dune::dvverb. It calls the custom operator new from "debugallocator.hh".

The relevant instruction is:

=> 0x0000555555556cf1 <+81>:	div    %r12

which should come from this division:

        ai.pages = (ai.capacity) / page_size + 2;

page_size is still 0 as the global variables from the "debugallocator.cc" translation unit have not been initialized when the "Dune::dvverb" object gets constructed. Note that the order in which global variables in different translation units are initialized is undefined according to the C++ standard, but the code currently requires a specific order (page_size must be initialized before anything uses the operator new).