AddressSanitizer 内存检测
AddressSanitizer(ASan)是 Clang/GCC 内置的内存错误检测工具,能检测堆/栈/全局变量的越界、use-after-free、内存泄漏等问题,开销约 2x。
启用 ASan
bash
# 编译时添加标志
g++ -fsanitize=address -g -O1 main.cpp -o main
clang++ -fsanitize=address -g -O1 main.cpp -o main
# 同时启用多个 Sanitizer
clang++ -fsanitize=address,undefined,leak -g -O1 main.cpp -o maincmake
# CMake 中启用
option(ENABLE_SANITIZERS "启用 Sanitizers" OFF)
if(ENABLE_SANITIZERS)
target_compile_options(myapp PRIVATE -fsanitize=address,undefined -g)
target_link_options(myapp PRIVATE -fsanitize=address,undefined)
endif()检测的错误类型
cpp
// 1. 堆越界(Heap Buffer Overflow)
void heap_overflow() {
int* arr = new int[5];
arr[5] = 1; // 越界!
delete[] arr;
}
// ASan 输出:
// ERROR: AddressSanitizer: heap-buffer-overflow on address 0x...
// WRITE of size 4 at 0x... thread T0
// #0 heap_overflow() main.cpp:3
// 2. 栈越界(Stack Buffer Overflow)
void stack_overflow() {
int arr[5];
arr[5] = 1; // 越界!
}
// 3. Use-After-Free
void use_after_free() {
int* p = new int(42);
delete p;
*p = 1; // 使用已释放内存!
}
// 4. Double Free
void double_free() {
int* p = new int(42);
delete p;
delete p; // 二次释放!
}
// 5. Use-After-Return(栈变量逃逸)
int* use_after_return() {
int local = 42;
return &local; // 返回栈变量地址!
}
// 6. 内存泄漏(需要 LeakSanitizer)
void memory_leak() {
int* p = new int(42);
// 忘记 delete
}ThreadSanitizer(数据竞争)
bash
# 检测数据竞争(不能与 ASan 同时使用)
clang++ -fsanitize=thread -g -O1 main.cpp -o maincpp
// 数据竞争示例
int counter = 0;
void increment() {
for (int i = 0; i < 100000; ++i) ++counter; // 数据竞争!
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join(); t2.join();
}
// TSan 输出:
// WARNING: ThreadSanitizer: data race
// Write of size 4 at 0x... by thread T2
// Previous write of size 4 at 0x... by thread T1UndefinedBehaviorSanitizer
bash
clang++ -fsanitize=undefined -g main.cpp -o maincpp
// 检测未定义行为
void ub_examples() {
// 有符号整数溢出
int x = INT_MAX;
int y = x + 1; // UB!
// 空指针解引用
int* p = nullptr;
*p = 1; // UB!
// 除以零
int a = 1 / 0; // UB!
// 移位越界
int b = 1 << 32; // UB!(int 是 32 位)
// 数组越界(静态数组)
int arr[5];
arr[10] = 1; // UB!
}Valgrind(更全面的内存分析)
bash
# 安装
sudo apt install valgrind
# 内存泄漏检测
valgrind --leak-check=full --show-leak-kinds=all ./myapp
# 调用图分析(性能)
valgrind --tool=callgrind ./myapp
callgrind_annotate callgrind.out.*
# 缓存分析
valgrind --tool=cachegrind ./myapp
cg_annotate cachegrind.out.*CI 集成
yaml
# .github/workflows/sanitizers.yml
name: Sanitizers
on: [push, pull_request]
jobs:
asan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 构建(ASan)
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_SANITIZERS=ON
cmake --build build
- name: 运行测试
run: ctest --test-dir build --output-on-failure
env:
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
UBSAN_OPTIONS: print_stacktrace=1:abort_on_error=1ASan 环境变量
bash
# 控制 ASan 行为
export ASAN_OPTIONS="detect_leaks=1:abort_on_error=1:print_stats=1"
# 常用选项
detect_leaks=1 # 启用内存泄漏检测
abort_on_error=1 # 发现错误时 abort(生成 core dump)
print_stats=1 # 打印统计信息
halt_on_error=0 # 发现错误后继续运行(找更多错误)
suppressions=asan.supp # 抑制特定错误关键认知
Debug 构建必须开启 ASan + UBSan,这是发现内存问题最有效的方式,开销约 2x 完全可以接受。ASan 和 TSan 不能同时使用。CI 中对所有测试运行 Sanitizers,能在上线前发现绝大多数内存问题。