AddressSanitizer 是一个快速的内存错误检测工具,它由一个编译时插桩模块和一个运行库组成。该工具可以检测以下类型的错误:
AddressSanitizer 引入的一般速度下降是两倍左右
使用 CMAKE 构建 LLVM/Clang
只需带有 -fsanitize=address
来编译链接你的程序,同时 AddressSanitizer 的运行时库链接到最终的可执行文件。因此,请确保在最终的链接时使用 clang(not ld)。当链接共享库而 AddressSanitizer 的运行时库未链接时,-Wl,-z,defs
可能会导致链接错误(不要与 AddressSanitizer 同用)。为了得到合理的性能开销,请添加 -O1
或者更高。想要在错误消息中添加更好的堆栈跟踪,启用 -fno-omit-frame-pointer
。为了获得完整的堆栈跟踪信息,可能需要禁用内联(使用 -O1)与尾递归消除(-fno-optimize-sibling-calls)
% cat example_UseAfterFree.cc
int main(int argc, char **argv) {
int *array = new int[100];
delete [] array;
return array[argc]; // BOOM
}
# Compile and link
% clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer example_UseAfterFree.cc
或者
# Compile
% clang++ -O1 -g -fsanitize=address -fno-omit-frame-pointer -c example_UseAfterFree.cc
# Link
% clang++ -g -fsanitize=address example_UseAfterFree.o
如果检测到 bug,程序将向 stderr 输出错误消息,并以非0退出码退出。AddressSanitizer 会在检测到第一个错误时退出:
如果你的进程是沙盒化的,且正在 OS X 10.10 或更低办不办上运行,需要设置 DYLD_INSERT_LIBRARIES
环境变量并将其指向与用于生成可执行文件的编译器打包的 ASAN 库(可以通过搜索 asan 的名字来查找库)。如果未设置环境变量,则该进程将尝试重新执行。另外请记住,将可执行文件移动到另一台机器时,ASAN 库也需要复制
为了让 AddressSanitizer 符号化输出,需要设置环境变量 ASAN_SYMBOLIZER_PATH
来指向 llvm-symbolizer
二进制程序(或确保 llvm-symbolizer
在 $PATH 中)
% ASAN_SYMBOLIZER_PATH=/usr/local/bin/llvm-symbolizer ./a.out
==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8
READ of size 4 at 0x7f7ddab8c084 thread T0
#0 0x403c8c in main example_UseAfterFree.cc:4
#1 0x7f7ddabcac4d in __libc_start_main ??:0
0x7f7ddab8c084 is located 4 bytes inside of 400-byte region [0x7f7ddab8c080,0x7f7ddab8c210)
freed by thread T0 here:
#0 0x404704 in operator delete[](void*) ??:0
#1 0x403c53 in main example_UseAfterFree.cc:4
#2 0x7f7ddabcac4d in __libc_start_main ??:0
previously allocated by thread T0 here:
#0 0x404544 in operator new[](unsigned long) ??:0
#1 0x403c43 in main example_UseAfterFree.cc:2
#2 0x7f7ddabcac4d in __libc_start_main ??:0
==9442== ABORTING
如果这不适用你的情况(例如你的进程已被沙盒化),可以使单独的脚本来离线标记结果(在线符号化可以通过设置禁用 ASAN_OPTIONS=symbolize=0
)
% ASAN_OPTIONS=symbolize=0 ./a.out 2> log
% projects/compiler-rt/lib/asan/scripts/asan_symbolize.py / < log | c++filt
==9442== ERROR: AddressSanitizer heap-use-after-free on address 0x7f7ddab8c084 at pc 0x403c8c bp 0x7fff87fb82d0 sp 0x7fff87fb82c8
READ of size 4 at 0x7f7ddab8c084 thread T0
#0 0x403c8c in main example_UseAfterFree.cc:4
#1 0x7f7ddabcac4d in __libc_start_main ??:0
...
请注意,在 OS X 上,可能需要在你的程序上运行 dsymutil
,在 AddressSanitizer 的报告中保存的信息是 file:line
当一个翻译单元中定义的全局变量初始化使用另一个翻译单元中定义的全局变量时,AddressSanitizer 可以选择性地检测动态初始化顺序问题
请注意,OS X 上不支持此选项
有关 AddressSanitizer 中泄漏检测器的更多信息,请参阅 LeakSanitizer 部分。在 Linux 上漏洞检测默认打开,在 OS X 上可使用 ASAN_OPTIONS=detect_leaks=1
开启。但是在其他平台上并未得到支持
AddressSanitizer 不会产生误报,如果你发现了一个误报请再进一步看看,也许并不是误报
运行时干预允许 AddressSanitizer 在未重新编译的代码中查找 bug。如果你在外部库中遇到 bug,我们建议立即将其报告给库维护者,以便得到解决。也可以使用如下方法启用抑制机制来解除这种阻塞关系,以便保持继续测试。此抑制机制只用于抑制外部代码中的问题,它不适用于 AddressSanitizer 重新编译的代码。若要消除外部库的错误,请将 ASAN_OPTIONS
环境变量设置为指向一个抑制文件。可以指定文件的完整路径,或者相对于可执行文件的路径
ASAN_OPTIONS=suppressions=MyASan.supp
使用以下格式来指定要抑制的函数或库的名字。你可以在错误报告中看到这些信息。请记住,抑制的范围越小,能捕捉到的 bug 越多
interceptor_via_fun:NameOfCFunctionToSuppress
interceptor_via_fun:-[ClassName objCMethodToSuppress:]
interceptor_via_lib:NameOfTheLibraryToSuppress
在某些情况下,根据是否启用 AddressSanitizer,可能需要执行不同的代码。 __has_feature
可以用于此目的
#if defined(__has_feature)
# if __has_feature(address_sanitizer)
// code that builds only under AddressSanitizer
# endif
#endif
某些代码不应由 AddressSanitizer 进行检测。可以使用函数属性 __attribute__ (no_sanitize ( "address"))
(已弃用同义词 no_sanitize_address 和 no_address_safety_analysis) 来禁用特定函数的检测。其他编译器可能不支持此属性, 因此建议将其与 __has_feature (address_sanitizer)
一起使用
AddressSanitizer 在特殊情况列表中支持 src
和 fun
实体类型,可以用来抑制在指定的源文件或函数的错误报告。此外,AddressSanitizer 还引入了 global
和 type
实体类型,可用于抑制对带有确定名字或类型的全局变量的 OOB 的错误报告,可以只指定类或结构体类型
可以使用 init
类别来抑制某些源文件或某些全局变量中发生的初始化顺序问题的错误报告
# Suppress error reports for code in a file or in a function:
src:bad_file.cpp
# Ignore all functions with names containing MyFooBar:
fun:*MyFooBar*
# Disable out-of-bound checks for global:
global:bad_array
# Disable out-of-bound checks for global instances of a given class ...
type:Namespace::BadClassName
# ... or a given struct. Use wildcard to deal with anonymous namespace.
type:Namespace2::*::BadStructName
# Disable initialization-order checks for globals:
global:bad_init_global=init
type:*BadInitClassSubstring*=init
src:bad/init/files/*=init
由 LeakSanitizer 生成的内存泄漏报告(如果它作为 AddressSanitizer 的一部分运行)可以被导出到一个单独的文件
LSAN_OPTIONS=suppressions=MyLSan.supp
其模式为 leak:
,如果模式与泄漏报告的符号化堆栈跟踪中的任何函数名、源文件名或库名匹配,则会抑制内存泄漏。有关详细信息,请参阅完整文档
AddressSanitizer 支持:
其他平台的接口正在开发
AddressSanitizer 在 LLVM 3.1 后支持的平台上完全可用,该测试套件已集成到 CMake 的构建中,可通过命令 make check-asan
运行
https://github.com/google/sanitizers/wiki/AddressSanitizer
原文地址