为什么char name [1]可以包含多个字符?

时间:2015-08-01 09:50:56

标签: c gcc segmentation-fault buffer

当我遇到这种情况时,我正在对一个主题进行一些研究。 假设以下C代码:

#include <stdio.h>
int main() {
char name[1];
scanf("%s",name);
printf("Hi %s",name);
return 0;
}

我已使用-fno-stack-protector编译并使用长于1的输入进行测试,例如John,&amp;令我惊讶的是,它有效! 当输入长于1时,不应该抛出分段错误 最终它以Alexander作为输入(9)打破,但它适用于少于9的任何东西 为什么输入的时间长于名称数组长度? P.S:我使用Ubuntu(64位),gcc版本4.8.4(Ubuntu 4.8.4-2ubuntu1~14.04)&amp; CLion作为IDE。

2 个答案:

答案 0 :(得分:7)

这是未定义的行为。您的程序有一个缓冲区溢出,因为它只分配一个字符,这足以存储空的以空字符结尾的字符串。

但是,缓冲区附近的内存尚未分配给您的程序。 scanf将您的输入放入该内存,因为它不知道您的字符串缓冲区有多长。当一个预先确定的字节序列放入你的字符串中时,这是一个很大的危险和无数黑客攻击的来源,希望覆盖一些重要元素,并最终获得控制权。

这就是为什么在没有指定大小的情况下使用%s是危险的。您需要始终为%s添加适当的大小限制,否则您的程序将面临缓冲区溢出的危险。

char name[120];
scanf("%119s",name);

此程序是安全的,因为即使恶意用户输入超过120个字符,scanf也会忽略超过第119个字符的所有内容,如%119s格式所述。

答案 1 :(得分:1)

存储输入的变量的大小和类型与scanf无关。

scanf只传递一个地址(指针)来存放从用户那里获得的输入。

聪明的编译器现在警告您传递给scanf的格式字符串是否与参数类型不匹配,但原则上您甚至可以将name声明为整数:

int name;

它会很好地保存输入字符串,最多三个字符(第四个是字符串结束,即零),假设int的大小是32位,即4个字节

它工作的事实纯粹是运气不好,因为输入数据在scanf存储时会超过为其分配的缓冲区的末尾(name)。

注意:即使只有一个字符的输入字符串,也只能为字符串分配一个字符。您始终需要考虑用于终止它们的EOS。因此,name至少应声明为char name[2];