天天看点

使用Clang作为编译器 —— AddressSanitizer

AddressSanitizer(未完成)

      • 1. 介绍
      • 2. 如何构建
      • 3. 使用(Usage)
      • 4. 用符号表现报告(Symbolizing the Reports)
      • 5. 额外的检查(Additional Checks)
          • 5.1 初始化顺序检查(Initialization order checking)
          • 5.2 内存泄漏检测(Memory leak detection)
      • 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. 更多的信息

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

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

-z

defs

可能会导致链接错误(不要将其与

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. 更多的信息