You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CSAPP实验:汇编层面缓冲区溢出为何跳过4字节?

关于CSAPP练习3.46栈溢出跳过4字节的疑问解答

背景信息

我正在阅读《深入理解计算机系统》(CSAPP),完成练习3.46时,需使用GDB测试一段存在漏洞的C代码以探究其内部机制。给出的漏洞代码及自行添加的main函数如下:

/** vulnerable code from the task **/
char *get_line()
{
    char buf[4];
    char *result;
    gets(buf);
    result = malloc(strlen(buf));
    strcpy(result, buf);
    return result;
}

/** main function that I added **/
int main(void)
{
    get_line();
    return 0;
}

编译时使用-fno-stack-protector和-g选项关闭栈保护并保留调试信息,同时通过命令sudo sysctl -w kernel.randomize_va_space=0关闭地址空间随机化以便观察。

已获取main和get_line的反汇编结果,在gets(buf)处设置断点,记录了调用gets前的栈状态,以及输入超长字符串0123456789012345678901234触发溢出后的栈状态。

疑问

为何溢出不从$rsp开始,而是跳过了4字节?

解答

这是由函数调用的栈帧布局和编译器的栈对齐规则导致的(以下基于常见的x86-64 SysV ABI环境分析,32位环境逻辑类似):

  • 首先,get_line函数的局部变量包含char buf[4](占4字节)和char *result(x86-64下占8字节)。根据SysV ABI规范,函数栈帧需要满足16字节对齐要求。当进入get_line函数时,栈指针%rsp会被调整以满足对齐要求,编译器会在buf和%rsp指向的栈顶之间插入4字节的填充空间,保证整体栈帧的对齐。
  • 当gets写入超长数据时,会先填满buf的4字节,接着覆盖这段预留的4字节填充空间,之后才会开始覆盖result变量、返回地址等栈上的关键数据,这就造成了“溢出跳过4字节”的视觉效果。
  • 如果是32位编译环境,原因类似:编译器为了局部变量的布局或栈帧结构(比如保存基址指针%ebp),在buf和栈顶之间预留了4字节间隔,溢出时需要先填满这段间隔才会触及栈上的其他内容。

核心原因是编译器的栈对齐规则或栈帧布局策略,在buf数组与栈顶(%rsp指向位置)之间插入了4字节填充,因此溢出时会先消耗这段填充,再继续覆盖栈上其他区域。

内容的提问来源于stack exchange,提问作者Leyiang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 05:00:26