该工具由编译器检测模块和替换malloc函数的一个运行库组成
1.1 ASAN介绍
ASAN,也即地址消毒技术,通过编译插桩,能够发现此堆/栈/全局变量读写溢出,内存泄露等问题,并将信息直接打印到日志中。(内存泄露检测功能已支持BMC的arm32位平台)。
ASAN地址消毒与valgrind工具的比较:
悬空指针
堆缓冲区溢出
堆栈缓冲区溢出
全局缓冲区溢出
use after free
初始化顺序错误
内存泄漏
ASAN地址消毒的优点:
1、 不用修改模块代码,直接编译插桩。
2、 问题检测的准确率高,日志中打印的信息很全面。
3、 使用方便,和BMC直接调试APP的方法很相似。
ASAN的运行条件为gcc版本 > 4.8,版本越高,可支持的ASAN编译参数越多,功能越强大,
4.2 ASan检测原理
ASan分两部分。一部分是在编译时会通过插入的检测代码(即编译时插桩)。第二部分是运行库,它存在于libasan.so动态库中,运行时,会对GLIBC中如下表一所示的29个函数进行替换以便检测内存错误(即运行时插桩)[3]。
通过对ASan源代码asan_interceptors.cc的函数InitializeAsanInterceptors分析,可以看出,ASan会防护如下GLIBC库28个函数。
表一 ASan防护函数列表
序号
分类
函数名
1
内存管理函数
malloc
2
free
3
mem*函数
memcmp
4
memcpy
5
memmove
6
str*函数
strcat
7
strchr
8
strcpy
9
strlen
10
wcslen
11
strncat
12
strncpy
13
strdup
14
strnlen
15
类型转换函数
atoi
16
strtol
17
atol
18
atoll
19
strtoll
20
signal,
jump相关函数
longjmp
21
sigaction
22
signal
23
siglongjmp
24
其他函数
index
25
pthread_create
26
atexit
27
throw
28
fork
以malloc 和free为例。通过gdb单步调试,发现malloc会被__interceptor_malloc代替。提示信息如下。
__interceptor_malloc (size=248) at ../../.././libsanitizer/asan/asan_malloc_linux.cc:38
free会被__interceptor_free代替,__interceptor_free会进一步调用Deallocate。
ASan对缓冲区溢出防护的的基本步骤如下:
1. 通过在被保护的栈、全局变量、堆周围建立标记为中毒状态(Poisnoned)的red-zones;
以栈缓冲区溢出检测为例,如下代码展示了red-zone的建立。
ASan使用影子内存来确定内存了访问是否安全。此外,ASan使用更加高效的映射机制和更加紧凑的影子编码,可以检测栈、全局变量和堆的错误,比AddrCheck工具速度要快。
ASan使能后,缓冲区的每一次访问之前,均会添加检测代码。
6 Address Sanitizer相关的GCC编译选项汇总
以GCC-5.1.0为例,除了-fsanitize=address外,还有一些其他的检测功能,详情如下。
1
-fsanitize=address
用户态内存错误检测,可以被环境变量ASAN_OPTIONS控制具体行为
2
-fsanitize=kernel-address
内核态内存错误检测器
3
-fsanitize=thread
使能ThreadSanitizer,快速数据竞争检测。
4
-fsanitize=leak
使能LeakSanitizer,内存泄露检测,作用于链接阶段。只有当-fsanitize=address和-fsanitize=thread均不使能时才有效。没有-fsanitize=address功能全面,只做内存泄露检测,但是速度比-fsanitize=address要快。
内存泄露检查-fsanitize=leak只有在GCC版本≥4.9时才有效。
5
-fsanitize=undefined
运行时快速未定义行为检测器。包括如下6~22共17个子选项。
6
-fsanitize=shift
移位操作符的移位大小超过了位宽或者小于零,或者左边是负值。 对于有符号数移位, 检查C中的有符号溢出,在C++中检查无符号溢出。
7
-fsanitize=integer-divide-by-zero
整数除零。
8
-fsanitize=unreachable
如果控制流到达 __builtin_unreachable.
9
-fsanitize=vla-bound
可变长数组边界值非正。
10
-fsanitize=null
使用一个空指针或者创建一个空引用
11
-fsanitize=return
仅C++有效,对函数返回值进行检查,定义了返回值为非空的函数如果未返回有效值将会报错。
12
-fsanitize=signed-integer-overflow
有符号整数溢出, 包含所有通过 -ftrapv 添加的检查, 并且检查有符号除法溢出 (INT_MIN / -1)。
13
-fsanitize=bounds
数组索引越界, 以防数组边界可以静态检测。
14
-fsanitize=alignment
使用一个未对齐的指针或者引用。
15
-fsanitize=object-size
尝试使用优化器可以探测到不属于访问对象的字节。 对象的大小使用 __builtin_object_size 检测, 并且结果可能会探测到多个问题在高层次的优化。
16
-fsanitize=float-divide-by-zero
浮点除零。不能被-fsanitize=undefined使能。
17
-fsanitize=float-cast-overflow
浮点到整形转换的检查,不能被-fsanitize=undefined使能。
18
-fsanitize=nonnull-attribute
对使用__attribute__ nonnull限定参数非空的函数进行检查。
19
-fsanitize=returns-nonnull-attribute
对使用__attribute__((returns_nonnull))限定返回为非空指针的函数进行检测。
20
-fsanitize=bool
加载一个既不是真也不是假的bool值。
21
-fsanitize=enum
加载一个枚举类型的值,但是值不在那个枚举类型范围内。
22
-fsanitize=vptr
使用一个vptr预示着具有错误动态类型的对象,或者它的生命长度还未开始或者已经结束。与 -fno-rtti 兼容。
23
-fno-sanitize=all
禁止之前所有的子选项,-fsanitize=all是不能使用的,因某些子选项是不兼容的
24
-fasan-shadow-offset=number
自定义AddressSanitizer检查中的shadow偏移。
25
-fsanitize-recover[=opts]
使用逗号分隔的列表控制错误恢复模式。如:-fsanitize-recover=undefined,float-cast-overflow,float-divide-by-zero
-fno-sanitize-recover等同于
-fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero
26
-fsanitize-undefined-trap-on-error
控制编译器用using __builtin_trap替代libubsan报告未定义行为.