“ stack smashing detected ”是gcc栈保护机制触发的主动终止,因缓冲区溢出覆盖了栈中金丝雀值(canary),函数返回前校验失败而调用__stack_chk_fail中止程序。

为什么一运行就报 *** stack smashing detected ***
这不是“程序写错了”的模糊提示,而是 GCC 在运行时主动中止了你——它在栈上埋了“金丝雀”(canary)值,发现被覆盖就立刻杀进程。触发条件很明确:函数栈帧里某个缓冲区(比如 char buf[256])被写爆了,越界数据踩到了紧邻的 canary 或返回地址。90% 的情况是数组越界或 strcpy/sprintf 类函数没做长度检查。别急着关保护,先定位哪行写的越界。
用 gdb 快速定位越界位置
编译时加调试信息和基础栈保护:g++ -g -fstack-protector-strong your.cpp -o your。然后:
- 启动
gdb ./your,输入run复现崩溃 - 崩溃后立刻执行
bt,看最顶上那个函数调用栈(不是main,是它调用的某个函数) - 用
frame 0切进去,再用list查看附近代码,重点盯:-
char数组声明和后续所有下标访问(arr[i]) -
strcpy、strcat、sprintf、gets这类不带长度参数的函数调用 -
memcpy的第三个参数是否大于目标缓冲区大小
-
如果 bt 显示栈帧混乱(比如函数名全是 ??),说明破坏太严重,得结合 valgrind --tool=memcheck ./your 看内存访问错误。
strcpy 和 snprintf 的关键区别在哪
前者完全信任源字符串以 <p>前者完全信任源字符串以 <code>\0 结尾且目标空间足够;后者强制要求你指定最大写入字节数,哪怕源超长也只截断、不越界。例如:
char dst[10]; strcpy(dst, "hello world"); // ❌ 越界,dst 只有 10 字节,"hello world" 占 12 字节(含 \0) snprintf(dst, sizeof(dst), "%s", "hello world"); // ✅ 安全,最多写 9 字符 + \0
其他必须替换的危险函数:
-
gets→fgets(必须指定size) -
strcat→strncat(第三个参数是剩余空间大小,不是总长) -
scanf("%s", buf)→scanf("%9s", buf)(硬编码宽度,匹配buf[10])
为什么换台机器就崩,本地却没事
常见但容易被忽略的环境问题:
- 链接了多个版本的同一库(比如两个
libglog.so),导致 ABI 不兼容,函数调用时栈帧布局错位 - 头文件路径混乱,
#include "config.h"实际包含的是旧版头文件,结构体大小不一致,memcpy按新版 size 拷贝,实际目标 buffer 更小 - 编译器默认栈保护级别不同(GCC 10+ 默认开
-fstack-protector-strong,老版本可能只开-fstack-protector或关闭)
查法:ldd ./your 看动态库路径是否唯一;nm -C ./your | grep glog 看符号是否混杂;readelf -S ./your | grep stack 确认是否启用了栈保护段。
真正难缠的不是越界本身,而是越界后程序还能跑几秒才崩——这时 canary 没被踩,但返回地址或局部变量已被悄悄改写,行为完全不可预测。所以别依赖“它以前能跑”,只要 *** stack smashing detected *** 出现过一次,就必须按栈帧逐行审计,不能靠屏蔽日志或关保护来绕过。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











