ASAN,全称 AddressSanitizer,可以用来检测内存问题,例如缓冲区溢出或对悬空指针的非法访问等。
根据谷歌的工程师介绍 ASAN 已经在 chromium 项目上检测出了300多个潜在的未知bug,而且在使用 ASAN 作为内存错误检测工具对程序性能损耗也是及其可观的。
根据检测结果显示可能导致性能降低2倍左右,比Valgrind(官方给的数据大概是降低10-50倍)快了一个数量级。而且相比于Valgrind只能检查到堆内存的越界访问和悬空指针的访问,ASAN 不仅可以检测到堆内存的越界和悬空指针的访问,还能检测到栈和全局对象的越界访问。
这也是 ASAN 在众多内存检测工具的比较上出类拔萃的重要原因,基本上现在 C/C++ 项目都会使用ASAN来保证产品质量,尤其是大项目中更为需要。
一、版本及安装
链接出错:
$ gcc -g asan.c -fsanitize=address -fno-omit-frame-pointer
/usr/bin/ld: cannot find /usr/lib64/libasan.so.0.0.0
collect2: error: ld returned 1 exit status
gcc版本:
- gcc 4.8.5 : 只有Asan,即只能检测内存越界。
- gcc 4.9.2 : 有Asan和Lsan两种,可以用asan来做越界检测,用lsan做内存泄露检测。(建议使用, gcc的安装跟gcc4.8.5一样,详看tensorflow 配置centos6环境)
- gcc 7.2 : Asan中集成了LSan。(建议使用, gcc的安装跟gcc4.8.5一样,详看tensorflow 配置centos6环境),意思就是只用asan就可以啦。
CentOS 安装:
sudo yum install libasan
Ubuntu 安装:
sudo apt-get install libasan0
二、编译选项
sanitizers项目,包含了 ASAN、LSAN、MSAN、TSAN等内存、线程错误的检测工具。
可检测的问题:
- 内存泄漏
- 堆栈和全局内存越界访问
- free后继续使用
- 局部内存被外层使用
- Initialization order bugs
工具分类:
-
ASAN: 内存错误检测工具,全称 AddressSanitizer,可以用来检测内存问题,例如缓冲区溢出或对悬空指针的非法访问等。在编译命令中添加-fsanitize=address启用
-
LSAN: 内存泄漏检测工具,已经集成到 ASAN 中,可以通过设置环境变量ASAN_OPTIONS=detect_leaks=0来关闭ASAN上的LSAN,也可以使用-fsanitize=leak编译选项代替-fsanitize=address来关闭ASAN的内存错误检测,只开启内存泄漏检查。
-
MSAN: 对程序中未初始化内存读取的检测工具,可以在编译命令中添加-fsanitize=memory -fPIE -pie启用,还可以添加-fsanitize-memory-track-origins选项来追溯到创建内存的位置
-
TSAN: 对线程间数据竞争的检测工具,在编译命令中添加-fsanitize=thread启用 其中ASAN就是我们今天要介绍的重头戏。
三、原理
github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm
四、错误类型
- (heap) use after free 释放后使用
- heap buffer overflow 堆缓存访问溢出
- stack buffer overflow 栈缓存访问溢出
- global buffer overflow 全局缓冲访问溢出
- use after return
- use after scope
- initializations order bugs
- memory leaks 内存泄露 LeakSanitizer: detected memory leaks
五、使用测试
asan.c
#include <stdio.h>
#include <stdlib.h>
int funtest(void)
{
char *p = (char *)malloc(10);
p[10] = 99; //越界
return 0;
}
int main(int argc, char *argv[])
{
funtest();
return 0;
}
- 用-fsanitize=address选项编译和链接程序
- 用-fno-omit-frame-pointer编译,以得到更容易理解stack trace
编译执行:
$ gcc -g asan.c -fsanitize=address -fno-omit-frame-pointer
$ ./a.out
=================================================================
==11540== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60040000dffa at pc 0x4006ef bp 0x7ffea8345f70 sp 0x7ffea8345f60
WRITE of size 1 at 0x60040000dffa thread T0
#0 0x4006ee (/home/test/asan/a.out+0x4006ee)
#1 0x40070c (/home/test/asan/a.out+0x40070c)
#2 0x7fb843b2c3d4 (/usr/lib64/libc-2.17.so+0x223d4)
#3 0x4005d8 (/home/test/asan/a.out+0x4005d8)
0x60040000dffa is located 0 bytes to the right of 10-byte region [0x60040000dff0,0x60040000dffa)
allocated by thread T0 here:
#0 0x7fb843eecef9 (/usr/lib64/libasan.so.0.0.0+0x15ef9)
#1 0x4006ae (/home/test/asan/a.out+0x4006ae)
#2 0x40070c (/home/test/asan/a.out+0x40070c)
#3 0x7fb843b2c3d4 (/usr/lib64/libc-2.17.so+0x223d4)
Shadow bytes around the buggy address:
0x0c00ffff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c00ffff9bf0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 00[02]
0x0c00ffff9c00:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c00ffff9c40: 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 righ 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
ASan internal: fe
==11540== ABORTING