为何AddressSanitizer对BSS段全局缓冲区溢出检测无效?
AddressSanitizer对BSS段全局缓冲区溢出无法检测的原因解析
我来帮你拆解这个问题——这本质上是AddressSanitizer(ASan)对不同内存段的全局变量采取了差异化保护策略导致的。
先把你的测试用例整理得更清晰,方便对比差异:
测试1:未初始化的全局数组(BSS段)
#include <stdio.h> int test[16]; int main() { test[17] = -1; }
编译运行操作:
/tmp $ gcc ./main.c -o main -fsanitize=address /tmp $ ./main # 无任何报错输出
测试2:初始化的全局数组(数据段)
#include <stdio.h> int test[16] = {1}; int main() { test[17] = -1; }
编译运行操作:
/tmp $ gcc ./main.c -o main -fsanitize=address /tmp $ ./main ================================================================= ==19776==ERROR: AddressSanitizer: global-buffer-overflow on address ... Looks like global buffer ov...
核心原因:BSS段与数据段的ASan保护机制差异
- 内存段本质区别
未初始化的全局变量(比如测试1里的test[16])会被分配到BSS段,这部分内存是程序启动时由操作系统批量清零的,多个未初始化全局变量会被紧凑排列;而初始化的全局变量(测试2的test[16])会进入数据段,每个变量拥有独立的内存区域。 - ASan保护策略不同
对于数据段的全局变量,ASan会在变量前后插入专门的红色区域(Redzone)——这是标记为不可访问的内存,一旦程序读写到该区域,ASan会立即触发缓冲区溢出报错;但对于BSS段的全局变量,ASan默认不会给每个变量单独添加红色区域(主要是为了避免内存浪费,毕竟BSS段通常存在大量小变量),当你溢出test[17]时,实际是写到了BSS段相邻的其他全局变量内存或未标记的空闲BSS内存,ASan无法识别这是非法操作,因此不会报错。
如何让ASan检测BSS段的全局缓冲区溢出?
你可以添加-fno-common编译选项,这个选项会强制编译器将BSS段的全局变量当作非公共符号处理,给每个变量分配独立内存块并添加红色区域,这样就能触发溢出检测了。
修改后的编译命令:
/tmp $ gcc ./main.c -o main -fsanitize=address -fno-common /tmp $ ./main # 此时会触发和测试2类似的global-buffer-overflow报错
内容的提问来源于stack exchange,提问作者Alex Hoppus
相关产品推荐
相关产品推荐

