【发布时间】:2010-12-19 18:17:37
【问题描述】:
我知道普通代码有可能溢出:
字符字符串[9];
scanf("%s", 字符串).
但是scanf("%8s", string)有可能溢出吗? 8 只是一个例子。
我知道 "%8s" 就像一个分隔符,但我也注意到当我输入超过 8 个字符的字符串时,程序将因以下原因终止:
* 检测到堆栈破坏 *:./a.out 终止
======= 回溯:=========
...
显然,默认情况下,GCC 有一个检测堆栈粉碎的标志。既然这是一个堆栈粉碎,那么我的猜测是仍然有可能溢出并执行任意代码。
与破坏scanf("%s")调用者的正常溢出相反,如果scanf("%8s")可以溢出,它将在scanf函数内溢出,因此当scanf尝试返回时,获得控制。
但是 scanf 是一个需要模式切换(从用户模式切换到内核模式)的系统调用,并且在内部它会调用诸如读取到标准输入等的东西。所以不确定我们是否可以在内核模式或其他东西中溢出。
欢迎评论!!
更新>>
char string[9] 在上面的例子中被假定。 char string[8] 在下面的实际代码中。
问题实际上是关于安全 scanf("%8s") 和 GCC 因堆栈粉碎而中止之间看似矛盾的故事。
简化代码:
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;
...
}
}
}
注意:
- foo 被其他人调用。
- 虽然字符串实际上是 8 个字节 带有“%8s”的代码,我不这么认为 导致粉碎。
【问题讨论】:
-
scanf 是一个运行时库函数——不需要模式切换,因为它在用户空间中运行,除非它必须请求缓冲区填充,在这种情况下它会调用 read 或 fread。
-
如答案中多次指出的那样,添加了一个 nul 字节,因此您需要一个 9 个字符的缓冲区来接受最多 8 个字符的输入。
-
正如很多人指出的那样,您在“注 2”中的假设。是错的。该示例允许单个字节的溢出,这是 gcc 正在检测的。
-
你们是对的。我已经用一个更简单的程序对其进行了测试,但我上次尝试时它并没有崩溃。现在,当我为 string[8] 和 scanf(%8s) 输入“12345678”时,它会因为堆栈粉碎而崩溃!所以这是吸取的教训。粉碎并不一定意味着存在堆栈溢出攻击。
-
即使在这种情况下缓冲区恰好在堆栈上,但编程错误是缓冲区溢出而不是堆栈溢出。我相应地重新标记了这个问题。
标签: c scanf buffer-overflow