编辑
2025-05-26
记录知识
0
请注意,本文编写于 32 天前,最后修改于 31 天前,其中某些信息可能已经过时。

目录

参考链接
asan的原理
asan的作用
影子内存的值
影子区域内存换算
运行
UseAfterFree
HeapOutOfBounds
StackOutOfBounds
GlobalOutOfBounds
UseAfterReturn
UseAfterScope
MemoryLeak
总结

本文基于asan的官方文章阅读后,对asan调试内存问题的方法做一次上手,一方面能够清楚asan的工作原理,另一方面后面遇到内存问题的时候,可以留一份记忆使用asan来定位内存问题。此文章力求浅显易懂。

参考链接

https://github.com/google/sanitizers/wiki/AddressSanitizer

asan的原理

为了检测内存问题,asan做了两方面事情

  1. 通过ld preload给代码插桩,hook住malloc/free new/delete 等底层函数
  2. 为程序的内存处建立影子内存

对于1,preload的目的是从glibc的底层api上对函数堆栈进行符号化,例如打印如下格式

#0xabcdf function_name file_name.cc:1234

对于2,影子内存的作用是监听内存是否越界访问等,其思路是:

对系统的内存,按照1/8的方式创建一个影子区域 然后在影子区域的值按照如下方式进行标记 1. 0x0 代表此8个字节均正常访问 2. 0x1-0x7 代表部分字节可正常访问,其值是 8-k 3. 负数(0xf1-0xfe,等) 代表此内存不可访问原因如下

这样,我们通过影子内存可以知道内存的错误访问,然后通过ld preload可以将错误的内存的堆栈及线程相关信息打印出来。从而定位内存问题

asan的作用

asan能够定位的问题如下:

  • Use after free (dangling pointer dereference):释放后访问(野指针)
  • Heap buffer overflow:堆溢出
  • Stack buffer overflow:栈溢出
  • Global buffer overflow:全局buffer溢出
  • Use after return:函数返回后访问
  • Use after scope:访问作用域之外的变量
  • Initialization order bugs:初始化顺序bug
  • Memory leaks:内存泄漏

影子内存的值

关于影子内存的值的解释,如下

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(asan内部访问): fe Left alloca redzone(alloca左红区): ca Right alloca redzone(alloca右红区): cb Shadow gap(间隔区域): cc

影子区域内存换算

关于出错地址到影子区域的内存换算,其公式如下

Shadow = (Mem >> 3) + offset; [0x10007fff8000, 0x7fffffffffff] HighMem [0x02008fff7000, 0x10007fff7fff] HighShadow [0x00008fff7000, 0x02008fff6fff] ShadowGap [0x00007fff8000, 0x00008fff6fff] LowShadow [0x000000000000, 0x00007fff7fff] LowMem

故python转换程序如下

import sys def asan_shadow_addr(addr): result = ((addr >> 3) + 0x0000100000000000) & 0xFFFFFFFFFFFF return hex(result) if __name__ == "__main__": addr_str = sys.argv[1] addr = int(addr_str, 0) output = asan_shadow_addr(addr) print(f"shadow_addr:{output}")

运行

我们创建一个run.sh,这样就不用每次都使用LD_PRELOAD环境变量了

# cat ./run.sh export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libasan.so.5.0.0 exec $@

下面通过测试代码来上手实验。

UseAfterFree

对于使用释放后区域,代码示例如下

int main(int argc, char **argv) { int *array = new int[100]; delete [] array; return array[argc]; // BOOM }

运行

# ./run.sh ./use_after_free ================================================================= ==64466==ERROR: AddressSanitizer: heap-use-after-free on address 0x007fa8803e44 at pc 0x0000004007fc bp 0x007fd9d45800 sp 0x007fd9d45820 READ of size 4 at 0x007fa8803e44 thread T0 #0 0x4007f8 in main /root/asan/use_after_free.cpp:4 #1 0x7fac394d8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #2 0x4006a8 (/root/asan/use_after_free+0x4006a8) 0x007fa8803e44 is located 4 bytes inside of 400-byte region [0x007fa8803e40,0x007fa8803fd0) freed by thread T0 here: #0 0x7fac611d54 in operator delete[](void*) (/usr/lib/aarch64-linux-gnu/libasan.so.5.0.0+0xefd54) #1 0x400798 in main /root/asan/use_after_free.cpp:3 #2 0x7fac394d8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #3 0x4006a8 (/root/asan/use_after_free+0x4006a8) previously allocated by thread T0 here: #0 0x7fac610f3c in operator new[](unsigned long) (/usr/lib/aarch64-linux-gnu/libasan.so.5.0.0+0xeef3c) #1 0x400780 in main /root/asan/use_after_free.cpp:2 #2 0x7fac394d8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #3 0x4006a8 (/root/asan/use_after_free+0x4006a8) SUMMARY: AddressSanitizer: heap-use-after-free /root/asan/use_after_free.cpp:4 in main Shadow bytes around the buggy address: 0x001ff5100770: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff5100780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff5100790: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff51007a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff51007b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x001ff51007c0: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd 0x001ff51007d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x001ff51007e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x001ff51007f0: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa 0x001ff5100800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff5100810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa ==64466==ABORTING

报错解析可以看到在栈区地址0x007fa8803e44读取了4字节,这里0x007fa8803e44是一个400字节的区域

READ of size 4 at 0x007fa8803e44 thread T0 0x007fa8803e44 is located 4 bytes inside of 400-byte region [0x007fa8803e40,0x007fa8803fd0)

pc的位置如下,其值是0x0000004007fc

#0 0x4007f8 in main /root/asan/use_after_free.cpp:4

内存释放的地方在测试代码第3行,如下:

freed by thread T0 here: #0 0x7fae794d54 in operator delete[](void*) (/usr/lib/aarch64-linux-gnu/libasan.so.5.0.0+0xefd54) #1 0x400798 in main /root/asan/use_after_free.cpp:3

内存申请的地方在测试代码第2行

previously allocated by thread T0 here: #0 0x7fae793f3c in operator new[](unsigned long) (/usr/lib/aarch64-linux-gnu/libasan.so.5.0.0+0xeef3c) #1 0x400780 in main /root/asan/use_after_free.cpp:2

对于影子区域内存转换如下

# python3 asan_shadow_addr.py 0x007fa8803e44 shadow_addr:0x100ff51007c8

可以看到0x100ff51007c8的值是0xfd,我们查表如下

Freed heap region: fd

返回代码中看,我们访问了数组的第1项,但访问之前,我们已经delete掉这个数组了。

delete [] array; return array[argc];

HeapOutOfBounds

代码如下

int main(int argc, char **argv) { int *array = new int[100]; array[0] = 0; int res = array[argc + 100]; // BOOM delete [] array; return res; }

运行

# ./run.sh ./heap_out_of_bounds ================================================================= ==65270==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x007fb3303fd4 at pc 0x0000004008a4 bp 0x007fedcddd30 sp 0x007fedcddd50 READ of size 4 at 0x007fb3303fd4 thread T0 #0 0x4008a0 in main /root/asan/heap_out_of_bounds.cpp:4 #1 0x7fb6e60d8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #2 0x400708 (/root/asan/heap_out_of_bounds+0x400708) 0x007fb3303fd4 is located 4 bytes to the right of 400-byte region [0x007fb3303e40,0x007fb3303fd0) allocated by thread T0 here: #0 0x7fb70dcf3c in operator new[](unsigned long) (/usr/lib/aarch64-linux-gnu/libasan.so.5.0.0+0xeef3c) #1 0x4007e0 in main /root/asan/heap_out_of_bounds.cpp:2 #2 0x7fb6e60d8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #3 0x400708 (/root/asan/heap_out_of_bounds+0x400708) SUMMARY: AddressSanitizer: heap-buffer-overflow /root/asan/heap_out_of_bounds.cpp:4 in main Shadow bytes around the buggy address: 0x001ff66607a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff66607b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff66607c0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00 0x001ff66607d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ff66607e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x001ff66607f0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa 0x001ff6660800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff6660810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff6660820: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff6660830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001ff6660840: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa

报错信息可以看到从0x007fb3303fd4读取了4字节,0x007fb3303fd4属于400字节的区域范围外+8的字节处 0x007fb3303fd0处是小括号,不包含0x007fb3303fd0

READ of size 4 at 0x007fb3303fd4 thread T0 0x007fb3303fd4 is located 4 bytes to the right of 400-byte region [0x007fb3303e40,0x007fb3303fd0)

pc的地址是0x0000004008a4,指向代码第四行的下一条指令

#0 0x4008a0 in main /root/asan/heap_out_of_bounds.cpp:4

影子区域内存转换

# python3 asan_shadow_addr.py 0x007fb3303fd4 shadow_addr:0x100ff66607fa

对于出错原因是堆左红区

Heap left redzone: fa

返回代码看,这里访问了第101项(访问0x007fa7303fd4),因为0x100ff66607fa包含8字节的影子描述,所以此问题和访问第100项问题报错(访问0x007fa7303fd0)是一致的
出错代码如下

int *array = new int[100]; int res = array[argc + 100]; // BOOM

StackOutOfBounds

栈越界的测试代码如下

int main(int argc, char **argv) { int stack_array[100]; stack_array[1] = 0; return stack_array[argc + 100]; // BOOM }

运行

==113972==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x007fd34029f4 at pc 0x000000400a94 bp 0x007fd34027c0 sp 0x007fd34027e0 READ of size 4 at 0x007fd34029f4 thread T0 #0 0x400a90 in main /root/asan/stack_of_bounds.cpp:4 #1 0x7fa46bcd8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #2 0x400858 (/root/asan/stack_of_bounds+0x400858) Address 0x007fd34029f4 is located in stack of thread T0 at offset 452 in frame #0 0x400928 in main /root/asan/stack_of_bounds.cpp:1 This frame has 1 object(s): [48, 448) 'stack_array' (line 2) <== Memory access at offset 452 overflows this variable 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-buffer-overflow /root/asan/stack_of_bounds.cpp:4 in main Shadow bytes around the buggy address: 0x001ffa6804e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa6804f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa680500: 00 00 00 00 00 00 f1 f1 f1 f1 f1 f1 00 00 00 00 0x001ffa680510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa680520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x001ffa680530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f3]f3 0x001ffa680540: f3 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 0x001ffa680550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa680560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa680570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa680580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

从报错可以了解0x007fd34029f4处读取了4字节,并且其位于frame的第452字节,其中stack_array范围是[48, 448),那么越界访问了第4-8个字节,错在stack_of_bounds.cpp的第4行。摘出错误信息如下

READ of size 4 at 0x007fd34029f4 thread T0 Address 0x007fd34029f4 is located in stack of thread T0 at offset 452 in frame [48, 448) 'stack_array' (line 2) <== Memory access at offset 452 overflows this variable SUMMARY: AddressSanitizer: stack-buffer-overflow /root/asan/stack_of_bounds.cpp:4 in main

影子区域内存转换如下

# python3 asan_shadow_addr.py 0x007fd34029f4 shadow_addr:0x100ffa68053e

可以看到其值是f3,也就是栈右红区

=>0x001ffa680530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f3]f3 Stack right redzone: f3

返回代码中看,这里访问了stack_array[101]正好是栈区的第4-8字节。 当然,如果想复现栈左红区的报错,我们访问数组的-1项即可stack_array[-1],那么错误如下

[48, 448) 'stack_array' (line 2) <== Memory access at offset 44 underflows this variable =>0x001ffc07f0f0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 f1[f1]00 00 0x001000082240: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 f9 f9 00 00 Stack left redzone: f1

GlobalOutOfBounds

测试代码如下

int global_array[100] = {-1}; int main(int argc, char **argv) { return global_array[argc + 100]; // BOOM }

运行

==115097==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000004111f4 at pc 0x000000400814 bp 0x007ff0360e80 sp 0x007ff0360ea0 READ of size 4 at 0x0000004111f4 thread T0 #0 0x400810 in main /root/asan/global_out_of_bounds.cpp:3 #1 0x7f8fecad8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #2 0x4006d8 (/root/asan/global_out_of_bounds+0x4006d8) 0x0000004111f4 is located 4 bytes to the right of global variable 'global_array' defined in 'global_out_of_bounds.cpp:1:5' (0x411060) of size 400 SUMMARY: AddressSanitizer: global-buffer-overflow /root/asan/global_out_of_bounds.cpp:3 in main Shadow bytes around the buggy address: 0x0010000821e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0010000821f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001000082200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001000082210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001000082220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x001000082230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9 0x001000082240: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 f9 f9 00 00 0x001000082250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001000082260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001000082270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001000082280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

通过上面报错信息可以看出

  1. 在0x0000004111f4处读取了4字节
  2. 0x0000004111f4是全局变量global_array(0x411060)的右边
  3. global_array的size是400,那么global_array的范围就是[0x411060, 0x4111f0)

影子区域内存转换如下

# python3 asan_shadow_addr.py 0x0000004111f4 shadow_addr:0x10000008223e =>0x001000082230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9 Global redzone: f9

对应代码验证确实是访问了全局变量之外的第4-8字节。

return global_array[1 + 100];

这里再多提一个细节,并不是所有的内存问题asan都能检测,我们留意影子区域布局如下

=>0x001000082230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[f9]f9 0x001000082240: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 f9 f9 00 00

可以看到在0x001000082244-0x001000082245两个影子字节处是0,那么意味着如果全局变量溢出的问题在这对应的16字节,asan将不起作用,我们修改代码验证。
我们计算当前错误离为0的影子字节个数为5,那么需要额外越界 5*8/4+1.此时原来的越界数组值是101,我们加上刚刚计算的11,得出112。那么代码如下

return global_array[112];

编译验证,发现asan检测不出这个溢出问题了。

UseAfterReturn

测试代码如下

int *ptr; __attribute__((noinline)) void FunctionThatEscapesLocalObject() { int local[100]; ptr = &local[0]; } int main(int argc, char **argv) { FunctionThatEscapesLocalObject(); return ptr[argc]; }

值得注意的是:

  • 默认asan的参数没办法这类问题,需要额外开启一个环境变量ASAN_OPTIONS=detect_stack_use_after_return=1

此时运行

# ASAN_OPTIONS=detect_stack_use_after_return=1 ./run.sh ./use_after_return ==115526==ERROR: AddressSanitizer: stack-use-after-return on address 0x007f9e1e6034 at pc 0x000000400b24 bp 0x007fee1358f0 sp 0x007fee135910 READ of size 4 at 0x007f9e1e6034 thread T0 #0 0x400b20 in main /root/asan/use_after_return.cpp:18 #1 0x7fa192cd8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #2 0x4008b8 (/root/asan/use_after_return+0x4008b8) Address 0x007f9e1e6034 is located in stack of thread T0 at offset 52 in frame #0 0x400988 in FunctionThatEscapesLocalObject() /root/asan/use_after_return.cpp:11 This frame has 1 object(s): [48, 448) 'local' (line 12) <== Memory access at offset 52 is inside this variable 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-return /root/asan/use_after_return.cpp:18 in main Shadow bytes around the buggy address: 0x001ff3c3cbb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ff3c3cbc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ff3c3cbd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ff3c3cbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ff3c3cbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x001ff3c3cc00: f5 f5 f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5 f5 f5 f5 0x001ff3c3cc10: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 0x001ff3c3cc20: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 0x001ff3c3cc30: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 0x001ff3c3cc40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ff3c3cc50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

通过上面报错信息可以看出

  1. 报错地址0x007f9e1e6034在FunctionThatEscapesLocalObject的栈帧中
  2. 是栈帧的第52个偏移
  3. 因为local是从第48个字节偏移的栈开始,local是int型数组,所以访问的是local[1]

影子区域内存转换如下

# python3 asan_shadow_addr.py 0x007f9e1e6034 shadow_addr:0x100ff3c3cc06 0x001ff3c3cbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x001ff3c3cc00: f5 f5 f5 f5 f5 f5[f5]f5 f5 f5 f5 f5 f5 f5 f5 f5 Stack after return: f5

根据上面的信息,有个细节值得注意的是

  • 我们留意影子下毒区域,如果在小于0x001ff3c3cc00,也就是访问栈变量大于下毒范围了,那么asan就不起作用了

下面演示asan无法检测的问题,我们修改代码,根据推算我们需要访问推前 -6*8/4+1的地址,那么下毒就没有意义了。

  • 这里6是影子字节格式,也就是出错往前有5个0xf5的下毒
  • 这里8是每个影子字节占实际8个字节
  • 这里4是int的大小
  • 这里1是为了越过下毒区域,也就是取到0x0x001ff3c3cbff占的8字节的后面4字节

那么修改代码如下

return ptr[-13];

此时运行可以发现asan检测不到问题

UseAfterScope

测试代码如下

volatile int *p = 0; int main() { { int x = 0; p = &x; } *p = 5; return 0; }

运行

# ./run.sh ./use_after_scope ================================================================= ==117169==ERROR: AddressSanitizer: stack-use-after-scope on address 0x007fd2d42760 at pc 0x000000400a88 bp 0x007fd2d426f0 sp 0x007fd2d42710 WRITE of size 4 at 0x007fd2d42760 thread T0 #0 0x400a84 in main /root/asan/use_after_scope.cpp:15 #1 0x7fb7b40d8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #2 0x400858 (/root/asan/use_after_scope+0x400858) Address 0x007fd2d42760 is located in stack of thread T0 at offset 32 in frame #0 0x400928 in main /root/asan/use_after_scope.cpp:10 This frame has 1 object(s): [32, 36) 'x' (line 12) <== Memory access at offset 32 is inside this variable 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 /root/asan/use_after_scope.cpp:15 in main Shadow bytes around the buggy address: 0x001ffa5a8490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a84a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a84b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a84c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a84d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x001ffa5a84e0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[f8]f3 f3 f3 0x001ffa5a84f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a8500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a8510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a8520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x001ffa5a8530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

报错信息如下

  1. 报错地址是0x007fd2d42760
  2. 它是x的地址

那么就是在x的作用域之外访问了x

影子区域内存转换如下

# python3 asan_shadow_addr.py 0x007fd2d42760 shadow_addr:0x100ffa5a84ec =>0x001ffa5a84e0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[f8]f3 f3 f3 Stack use after scope: f8

这里f8的位置的0-4个字节就是对应实际内存的x的位置

MemoryLeak

#include <stdlib.h> void *p; int main() { p = malloc(7); p = 0; // The memory is leaked here. return 0; }

上面代码分配了7个字节给p,然后对指针p赋予空指针,这样代码就存在7字节的内存泄漏,运行如下

# ./run.sh ./memory-leak ================================================================= ==118406==ERROR: LeakSanitizer: detected memory leaks Direct leak of 7 byte(s) in 1 object(s) allocated from: #0 0x7f8ee4025c in __interceptor_malloc (/usr/lib/aarch64-linux-gnu/libasan.so.5.0.0+0xed25c) #1 0x400798 in main /root/asan/memory-leak.cpp:6 #2 0x7f8ebc5d8c in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x20d8c) #3 0x4006c8 (/root/asan/memory-leak+0x4006c8) SUMMARY: AddressSanitizer: 7 byte(s) leaked in 1 allocation(s).

这个堆栈浅显易懂,就不解释了。

总结

本文根据官方的文档,将几种asan定位内存问题进行了上手实验,能够加深对asan工具的使用细节的理解。但故意遗漏了关于全局对象构造顺序相关的问题讨论,这块问题后面会专门提,就不在此篇幅解释了。