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

Time:12-15

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:

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?

CodePudding user response:

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);
}
  • Related