# 利用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編譯的程序
- 其它
- 只做語法檢查
- 保存臨時文件
- 打開警告信息
- 指定語言類型
- 改變結構體成員的字節對齊