【发布时间】:2018-02-12 14:58:34
【问题描述】:
所以,今天我在运行一些使用 Address Sanitizer 构建的代码时,偶然发现了一个奇怪的 stack-use-after-scope 错误。 我有这个简化的例子:
#include <functional>
class k
{
public: operator int(){return 5;}
};
const int& n(const int& a)
{
return a;
}
int main()
{
k l;
return std::bind(n, l)();
}
ASAN 抱怨最后一行代码:
==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
#0 0x400a00 (/root/tstb.exe+0x400a00)
#1 0x7f97ce699730 in __libc_start_main (/lib64/libc.so.6+0x20730)
#2 0x400a99 (/root/tstb.exe+0x400a99)
Address 0x7ffeab375210 is located in stack of thread T0 at offset 288 in frame
#0 0x40080f (/root/tstb.exe+0x40080f)
This frame has 6 object(s):
[32, 33) '<unknown>'
[96, 97) '<unknown>'
[160, 161) '<unknown>'
[224, 225) '<unknown>'
[288, 292) '<unknown>' <== Memory access at offset 288 is inside this variable
[352, 368) '<unknown>'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope (/root/tstb.exe+0x400a00)
Shadow bytes around the buggy address:
0x1000556669f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100055666a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100055666a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
0x100055666a20: f1 f1 f8 f2 f2 f2 f2 f2 f2 f2 f8 f2 f2 f2 f2 f2
0x100055666a30: f2 f2 f8 f2 f2 f2 f2 f2 f2 f2 f8 f2 f2 f2 f2 f2
=>0x100055666a40: f2 f2[f8]f2 f2 f2 f2 f2 f2 f2 00 00 f2 f2 f3 f3
0x100055666a50: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100055666a60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100055666a70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100055666a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100055666a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==27575==ABORTING
如果我理解正确,它表示我们正在访问一个堆栈变量,因为它已经超出了范围。
查看未检测和未优化的反汇编,我确实看到它发生在实例化 __invoke_impl 内部:
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.
在调用k::operator int() 之后,它会将返回值放入堆栈并将其地址传递给n(),后者立即返回它,然后从__invoke_impl 本身返回它(一直到main 的返回)。
所以,这里看起来像 ASAN,我们真的有一个堆栈使用后范围访问。
问题是:我的代码有什么问题?
我尝试使用 gcc、clang 和 icc 构建它,它们都产生类似的汇编器输出。
【问题讨论】:
-
我无法重现此内容,但可能是 UB。您是否尝试过不将
n作为参考,看看您是否仍然获得stack-use-after-scope? -
这有什么奇怪的?您的
bind(...)()表达式返回对int的常量引用。int应该住在哪里?