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