So, today I ran some code created using Address Sanitizer, and came across a strange error using the stack to use. I have a simplified example:
#include <functional> class k { public: operator int(){return 5;} }; const int& n(const int& a) { return a; } int main() { kl; return std::bind(n, l)(); }
ASAN complains about the last line of code:
==27575==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffeab375210 at pc 0x000000400a01 bp 0x7ffeab3750e0 sp 0x7ffeab3750d8 READ of size 4 at 0x7ffeab375210 thread T0
If I understand correctly, he says that we get access to the stack variable after it has already gone out of scope. If you look at uninstrumented and unoptimized disassembly, I really see that this happens inside the __invoke_impl instance:
Dump of assembler code for function std::__invoke_impl<int const&, int const& (*&)(int const&), k&>(std::__invoke_other, int const& (*&)(int const&), k&): 0x0000000000400847 <+0>: push %rbp 0x0000000000400848 <+1>: mov %rsp,%rbp 0x000000000040084b <+4>: push %rbx 0x000000000040084c <+5>: sub $0x28,%rsp 0x0000000000400850 <+9>: mov %rdi,-0x28(%rbp) 0x0000000000400854 <+13>: mov %rsi,-0x30(%rbp) 0x0000000000400858 <+17>: mov -0x28(%rbp),%rax 0x000000000040085c <+21>: mov %rax,%rdi 0x000000000040085f <+24>: callq 0x4007a2 <std::forward<int const& (*&)(int const&)>(std::remove_reference<int const& (*&)(int const&)>::type&)> 0x0000000000400864 <+29>: mov (%rax),%rbx 0x0000000000400867 <+32>: mov -0x30(%rbp),%rax 0x000000000040086b <+36>: mov %rax,%rdi 0x000000000040086e <+39>: callq 0x4005c4 <std::forward<k&>(std::remove_reference<k&>::type&)> 0x0000000000400873 <+44>: mov %rax,%rdi 0x0000000000400876 <+47>: callq 0x40056a <k::operator int()> 0x000000000040087b <+52>: mov %eax,-0x14(%rbp) 0x000000000040087e <+55>: lea -0x14(%rbp),%rax 0x0000000000400882 <+59>: mov %rax,%rdi 0x0000000000400885 <+62>: callq *%rbx => 0x0000000000400887 <+64>: add $0x28,%rsp 0x000000000040088b <+68>: pop %rbx 0x000000000040088c <+69>: pop %rbp 0x000000000040088d <+70>: retq End of assembler dump.
After calling k::operator int() it pushes the return value onto the stack and passes its address to n() , which immediately returns it, and then returns from __invoke_impl itself (and reaches the main return).
So, it looks like ASAN right here, and we really have access to the use stack after access.
Question: what is wrong with my code?
I tried to build it using gcc, clang and icc, and they all produce similar assembler outputs.