Linux 应用程序调试技术/堆栈损坏
外观
堆栈损坏很难诊断。幸运的是,gcc 4.x 可以检测代码以检查堆栈损坏
- -fstack-protector 为包含“alloca”或具有 (signed 或 unsigned) char 数组且大小 > 8 (SSP_BUFFER_SIZE) 的函数添加堆栈保护
- -fstack-protector-strong 为更多函数添加堆栈保护,见下文
- -fstack-protector-all 为所有函数添加堆栈保护
gcc 将添加保护变量和代码以检查函数退出时的缓冲区溢出。一个简单的例子
/* Compile with: gcc -ggdb -fstack-protector-all stacktest.c */
#include <stdio.h>
#include <string.h>
void bar(char* str)
{
char buf[4];
strcpy(buf, str);
}
void foo()
{
printf("It survived!");
}
int main(void)
{
bar("Longer than 4.");
foo();
return 0;
}
运行时,程序将转储核心
$ ./a.out
*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)
Core was generated by `./a.out'.
Program terminated with signal 6, Aborted.
#0 0x0000003684030265 in raise () from /lib64/libc.so.6
(gdb) bt full
#0 0x0000003684030265 in raise () from /lib64/libc.so.6
No symbol table info available.
#1 0x0000003684031d10 in abort () from /lib64/libc.so.6
No symbol table info available.
#2 0x000000368406a84b in __libc_message () from /lib64/libc.so.6
No symbol table info available.
#3 0x00000036840e8ebf in __stack_chk_fail () from /lib64/libc.so.6
No symbol table info available.
#4 0x0000000000400584 in bar (str=0x400715 "Longer than 4.") at stacktest.c:10
buf = "Long"
#5 0x00000000004005e3 in main () at stacktest.c:19
No locals.
由谷歌添加到 gcc。
优势 - 在牺牲少量安全性(对于使用 -fstack-protector-all 的场景)的同时获得很大的性能提升
背景 - 有时堆栈保护过于简单,而堆栈保护所有则过度杀伤,例如,为了构建我们的核心系统之一,我们强制在所有编译命令中添加“-fstack-protector-all”,这会导致原子和 arm 上的巨大性能损失(由于在函数序言和结语上的额外堆栈保护/检查指令)。系统安全团队认为使用“-fstack-protector”不够安全(仅“保护”<2% 的函数)。“-fstack-protector-strong”在“-fstack-protector”和“-fstack-protector-all”之间取得平衡。
为函数添加检查
- 如果任何局部变量的地址作为赋值的 RHS(右值)的一部分被获取
- 或者如果任何局部变量的地址作为函数参数的一部分被获取。
- 或者如果它包含一个数组,无论数组类型或长度如何
- 或者如果它包含一个结构体/联合体,其中包含一个数组,无论数组类型或长度如何。
- 或者如果函数包含寄存器局部变量
请参阅 http://gcc.gnu.org/ml/gcc-patches/2012-06/msg00974.html
从 gcc 4.9 及更高版本开始,您可以使用 ubsan 净化器 进行边界检查。