使用Clang作为编译器 —— AddressSanitizer


本文为译文,点击 此处查看原文。

1. 介绍

AddressSanitizer是一种快速的内存错误检测器。它由编译器检测模块和运行时库组成。该工具可以检测以下类型的bug

  • 对 heap、stack 和 globals 的越界访问
  • Use-after-free
  • Use-after-return(runtime flag ASAN_OPTIONS=detect_stack_use_after_return=1)
  • Use-after-scope(clang flag -fsanitize-address-use-after-scope)
  • Double-free, invalid free
  • Memory leaks (experimental)

AddressSanitizer引入的典型减速是2倍。

2. 如何构建

使用 CMake 构建 LLVM/Clang。

3. 使用(Usage)

只需使用 -fsanitize=address flag 编译并链接程序。AddressSanitizer运行时库应该链接到最终的可执行文件,所以确保在最后的链接步骤中使用clang(而不是ld)。当链接共享库时,AddressSanitizer运行时没有被链接,因此-Wl-zdefs可能会导致链接错误(不要将其与AddressSanitizer一起使用)。要获得合理的性能,请添加-O1或更高。要在错误消息中获得更好的堆栈跟踪,请添加-fno-omit-frame-pointer。要获得完美的堆栈跟踪,您可能需要禁用内联(只使用-O1)和尾部调用消除(-fno-optimize-sibling-call)。

% 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 打印一条错误消息,并使用非零的退出码退出。AddressSanitizer在第一次检测到错误时退出。这是它的设计:

  • 这种方法允许AddressSanitizer生成更快更小的代码(两者都可以生成约5%的代码)。
  • 修复bug变得不可避免。AddressSanitizer不会产生虚假警报。一旦发生内存损坏,程序就处于不一致的状态,这可能导致混淆的结果和潜在的误导后续报告。

如果您的进程是沙箱化的,并且运行在OS X 10.10或更早版本上,则需要设置DYLD_INSERT_LIBRARIES环境变量,并将其指向ASan库,ASan库由编译器打包,用于构建可执行文件。(您可以通过搜索以asan命名的动态库来找到这个库。)如果没有设置环境变量,进程将尝试重新执行。还要记住,当将可执行文件移动到另一台机器时,还需要复制ASan库。

4. 用符号表现报告(Symbolizing the Reports)

要使AddressSanitizer将 symbolize 输出,您需要设置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

如果这对您不起作用(例如,您的过程是沙箱),您可以使用一个单独的脚本脱机来 symbolize 结果(通过设置ASAN_OPTIONS=symbolize=0可以强制禁用 online symbolization):

% 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
...

注意,在macOS上,您可能需要在二进制文件上运行dsymutil,以便在AddressSanitizer报告中包含文件file:line信息。

5. 额外的检查(Additional Checks)

5.1 初始化顺序检查(Initialization order checking)

当在一个翻译单元中定义的全局变量的初始化使用另一个翻译单元中定义的全局变量时,AddressSanitizer可以选择性地检测动态初始化顺序问题。要在运行时启用此检查,您应该设置环境变量ASAN_OPTIONS=check_initialization_order=1
注意,macOS 不支持此选项。

5.2 内存泄漏检测(Memory leak detection)

有关AddressSanitizer中的 leak detector 的更多信息,请参见 LeakSanitizer.。默认情况下,Linux上打开了泄漏检测,可以使用ASAN_OPTIONS=detect_leaks=1在macOS上启用;然而,它还没有在其他平台上得到支持。

6. 问题抑制(Issue Suppression)

6.1 抑制外部库中的报告(Suppressing Reports in External Libraries)
6.2 使用__has_feature(address_sanitizer)条件编译
6.3 使用__attribute__((no_sanitize("address")))禁用检测
6.4 抑制重新编译代码中的错误(黑名单)
6.5 抑制内存泄漏

7. 限制

8. 支持的平台

9. 当前的状态

10. 更多的信息

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值