Sanitizer
asan,即Address Sanitizer,是一个适用于c/c++的动态内存错误检测器,它由一个编译器检测模块(LLVM pass)和一个替换malloc函数的运行时库组成,在性能及检测内存错误方面都优于Valgrind。
释放后使用(use-after-free)、多次释放(double-free)、缓冲区溢出(buffer overflows)和下溢(underflows)
在生产中不要使用ASAN,实测下来比release的代码会慢2-5倍。实际情况看看下当前Leetcode下C++和java的执行速度对比
如何使用和编译选项
按照推荐,官方wiki给的是
1
-fsanitize=address -O1 -fno-omit-frame-pointer -g
clang官方wiki告诉你,3.1这种上古版本用-faddress-sanitizer
,但应该没人用3.1的clang吧。
支持的平台
Linux i386/x86_64 (tested on Ubuntu 12.04)
macOS 10.7 - 10.11 (i386/x86_64)
iOS Simulator
Android ARM
NetBSD i386/x86_64
FreeBSD i386/x86_64 (tested on FreeBSD 11-current)
Windows 8.1+ (i386/x86_64)
额外随便看看
编译器插桩模块(a compiler instrumentation module,现在是一个 LLVM 的 pass 了),还有一个运行时的库用来替换 malloc 函数。
插桩模块主要用在栈内存上,而运行时库主要用在堆内存上。
运行时库会使用 __asan_malloc
来替换掉所有对 malloc 的调用。
在 malloc 函数所分配的字节周围,插入雷区,对于 free 释放掉的内存进行投毒,从而跟踪 malloc / free 行为。
1
2
3
4
5
6
7
8
9
void *__asan_malloc(size_t sz) {
void *rz = malloc(RED_SZ); // 上接雷区
Poison(rz, RED_SZ);
void *addr = malloc(sz); // 真正分配的内存
UnPoison(addr, sz);
rz = malloc(RED_SZ); // 下接雷区
Poison(rz, RED_SZ);
return addr; // 返回分配的内存首地址
}
局限
ASan无法覆盖到未初始化的内存,对于未初始化的内存,进行读取的行为同样危险,这时候就需要 MSan 出马了。
UndefinedBehaviorSanitizer (UBSan)
UBSan 主要就是检查未定义行为(undefined behavior),包括有符号整型溢出、空指针的使用、除 0 操作、越界读取等。
UBSan 在编译时对可疑操作进行插桩,以捕获程序运行时的未定义行为,如果检查到是 UB 行为,则会调用 __ubsan_handle_
函数进行处理,输出报错信息
ThreadSanitizer (TSan)
TSan 用来检查数据争用(data race)、死锁(deadlock) 的。
当多个线程同时操作同一个变量的时候,而至少一个的操作是写操作时,就会发生数据争用。
当多个线程因争夺资源而造成的一种互相等待的想象,若无外力介入,它们都无法继续推进,就是死锁。
TSan 需要所有代码都以 -fsanitize=thread 编译参数进行编译,不然某些代码可能会导致误判。