这篇文章上次修改于 902 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
1 ASAN 简介
AddressSanitizer(内存除毒器简称ASAN)早先是LLVM中的特性,后被加入GCC 4.8,在GCC 4.9后加入对ARM平台的支持。因此GCC 4.8以上版本使用ASAN时不需要安装第三方库,通过在编译时指定编译CFLAGS即可打开开关 ( 例如:-fsanitizer=address);可以诊断内存越界,非法访问,内存泄漏,double free等常见内存问题,原理与valgrind不同(valgrind利用位图模拟内存的分配策略,asan则利用额外分配的shadow memory以及redzone等插桩内存对内存进行检测),效率比valgrind高好几倍(降低2倍左右,valgrind则10倍左右),也可以检测出valgrind无法检测出的内存错误;
Address Sanitizer可以用来检测如下内存使用错误:
- 内存释放后又被使用
- 内存重复释放
- 释放未申请的内存
- 使用栈内存作为函数返回值
- 使用了超出作用域的栈内存
- 内存越界访问
2 ASAN 原理
代码中所有的内存访问操作都被编译器转换为如下形式:
Before:
*address = ...; // or: ... = *address;
After:
if (IsPoisoned(address)) {
ReportError(address, kAccessSize, kIsWrite);
}
*address = ...; // or: ... = *address;
详见:
- https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm
- AddressSanitizer: A Fast Address Sanity Checker
3 ASAN 用法示例
项目结构:
example
├── ascan
│ ├── ascan.cc
│ ├── BUILD
│ └── example.cc
├── .bazelrc # 编译参数,如 ascan 编译参数
├── WORKSPACE # 项目依赖,内容可为空
example.cc:内存泄露示例,分配了内存但是没有释放
#include <iostream>
int main(int argc, char** argv) {
int *p = new int(5);
return 0;
}
BUILD:
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
cc_binary(
name = "example",
srcs = ["example.cc"],
deps = [],
)
.bazelrc: 编译参数
# bazel build --config asan
build:asan --strip=never
build:asan --copt="-fsanitize=address" # 开启内存越界检测,包括内存泄露检测
build:asan --copt="-fno-omit-frame-pointer" # 不省略函数帧数据,主要指栈顶,栈底寄存器,便于分辨帧的开始结尾
build:asan --copt="-g"
build:asan --linkopt="-fsanitize=address"
编译参数举例:
- -fsanitize=address:开启内存越界检测,包括内存泄露检测
- -fno-omit-frame-pointer:不省略函数帧数据,主要指栈顶,栈底寄存器,便于分辨帧的开始结尾
- -fsanitize-recover=address:检测到错误之后继续运行程序,默认生效,如果没有生效的话,可进一步设置ASAN_OPTIONS=halt_on_error=0;该功能需要升级到 gcc6 才支持。
结果:
$ bazel build --config=asan ascan:example
$ export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/home/asan.log
$ ./bazel-bin/ascan/example
=================================================================
==10296==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7fc2a70aa592 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99592)
#1 0x400bae in main ascan/example.cc:4
#2 0x7fc2a657a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).
可以看到给出了内存泄漏的错误提示,并指出了泄漏的位置是 ascan/example.cc:4
其它示例见:https://docs.microsoft.com/en-us/cpp/sanitizers/asan-error-examples?view=msvc-160
运行参数举例:
- halt_on_error=0:检测内存错误后继续运行,否则程序会终止
- detect_leaks=1:进行内存泄露检测,默认进行
- malloc_context_size=15:内存错误发生时,显示的调用栈层数为15
- log_path=/home/asan.log:内存检查问题日志存放文件路径。
没有评论