# 利用Address Sanitizer工具检查内存访问错误
## 例子
a.c:
#include <stdio.h>
int main(void) {
// your code goes here
int a[3] = {0};
a[3] = 1;
printf("%d\n", a[3]);
return 0;
}
b.c:
#include <stdio.h>
#include <malloc.h>
int main(void) {
int *p = NULL;
p = malloc(10 * sizeof(int));
free(p);
*p = 3;
return 0;
}
## 技巧
gcc从`4.8`版本起,集成了`Address Sanitizer`工具,可以用来检查内存访问的错误(编译时指定“`-fsanitize=address`”)。以上面`a.c`程序为例:
gcc -fsanitize=address -g -o a a.c
执行`a`程序:
[root@localhost nan]# ./a
=================================================================
==539==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff3a152c9c at pc 0x4009b6 bp 0x7fff3a152c60 sp 0x7fff3a152c58
WRITE of size 4 at 0x7fff3a152c9c thread T0
#0 0x4009b5 in main /home/nan/a.c:6
#1 0x34e421ed1c in __libc_start_main (/lib64/libc.so.6+0x34e421ed1c)
#2 0x4007b8 (/home/nan/a+0x4007b8)
Address 0x7fff3a152c9c is located in stack of thread T0 at offset 44 in frame
#0 0x400907 in main /home/nan/a.c:3
This frame has 1 object(s):
[32, 44) 'a' <== Memory access at offset 44 overflows this variable
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-buffer-overflow /home/nan/a.c:6 main
Shadow bytes around the buggy address:
0x100067422540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100067422550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100067422560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100067422570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100067422580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
=>0x100067422590: f1 f1 00[04]f4 f4 f3 f3 f3 f3 00 00 00 00 00 00
0x1000674225a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000674225b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000674225c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000674225d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000674225e0: 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
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Contiguous container OOB:fc
ASan internal: fe
==539==ABORTING
可以看到,执行程序时检测出了`a`数组的越界访问(`a[3] = 1`)。
再看一下`b`程序:
gcc -fsanitize=address -g -o b b.c
执行`b`程序:
[root@localhost nan]# ./b
=================================================================
==1951==ERROR: AddressSanitizer: heap-use-after-free on address 0x60400000dfd0 at pc 0x4007f9 bp 0x7fff34277bb0 sp 0x7fff34277ba8
WRITE of size 4 at 0x60400000dfd0 thread T0
#0 0x4007f8 in main /home/nan/b.c:9
#1 0x34e421ed1c in __libc_start_main (/lib64/libc.so.6+0x34e421ed1c)
#2 0x400658 (/home/nan/b+0x400658)
0x60400000dfd0 is located 0 bytes inside of 40-byte region [0x60400000dfd0,0x60400000dff8)
freed by thread T0 here:
#0 0x7fbbb7a7d057 in __interceptor_free /opt/gcc-4.9.2/src/gcc-4.9.2/libsanitizer/asan/asan_malloc_linux.cc:62
#1 0x4007c1 in main /home/nan/b.c:8
#2 0x34e421ed1c in __libc_start_main (/lib64/libc.so.6+0x34e421ed1c)
previously allocated by thread T0 here:
#0 0x7fbbb7a7d26f in __interceptor_malloc /opt/gcc-4.9.2/src/gcc-4.9.2/libsanitizer/asan/asan_malloc_linux.cc:72
#1 0x4007b1 in main /home/nan/b.c:7
#2 0x34e421ed1c in __libc_start_main (/lib64/libc.so.6+0x34e421ed1c)
SUMMARY: AddressSanitizer: heap-use-after-free /home/nan/b.c:9 main
Shadow bytes around the buggy address:
0x0c087fff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c087fff9bf0: fa fa fa fa fa fa fa fa fa fa[fd]fd fd fd fd fa
0x0c087fff9c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Contiguous container OOB:fc
ASan internal: fe
==1951==ABORTING
执行程序时检测出了访问释放内存的错误(`*p = 3`)。
详情参见[gcc手册](https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Debugging-Options.html#index-fsanitize_003daddress-593)
## 贡献者
nanxiao
- 信息显示
- 打印gcc预定义的宏信息
- 打印gcc执行的子命令
- 打印优化级别的对应选项
- 打印彩色诊断信息
- 打印头文件搜索路径
- 打印连接库的具体路径
- 预处理
- 生成没有行号标记的预处理文件
- 在命令行中预定义宏
- 在命令行中取消宏定义
- 汇编
- 把选项传给汇编器
- 生成有详细信息的汇编文件
- 调试
- 利用Address Sanitizer工具检查内存访问错误
- 利用Thread Sanitizer工具检查数据竞争的问题
- 连接
- 把选项传给连接器
- 设置动态连接器
- 函数属性
- 禁止函数被优化掉
- 强制函数inline
- 常见错误
- error: cast from ... to ... loses precision
- all warnings being treated as errors
- gdb无法调试gcc编译的程序
- 其它
- 只做语法检查
- 保存临时文件
- 打开警告信息
- 指定语言类型
- 改变结构体成员的字节对齐