I have a class that manages a container. My insert-a-value function returns an object whose destructor removes the inserted value. The idea is that I can register a value with the container, save the resulting Registration object, then just let that object go out of scope to remove the registered value. I've reduced the problematic code to the following:
#include <iostream>
#include <list>
#include <vector>
#include <memory>
#include <string>
#include <unordered_map>
class Directory {
public:
struct Registration {
Registration(Directory& dir, const std::string& key)
: dir_{dir}, key_{key}
{}
~Registration() { dir_.del(key_); }
Directory& dir_;
const std::string& key_;
};
using registration_t = std::unique_ptr<Registration>;
registration_t put(std::string key, std::string val) {
map_[key] = val;
return std::make_unique<Registration>(*this, key);
}
void del(const std::string& key) {
map_.erase(key);
}
private:
std::unordered_map<std::string,std::string> map_;
};
int main(int argc, char* argv[])
{
Directory directory_;
std::list<typename Directory::registration_t> registrations_;
registrations_.push_back(directory_.put("asdf", "djdjdjdjd"));
for ( const auto& i: registrations_ )
std::cout << i->key_ << std::endl;
}
When I run this with AddressSanitizer, it reports the following:
=================================================================
==153498==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffcb33622e0 at pc 0x7fb29b24a2fd bp 0x7ffcb3362140 sp 0x7ffcb33618e8
READ of size 4 at 0x7ffcb33622e0 thread T0
#0 0x7fb29b24a2fc (/lib64/libasan.so.5+0x6a2fc)
#1 0x7fb29b116082 in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (/lib64/libstdc++.so.6+0x136082)
#2 0x402826 in main /home/john/asdf/test.cpp:43
#3 0x7fb29accf1a2 in __libc_start_main ../csu/libc-start.c:308
#4 0x4023dd in _start (/home/john/asdf/a.out+0x4023dd)
Address 0x7ffcb33622e0 is located in stack of thread T0 at offset 240 in frame
#0 0x4024a5 in main /home/john/asdf/test.cpp:37
This frame has 9 object(s):
[32, 33) '<unknown>'
[48, 49) '<unknown>'
[64, 72) '<unknown>'
[96, 104) '__for_begin' (line 42)
[128, 136) '__for_end' (line 42)
[160, 184) 'registrations_' (line 39)
[224, 256) '<unknown>' <== Memory access at offset 240 is inside this variable
[288, 320) '<unknown>'
[352, 408) 'directory_' (line 38)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope (/lib64/libasan.so.5+0x6a2fc)
Shadow bytes around the buggy address:
0x100016664400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100016664410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100016664420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100016664430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
0x100016664440: f1 f1 f8 f2 f8 f2 f8 f2 f2 f2 00 f2 f2 f2 00 f2
=>0x100016664450: f2 f2 00 00 00 f2 f2 f2 f2 f2 f8 f8[f8]f8 f2 f2
0x100016664460: f2 f2 f8 f8 f8 f8 f2 f2 f2 f2 00 00 00 00 00 00
0x100016664470: 00 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00
0x100016664480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100016664490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000166644a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
It does not seem to matter if directory_ and registrations_ are local to main() or global. I've tried it with both registrations_ as both a vector and list, and with Directory::registration_t as both a shared_ptr and a unique_ptr.
I'm sure I must be missing something, but I've been staring at this for a while now and can't figure out what it is.