x86-64下struct_t大小为16,为何fun(4)(5)可输出fun(6)却栈溢出?
问题分析:数组越界与栈溢出检测的行为差异
测试代码
#include <stdio.h> typedef struct { int a[2]; double d; } struct_t; double fun(int i) { volatile struct_t s; s.d = 3.14; s.a[i] = 1073741824; /* Possibly out of bounds */ return s.d; } int main(void) { int size = 6; for (int i = 0; i <= size; i++) printf("%.10lf\n", fun(i)); return 0; }
运行输出
3.1400000000 3.1400000000 3.1399998665 2.0000006104 3.1400000000 3.1400000000 *** stack smashing detected ***: terminated Aborted
原因解析
首先明确x86-64架构下struct_t的内存布局:
- 数组
a[2]占8字节(每个int为4字节),double d占8字节,因结构体对齐要求,整个结构体大小为16字节。
再看fun函数的栈帧结构(编译器默认开启栈保护-fstack-protector):
栈上会依次分配:struct_t s(16字节)、栈保护金丝雀(canary,x86-64下为8字节)、栈基址(rbp)、返回地址等内容。
逐个分析i的取值影响:
- i=0、1:
a[i]是数组合法索引,不会越界修改结构体其他成员,s.d保持3.14,输出正常。 - i=2:
a[2]越界,刚好覆盖d的低4字节(a数组末尾后紧接d的起始位置),破坏了double类型的部分二进制表示,导致输出值偏离3.14。 - i=3:
a[3]覆盖d的高4字节,完全改变了d的二进制值,输出结果大幅变化。 - i=4、5:
a[4]、a[5]修改的是struct_t s结构体之后的栈内存区域,但还未触及栈保护的金丝雀值,函数返回前的金丝雀校验不会触发异常,因此程序能正常输出。 - i=6:
a[6]的写入位置刚好覆盖了栈保护金丝雀的值,函数返回前编译器插入的校验代码会发现金丝雀被篡改,直接触发栈溢出检测错误并终止程序。
内容的提问来源于stack exchange,提问作者douyu
相关产品推荐
相关产品推荐

