asan默认可检测堆(new/malloc)、全局变量数组越界,但不捕获栈局部数组及结构体内嵌数组越界;必须启用-fsanitize=address、-fno-omit-frame-pointer和-g参数才能正常工作。

ASan 能检测哪些数组越界
ASan 默认只捕获堆内存(new/malloc)和全局变量的越界访问,对栈上局部数组(如 int a[10]; a[11] = 1;)**不报错**——这不是 bug,是编译器为控制开销做的取舍。它也不检测结构体内嵌数组的越界(如 struct { int arr[5]; } s; s.arr[8] = 1;),因为这类访问可能被优化为直接内存操作,未触发插桩边界检查。
常见可捕获场景包括:
-
new int[5]后访问arr[5]或arr[-1]→ 报heap-buffer-overflow -
std::vector::data()返回指针后越界读写 → ASan 能识别容器分配的底层堆内存 - 全局
int g_arr[3];后访问g_arr[5]→ 报global-buffer-overflow
编译时必须加的参数有哪些
漏掉任意一个关键 flag,ASan 就不会生效或报链接错误。最简可靠组合是:
g++ -fsanitize=address -fno-omit-frame-pointer -g -O1 main.cpp -o main
注意三点:
-
-fsanitize=address必须同时出现在编译和链接阶段;若用 CMake,需确保CMAKE_CXX_FLAGS和CMAKE_EXE_LINKER_FLAGS都包含它 -
-fno-omit-frame-pointer不是可选的:没有它,调用栈会大量显示???,无法定位到源码行 -
-O1是平衡点:-O0可能因栈帧合并导致漏报,-O2+可能让优化后的指令跳转掩盖真实访问位置
运行时报错怎么看懂关键信息
遇到 ERROR: AddressSanitizer: heap-buffer-overflow 时,重点盯三处:
-
READ of size 4 at 0x60200000001c thread T0→ 表明是 4 字节读操作,地址在堆上,不是空指针解引用 -
#0 0x4012aa in main /tmp/test.cpp:5→ 最顶上这行才是越界发生的位置(不是调用它的地方) -
allocated by thread T0 here:→ 紧跟其后那行显示new或malloc的位置,帮你确认分配大小
如果看到 Address 0x60200000001c is located 4 bytes inside of a 16-byte region,说明你越界了 4 字节,原始分配是 16 字节(即 4 个 int),对应下标 [4] —— 此时别只看 “inside”,要结合 size 和 offset 推算真实越界量。
栈数组越界怎么补救
ASan 对 int buf[8]; buf[10] = 1; 默认静默,想覆盖这类问题,得换策略:
- 启用 UBSan:加
-fsanitize=undefined,它会在运行时检查数组索引,但仅限编译器能推断出大小的场景(如字面量数组) - 用编译器警告兜底:
-Warray-bounds在编译期报出多数静态可判定的越界,零运行开销 - 手动加固关键路径:对运行时大小的栈数组(如
int arr[n];),配合-fstack-protector-strong提高红区保护强度,但不能保证 100% 捕获
真正难啃的是那些靠指针算术绕过容器边界的访问(如 char* p = vec.data(); p += 100;),ASan 和 UBSan 都不跟踪这种“合法指针+非法偏移”,只能靠代码审查或自定义封装(如 SafeArray)来约束。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











