c++ ASan怎么用 c++ AddressSanitizer内存检查【工具】

AddressSanitizer(ASan)是Clang/GCC提供的编译期插桩内存检测工具,可快速发现缓冲区溢出、use-after-free等错误,需编译链接均加-fsanitize=address、禁用高优化并加-g,运行时报错含详细堆栈与内存信息。

AddressSanitizer(ASan)是 Clang 和 GCC 提供的内存错误检测工具,能快速发现堆栈缓冲区溢出、use-after-free、double-free、内存泄漏(需额外开启)等常见问题。它不是运行时库,而是编译期插桩 + 运行时检测,开销约 2×,但远比 Valgrind 快。

编译时启用 ASan

关键是在编译和链接阶段都加上 -fsanitize=address,并建议禁用优化(或至少不用 -O2 及以上),否则可能漏报或误报:

  • Clang:clang++ -fsanitize=address -g -O1 main.cpp -o main
  • GCC(≥4.8):g++ -fsanitize=address -g -O1 main.cpp -o main
  • 务必加 -g,否则报错时看不到源码行号
  • 避免 -O2/-O3:优化可能把检测逻辑优化掉,或导致栈变量生命周期判断不准

运行时行为与典型报错

程序运行后一旦触发非法内存访问,ASan 会立即终止进程,并打印带堆栈、内存布局、访问地址、对象起源等信息的详细报告:

  • 例如越界写:ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001c
  • 例如释放后使用:ERROR: AddressSanitizer: use-after-free on address 0x602000000030
  • 报告中会标出分配/释放位置(allocated by thread T0 here:)、访问位置、以及附近内存快照
  • 默认不检测内存泄漏;如需开启,运行时加环境变量:ASAN_OPTIONS=detect_leaks=1 ./main

常见配置与调试技巧

通过环境变量可微调 ASan 行为,适合不同场景:

  • ASAN_OPTIONS=abort_on_error=1:出错直接 abort,方便用 gdb 调试(gdb ./mainrun → 崩溃后 bt
  • ASAN_OPTIONS=disable_coredump=0:允许生成 core dump,便于事后分析
  • ASAN_OPTIONS=allocator_may_return_null=1:让 new 失败时返回 nullptr 而非抛异常(兼容旧代码)
  • 若项目含内联汇编或自定义内存管理(如对象池),可能触发误报,可用 __asan_poison_memory_region() / __asan_unpoison_memory_region() 手动标记

CMake 项目中集成 ASan

在 CMakeLists.txt 中添加条件编译支持,避免每次手动改命令:

  • 启用方式:cmake -DCMAKE_CXX_FLAGS="-fsanitize=address -g -O1" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" ..
  • 或在 CMakeLists.txt 内加:
    if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|GNU")
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -g -O1")
      set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
    endif()
  • 注意:CMake 的 add_compile_options 不影响链接器标志,必须单独设 CMAKE_EXE_LINKER_FLAGS