Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

map.erase(it) causes double free when it comes from map.find in template

I am trying to run the following:

#include <algorithm>
#include <iostream>
#include <map>

template <class T>
auto first(T t){
  return t.find(0);
}

void f(bool b){
  std::map<int, int> map;
  map.insert({0, 0});
  auto it = b ? first(map) : map.find(0);
  std::cout << "About to erase" << std::endl;
  map.erase(it); // Line 15
}

int main(void){
  f(false);
  std::cout << "Exited g(false) sucessfully" << std::endl;
  f(true); // Line 21
  std::cout << "Exited g(true) sucessfully" << std::endl;
}

The function f should:

  • Initalise a map
  • Add an element to that map
  • Get an iterator to that element
  • Erase that element

However, compiling with g++ -g (g++ version 9.3.0) prints:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

About to erase
Exited g(false) sucessfully
About to erase
free(): double free detected in tcache 2
Aborted

so when it = map.find(0) everything works as expected, but when it = first(map), calling map.erase(it) generates a double free error.

Running this with gdb gives the backtrace:

(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff77ec859 in __GI_abort () at abort.c:79
#2  0x00007ffff78573ee in __libc_message (action=action@entry=do_abort, 
    fmt=fmt@entry=0x7ffff7981285 "%s\n") at ../sysdeps/posix/libc_fatal.c:155
#3  0x00007ffff785f47c in malloc_printerr (
    str=str@entry=0x7ffff79835d0 "free(): double free detected in tcache 2")
    at malloc.c:5347
#4  0x00007ffff78610ed in _int_free (av=0x7ffff79b2b80 <main_arena>, p=0x55555556d2e0, 
    have_lock=0) at malloc.c:4201
#5  0x0000555555556b62 in __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<int const, int> > >::deallocate (this=0x7fffffffe120, __p=0x55555556d2f0)
    at /usr/include/c++/9/ext/new_allocator.h:128
#6  0x00005555555569d1 in std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<int const, int> > > >::deallocate (__a=..., __p=0x55555556d2f0, __n=1)
    at /usr/include/c++/9/bits/alloc_traits.h:470
#7  0x000055555555655b in std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_put_node (this=0x7fffffffe120, __p=0x55555556d2f0)
    at /usr/include/c++/9/bits/stl_tree.h:584
#8  0x0000555555555e0c in std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_drop_node (this=0x7fffffffe120, __p=0x55555556d2f0)
    at /usr/include/c++/9/bits/stl_tree.h:651
#9  0x00005555555564c8 in std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::_M_erase_aux (this=0x7fffffffe120, __position=...) at /usr/include/c++/9/bits/stl_tree.h:2511
#10 0x0000555555555d7b in std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, std::less<int>, std::allocator<std::pair<int const, int> > >::erase[abi:cxx11](std::_Rb_tree_iterator<std::pair<int const, int> >) (this=0x7fffffffe120, 
    __position=...) at /usr/include/c++/9/bits/stl_tree.h:1220
#11 0x0000555555555955 in std::map<int, int, std::less<int>, std::allocator<std::pair<int const, int> > >::erase[abi:cxx11](std::_Rb_tree_iterator<std::pair<int const, int> >) (
    this=0x7fffffffe120, __position=...) at /usr/include/c++/9/bits/stl_map.h:1037
#12 0x000055555555544c in f (b=true) at test.cpp:15
#13 0x00005555555554f9 in main () at test.cpp:21

What is causing this problem, and how should I change the template to fix it?

>Solution :

template <class T>
auto first(T t){
  return t.find(0);
}

The first function is pass by value, so when t.find(0) is returned, the local variable t will be destroyed, and it will be initialized by a dangling iterator that points to the destroyed container.

You should pass by reference:

template <class T>
auto first(T& t){
  return t.find(0);
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading