启用ASan需编译和链接均加-fsanitize=address -g,Clang推荐-O1;报错含三段位置信息构成证据链;可配ASAN_OPTIONS调控行为;Linux建议-static-libasan避免依赖问题。

编译时加 -fsanitize=address -g 就能启用 ASan
ASan 不是独立工具,而是 Clang/GCC 编译器内置的运行时检测插桩机制。只要在编译和链接阶段都带上 -fsanitize=address,生成的可执行文件就会在每次内存访问时插入检查逻辑。必须同时加 -g,否则报错时无法显示源码行号——你只会看到 0x7ffeefbff428 这种地址,毫无调试价值。
常见错误:只在编译(g++ -c -fsanitize=address)加,但链接时漏了。结果是链接失败,报一堆 __asan_report_load4 未定义引用。正确做法是:所有 g++ 或 clang++ 命令(包括单文件编译、多文件链接、甚至用 cmake 构建)都得带 -fsanitize=address -g。
- Clang 推荐用
clang++ -fsanitize=address -g -O1:ASan 在-O1下最稳定,-O2及以上可能因优化导致误报或漏报 - GCC 7.1+ 支持 ASan,但对 C++ 模板和 STL 的检测不如 Clang 精确,尤其涉及
std::vector迭代器越界时 - 不要混用不同编译器生成的目标文件:Clang 编译的
.o不能用 GCC 链接,反之亦然,否则 ASan 运行时库冲突
运行时报 ERROR: AddressSanitizer: heap-use-after-free 就是 UAF
ASan 报错格式高度结构化,第一行就明确缺陷类型。比如 heap-use-after-free 表示堆上对象已被 delete,但后续代码仍通过野指针访问;stack-buffer-overflow 是栈数组越界;global-buffer-overflow 是全局数组下标过大。
关键看报错里的三段信息:触发访问的源码位置(main.cpp:42)、被释放/越界的内存块分配位置(malloc 或 new 行号)、以及释放位置(UAF 场景下必有这一行)。这三者时间顺序构成完整证据链。
立即学习“C++免费学习笔记(深入)”;
- 如果报错里出现
freed by thread T0 here:,说明释放和使用发生在同一线程,问题较直接 - 若写成
previously allocated by thread T1 here:,就是典型的多线程竞争,需配合-fsanitize=thread进一步分析 - ASan 默认关闭对栈回收后访问的检测(如函数返回后访问局部变量地址),需额外加
-fsanitize-address-use-after-scope才能捕获这类问题
ASAN_OPTIONS 环境变量控制行为细节
ASan 运行时行为可通过环境变量微调,不用改代码、不重新编译。最常用的是:
-
ASAN_OPTIONS=detect_stack_use_after_return=1:启用栈上 UAF 检测(默认关闭,因为性能开销大) -
ASAN_OPTIONS=abort_on_error=1:一出错立刻abort(),避免程序继续运行掩盖更多问题 -
ASAN_OPTIONS=disable_coredump=0:允许生成 core dump,方便用gdb ./a.out core回溯上下文 -
ASAN_OPTIONS=allocator_may_return_null=1:让 ASan 内存分配器在失败时返回nullptr而非崩溃,适配某些异常处理逻辑
注意:ASAN_OPTIONS 必须在运行前设置,且只对当前进程生效。在脚本中写成 ASAN_OPTIONS="abort_on_error=1" ./a.out 是有效的;但写成 export ASAN_OPTIONS=... 后再运行,也有效——只要没被子进程覆盖。
静态链接 ASan 运行时库避免部署依赖问题
默认情况下,ASan 生成的可执行文件动态链接 libasan.so。这意味着目标机器必须安装对应版本的 sanitizer 运行时库,否则报 error while loading shared libraries: libasan.so.5: cannot open shared object file。
解决方法是静态链接:加 -static-libasan 参数。Clang 和 GCC 都支持。这样生成的二进制自带 ASan 检查逻辑,拷到任意 Linux x86_64 机器都能直接跑(内核版本 ≥ 2.6.32 即可)。
- 静态链接后体积增大 5–10MB,但换来的是零依赖部署
- 不要同时用
-static和-static-libasan:前者会把 libc 也静态链接,可能导致getaddrinfo等函数行为异常 - macOS 上
-static-libasan不可用,ASan 强制动态链接;Linux 是主要使用场景
ASan 不是银弹——它只能捕获实际执行到的非法访问。没走到的分支、条件编译掉的代码、或者被编译器优化掉的访问,它都看不到。覆盖率低的测试用例跑 ASan,大概率漏问题。


















