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

为何向容量128的缓冲区发送127个字符会触发Segmentation fault?

缓冲区容量128,输入127字符却触发段错误的原因

问题代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BANNER 
  "Welcome to " LEVELNAME ", brought to you by https://exploit.education"

char *gets(char *);

void start_level() {
  char buffer[128];
  gets(buffer);
}

int main(int argc, char **argv) {
  printf("%s\n", BANNER);
  start_level();
}

测试现象

输入126个字符时程序正常退出:

user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print("A"*126)'  | ./stack-five 
Welcome to phoenix/stack-five, brought to you by https://exploit.education
user@phoenix-amd64:

输入127个字符时触发段错误:

user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print("A"*127)'  | ./stack-five 
Welcome to phoenix/stack-five, brought to you by https://exploit.education
Segmentation fault

疑问

缓冲区容量为128,输入127个字符加上NULL终止符应该刚好填满缓冲区,为何会触发崩溃?


原因分析

核心问题出在自定义gets函数的行为以及Pythonprint的输出特性:

  1. Pythonprint的额外输出字符:
    Python的print函数默认会在输出末尾添加一个换行符(\n)。所以print("A"*127)实际输出的是127个A加上1个换行符,总共有128个字符。

  2. 自定义gets与标准库行为不同:
    代码中显式声明了char *gets(char *);,说明这不是标准库的gets函数。标准gets会丢弃换行符,仅将读取的字符存入缓冲区后追加NULL终止符;但这个练习中的自定义gets会将换行符也存入缓冲区,之后再追加NULL终止符。

  3. 溢出破坏栈帧触发崩溃:

    • 输入126个A时,实际输入是126个A+换行符,共127个字符,加上NULL终止符后总长度为128,刚好填满buffer[128],不会溢出到栈帧的其他区域(如RBP栈基址、返回地址),程序正常返回。
    • 输入127个A时,实际输入是127个A+换行符,共128个字符,加上NULL终止符后总长度为129,超出缓冲区1字节。这多出来的1字节会覆盖栈帧中的RBP(栈基址)的低字节,导致start_level函数返回时,栈帧恢复逻辑出错,最终触发段错误。

64位系统中,栈帧从高到低的布局为:返回地址 → RBP栈基址 → 缓冲区。当RBP被覆盖后,执行leave指令恢复栈帧时会使用错误的RBP值,导致rsp指向非法区域,ret指令读取错误的返回地址,直接触发段错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 04:17:10