clang之AddressSanitizer

您所在的位置:网站首页 clang安装sanitizer clang之AddressSanitizer

clang之AddressSanitizer

2023-12-25 13:22| 来源: 网络整理| 查看: 265

Clang 12 documentation

Clang 12 documentation包含了一系列工具,如 AddressSanitizer、ThreadSanitizer、LeakSanitizer、LibTooling等。

clang之AddressSanitizer clang之MemorySanitizer clang之LeakSanitizer clang之UndefinedBehaviorSanitizer clang之Hardware-assisted-AddressSanitizer clang之SafeStack clang之ShadowCallStack clang之ThreadSanitizer clang之Thread-Safety-Analysis clang之DataFlowSanitizer

这部分是对clang文档 Clang 12 documentation AddressSanitizer 的翻译。仅供参考。

翻译是一件头疼的事情,不光考验英文,更考验对于原文档的理解程度,更有一些专业的技术名词和术语。。。

介绍

AddressSanitizer是一个快速探测内存错误的工具。包含了一个编译器插桩(compiler instrumentation)模块和一个运行时库(run-time library)。该工具可以检测如下类型的bug:

Out-of-bounds accesses to heap, stack and globals。堆、栈、全局变量的越界访问。 Buffer overflow:Heap、stack、Global。 Use-after-free。释放后再次使用该内存。 Use-after-return (运行时标记 ASAN_OPTIONS=detect_stack_use_after_return=1 )。函数返回后再次使用返回值的内存。 Use-after-scope (clang标记 -fsanitize-address-use-after-scope )。出作用域后再次使用变量内存。 Double-free, invalid-free。再次释放、错误的释放。 Memory-leaks(experimental)。内存泄露(试验阶段)。 Initialization order bugs。初始化顺序的错误。

通常情况下,AddressSanitizer会带来2x的性能损耗。

如何构建

使用 CMake 来构建 LLVM/Clang。

用法

简单地使用 -fsanitize=address 标记来编译链接代码即可。AddressSanitizer 的 run-time library 应该被链接到最终的可执行文件中,所以确保使用 clang(而非ld) 来执行最终的链接操作。当链接共享库的时候,AddressSanitizer runtime 库不会被链接。所以,-Wl, -z, defs 可能导致链接错误(不要跟 AddressSanitizer 一起使用)。使用 -O1 或更高的优化标记,可以获取更为合理的性能。使用 -fno-omit-frame-pointer ,可以在错误信息中提取更有意义的栈帧信息。为了获取完美的栈帧信息,可能需要禁用内联(仅使用 -O1)和跟踪调用消除(-fno-optimize-sibling-calls)(这里英文是 tail call elimination)。

% 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会在首次检测到错误的时候退出。这样设计的原因在于:

这种方案使得 AddressSanitizer 能生成更快、更少的相关代码(约减少5%)。 遇到bug就无法忽略了,必须解决。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

如果不生效,则需要使用一个单独的脚本,来独立(offline)符号化这些结果。(可以通过设置 ASAN_OPTIONS=symbolize=0 来强制禁用协同符号化(英文是online symbolization))。

% 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 命令来将文件行号(file:line)加到 AddressSanitizer 的报告中。

其他检查 检查初始化顺序

AddressSanitizer能够顺便检测动态初始化顺序的一些问题,如在一个转换单元中定义的一个全局变量,其初始化过程需要用到另一个转换单元中的全局变量。可以通过设置环境变量 ASAN_OPTIONS=check_initialization_order=1 来在runtime时开启该项检查。

注意,该选项在macOS上不支持。

检测内存泄露

若想要获取 AddressSanitizer 中检测内存泄露的更多信息,可以查看 LeakSanitizer。在Linux系统中,内存泄露检测是默认开启的,在macOS中可以使用 ASAN_OPTIONS=detect_leaks=1 来开启。而其他平台则暂时不支持。

Issue Suppression(如何剔除一些问题)

AddressSanitizer 通常不会误报问题。如果发现误报,再次检测。

剔除外部库的报告

运行时插桩(Runtime interposition)允许 AddressSanitizer 在未经重新编译的代码中查找问题。如果遇到一个外部库的问题,建议尽快将其汇报给库的维护者。

However, you can use the following suppression mechanism to unblock yourself and continue on with the testing. This suppression mechanism should only be used for suppressing issues in external code; it does not work on code recompiled with AddressSanitizer. To suppress errors in external libraries, set the ASAN_OPTIONS environment variable to point to a suppression file. You can either specify the full path to the file or the path of the file relative to the location of your executable.

ASAN_OPTIONS=suppressions=MyASan.supp

使用下边的格式来指定需要剔除的函数名或者库名。这些在错误报告中可以看到。谨记:剔除的范围越小,就能捕获到更多的bug。

interceptor_via_fun:NameOfCFunctionToSuppress interceptor_via_fun:-[ClassName objCMethodToSuppress:] interceptor_via_lib:NameOfTheLibraryToSuppress 使用 ***__has_feature(address_sanitizer)***进行额外的编译

在一些案例中,可能需要根据 AddressSanitizer 是否开启来执行不同的代码。这种场景下,使用 __has_feature 即可。

#if defined(__has_feature) # if __has_feature(address_sanitizer) // code that builds only under AddressSanitizer # endif #endif 禁用 attribute 插桩 ((no_sanitize("address")))

一些代码不应该使用 AddressSanitizer 来做相关的代码插桩。可以使用编译属性 attribute((no_sanitize("address")) (已经弃用了 synonyms no_sanitize_address 和 no_address_safety_analysis ) 来禁用指定函数的插桩。这个编译属性可能没有被其他编译器所支持,所以建议将其与 __has_feature(address_sanitizer) 结合使用。

The same attribute used on a global variable prevents AddressSanitizer from adding redzones around it and detecting out of bounds accesses.

剔除再次编译的错误(黑名单)

AddressSanitizer supports src and fun entity types in Sanitizer special case list, that can be used to suppress error reports in the specified source files or functions. Additionally, AddressSanitizer introduces global and type entity types that can be used to suppress error reports for out-of-bound access to globals with certain names and types (you may only specify class or struct types).

可以使用一个初始的分类,将特定源码文件或特定全局变量的初始化顺序的问题,从原始报告中剔除。

# 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:。如果在泄露报告的符号化堆栈信息中,匹配到了任何函数名、源码文件名、或者库名,则内存泄露会被剔除掉。更多信息查看full documentation。

限制 AddressSanitizer会消耗更多的内存。准确的内存消耗取决于申请的内存大小。(同样内存申请总量的情况下)申请的内存尺寸越小,则内存消耗越大。 AddressSanitizer会使用更多的栈内存。最多可能到3x。 在64位平台上,AddressSanitizer映射到(未来不一定)16+ Terabytes的虚拟地址空间. 这意味着一些工具如 ulimit 可能就不像通常那样生效了。 不支持可执行文件的静态链接。 支持的平台

AddressSanitizer 支持如下平台:

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) Ports to various other platforms are in progress. 当前状态

从LLVM 3.1开始,AddressSanitizer 就在支持的平台上可以使用了。测试套件集成在CMake中,可以使用 make check-asan 命令来运行。

The Windows port is functional and is used by Chrome and Firefox, but it is not as well supported as the other ports.

更多信息 google/sanitizers/wiki/AddressSanitizer


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3