strange stack trace with address sanitizer

88 Views Asked by At

Trying to debug a memory corruption with address sanitizer. It detects error, read after free. Read gives me a meaningful stack trace which makes sense, but then for the place were memory was allegedly freed i get weird one:

    #0 0xf793ba08 in operator delete(void*) ../../../../src/libsanitizer/asan/asan_new_delete.cc:165
ing<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >*, unsigned int) /usr/include/c++/9/ext/new_allocator.h:128
    #2 0xa0217ef in std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> > > >::deallocate(std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> > >&, std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >*, unsigned int) /usr/include/c++/9/bits/alloc_traits.h:469
    #3 0xa0217ef in std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> > >::_M_put_node(std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >*) /usr/include/c++/9/bits/stl_tree.h:584
    #4 0xa0217ef in std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> > >::_M_drop_node(std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >*) /usr/include/c++/9/bits/stl_tree.h:651
    #5 0xa0217ef in std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> > >::_M_erase(std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >*) /usr/include/c++/9/bits/stl_tree.h:1920
    #6 0xa0217ef in std::_Rb_tree<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats>, std::_Select1st<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> >, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> > >::~_Rb_tree() /usr/include/c++/9/bits/stl_tree.h:1000
    #7 0xa0217ef in std::map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, task::my_task_stats, std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, task::my_task_stats> > >::~map() /usr/include/c++/9/bits/stl_map.h:300
    #8 0xf6e46f05  (/lib/i386-linux-gnu/libc.so.6+0x33f05)

What do I make of the destructor being called straight from libc? What makes it stranger is that this map that is being deleted is a static field in a class. Does this mean that the error happened when process was terminating? How would i figure out why it is terminating?

1

There are 1 best solutions below

2
Employed Russian On

a followup question -- how do I prevent multi-threaded programs from crashing like this on exit?

By not making any global C++ objects with non-trivial destructors.

ChatGPT advises me to use a snigleton pointer instead and initialize it using std::once_flag.

That is one valid solution. Often you don't need the std::once_flag at all, e.g.

std::map<int, int>& GetGlobalMap()
{
  // new is guaranteed to execute only once.
  static auto *the_map = new std::map<int, int>();
  return *the_map;
}

I think this is a valid advice. Of course this will register as a leak on shutdown

No, it will not. Any leak detector worth using makes a distinction between "leaked" (you've lost the pointer and couldn't free / delete even if you wanted to) and "still allocated at exit" (you could free, but haven't bothered to).