以下是该程序的源代码
#include <stdio.h>
% filename: test.c
int main(){
int local = 0;
char buff[7];
printf("Password: ");
gets(buff);
if (local)
printf("Buff: %s, local:%d\n", buff, local);
return 0;
}
我使用&#34; gcc test.c -fno-stack-protector -o test&#34;编译这个文件,当我运行该文件时,为了使溢出,我需要输入至少13个字符。我认为是因为我只向缓冲区声明了7个字节,这意味着当用户输入至少8个字符时,就会发生溢出。但似乎不是这种情况,为什么?
答案 0 :(得分:1)
堆栈布局取决于实现。
无法保证local
对象将在buff
对象之后或之前,或者两者都是连续的。
在您的情况下,可能在buff
对象之后插入5个字节(1 + 4)的填充。这种填充是非常常见的,并且出于性能原因通常由编译器插入。填充的大小可能因编译器,编译器版本,编译器选项甚至不同的源代码而异。
要更好地了解布局,只需打印buff
和local
个对象的地址:
printf("%p %p\n", (void *) buff, (void *) local);
答案 1 :(得分:0)
gets(buff);
C中没有默认的绑定检查。最好的程序会崩溃,通常会得到未分配的内存,所以它运行正常。甚至有时你已经使用了已经使用过的内存,并且在你遇到问题之前可能没有意识到这一点。
答案 2 :(得分:0)
无法保证堆叠中的项目将被紧密地分配在一起,事实上我很确定它们甚至不能保证以相同的顺序放置。
如果您想了解为什么它没有像您期望的那样失败,那么最好的办法是查看汇编程序输出,例如gcc -S
。
对于它的价值,CygWin下的gcc 4.8.3
在八个字符中失败,正如人们所期望的那样。 以7个字符溢出但是,因为溢出的字符是NUL终止符,所以仍然将local
保留为零。
底线,未定义的行为就是这样。对于开发人员来说,大多数烦人的功能是它有时会起作用,这意味着我们有时会错过它,直到代码投入生产。如果UB以某种方式绑定到连接到开发人员私人部分的电极,我保证会有更少的错误: - )