scanf溢出(“8s”,字符串)?
我知道普通代码可能会溢出: 字符串[9]强> scanf(“%s”,字符串) 但是有可能溢出scanf(“%8s”,字符串)吗?8只是一个例子 我知道“%8s”的工作原理类似于定界,但我也注意到,当我输入长度超过8个字符的字符串时,程序将终止,原因是: *检测到堆栈崩溃*:./a.out终止 =========回溯:========= 显然有一个标志检测默认情况下由GCC打开的堆栈破坏。由于这是一个堆栈崩溃,那么我的猜测是仍然有可能溢出并执行任意代码 与破坏scanf(“%s”)调用方的正常溢出相反,如果scanf(“%8s”)可以溢出,则它将在scanf函数中溢出,以便在scanf尝试返回时获得控制权 但是scanf是一个需要模式切换(从用户模式切换到内核模式)的系统调用,在内部它将调用诸如读取stdin等内容。因此不确定我们是否可以在内核模式或其他模式下溢出 欢迎评论 更新>> 在上面的示例中,假定为字符字符串[9]。以下实数代码中的字符字符串[8] 问题实际上是关于安全scanf(“%8s”)和GCC之间由于堆栈崩溃而中止的看似矛盾的故事 简化代码:scanf溢出(“8s”,字符串)?,c,scanf,buffer-overflow,C,Scanf,Buffer Overflow,我知道普通代码可能会溢出: 字符串[9] scanf(“%s”,字符串) 但是有可能溢出scanf(“%8s”,字符串)吗?8只是一个例子 我知道“%8s”的工作原理类似于定界,但我也注意到,当我输入长度超过8个字符的字符串时,程序将终止,原因是: *检测到堆栈崩溃*:./a.out终止 =========回溯:========= 显然有一个标志检测默认情况下由GCC打开的堆栈破坏。由于这是一个堆栈崩溃,那么我的猜测是仍然有可能溢出并执行任意代码 与破坏scanf(“%s”)调用方的正常溢出相
void foo(pass some pointer) {
char input[8];
int input_number = 0;
while (1) { // looping console
printf some info;
scanf("%8s", input);
input_number = atoi(input);
if ((strlen(input) == 1) && (strncmp(input, "q", 1) == 0)) {
input_number = -1;
}
switch (input_number) {
case -1: to quit the console if input = 'q';
default: to print info that pointer refers to;
...
}
}
}
注:
如果字符串分配少于8个特许,它肯定会覆盖缓冲区,而且scanf不会附加空终止符。但只要字符串中有足够的空间容纳值,就不应该得到正确的值 如果你想让你的输入更加稳健,千万不要使用
scanf
(或者fscanf
)
您应该使用fgets
(或类似的“防止缓冲区溢出”变体),然后在此基础上使用sscanf
scanf
和fscanf
的主要问题是,如果行不是预期的格式(即,如果scanf
失败),文件指针可能会位于不确定的位置。使用fgets/sscanf
方法,可以更轻松地保证您处于行边界上,而不必使用ftell
和fseek
来移动文件
关于您关于缓冲区是否会溢出的特定查询,C标准有如下说明:
。。。相应的参数应该是一个指向字符数组初始元素的指针,该数组足够大,可以接受序列和一个将自动添加的终止空字符
因此,对于“%8s”
格式,您需要一个9个字符的数组
我怀疑你的代码中还有其他问题。使用测试程序:
#include <stdio.h>
int main(int argc, char* argv[]) {
char x1;
char a[9];
char x2;
x1 = x2 = ' ';
scanf ("%s",a);
printf ("[%c] [%s] [%c]\n",x1,a,x2);
return 0;
}
当我将同一程序更改为使用“%8s”
时,我得到(对于完全相同的输入):
见:
每个指令由以下内容之一组成…一个可选的非零十进制整数,用于指定最大字段宽度
s匹配非空白字符的字节序列。应用程序应确保相应的参数是指向字符、有符号字符或无符号字符数组的初始字节的指针,该数组的大小足以接受序列和一个自动添加的终止空字符代码
因此它不会使9字节字符串缓冲区溢出。正如ysth所指出的,数组应该能够包含字符串和终止的空字符,因此使用8字节数组(特别是如果它是在堆栈上分配的,就像在代码中一样)很可能会把它搞砸。至少需要9个字节才能不溢出。事实上,我认为scanf会在末尾加一个“\0”。C标准表示“一个终止的空字符,它将被自动添加。”paxdiabloI也引用了这句话,意思是如果你没有空字符的空间。如果你没有空字符的空间,它将把它放在那里(如果必要,覆盖堆栈的其他部分).scanf是一个运行时库函数,由于它在用户空间中运行,因此不需要模式切换,除非它必须请求缓冲区填充,在这种情况下,它将调用read或fread。如答案中多次指出的,添加了nul字节,所以你需要一个9个字符的缓冲区来接受最多8个字符的输入。正如很多人指出的,你在“注2”中的假设是错误的。这个例子允许一个字节的溢出,这是gcc正在检测的。你们说得对。我已经用一个更简单的程序测试了它,但上次我试的时候它没有崩溃。现在,当我为字符串[8]和scanf(%8s)输入“12345678”时,由于堆栈崩溃,它崩溃了!这是我们学到的教训。粉碎并不一定意味着存在堆栈溢出攻击。在这种情况下,即使缓冲区恰好位于堆栈上,编程错误也是缓冲区溢出,而不是堆栈溢出。我相应地重新标记了问题。是的,我知道。但是现在我很想知道scanf(%8s)是否和scanf有同样的问题,因为GCC告诉我仍然有堆栈崩溃发生!描述“健壮”?举几个例子?@ysth:(1)将您的输入作为行。(2) 确保获得整行(\n字符在末尾),否则错误为“行太长”。(3) 在线使用sscanf-您可以在在线上任意多次执行此操作,而无需担心底层文件。我也像您一样测试了类似的简化代码,GCC不会抱怨堆栈崩溃。。我已经发布了orig代码,但在运行时,当我输入12345678(注意实码字符字符串[8])时,会出现GCC抱怨。@Figo,您需要一个9字符数组来存储一个8字符字符串。这是因为一个8个字符的字符串由8个字符组成(显而易见)
pax> ./qq.exe
dfjdhadgha...lghjdfgjhd
[s] [dfjdhadgha...lghjdfgjhd] [ ]
6 [main] qq 4744 _cygtls::handle_exceptions: Error while dumping state
(probably corrupted stack)
Segmentation fault (core dumped)
pax> ./qq.exe
dfjdhadgha...lghjdfgjhd
[ ] [dfjdhadg] [ ]