scanf%d在大输入时的段错误

时间:2011-07-02 02:25:37

标签: c scanf

所以我在一些c代码上运行了一些静态代码分析器,让我感到惊讶的一件事就是警告:

int val;
scanf("%d", &val);

表示对于足够大的输入,这可能导致段错误。当然这确实可以实现。现在修复很简单(指定一些宽度;毕竟我们知道有效整数最多可能有多少个位置,具体取决于架构)但我想知道的是为什么这是首先发生的以及为什么这不是被认为是libc中的一个错误(还有一个简单的解决方法)?

现在我假设首先出现这种行为的原因是我缺席了吗?

编辑:好的,因为这个问题似乎没那么明确,更多解释: 没有代码分析器一般不会警告scanf,而是关于scanf读取没有指定宽度的数字。

所以这是一个最小的工作示例:

#include <stdlib.h>
#include <stdio.h>

int main() {
    int val;
    scanf("%d", &val);
    printf("Number not large enough.\n");
    return 0;
}

我们可以通过发送一个巨大的数字(使用例如Python)来获得段错误:

import subprocess
cmd = "./test"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, shell=True)
p.communicate("9"*50000000000000)
# program will segfault, if not make number larger

3 个答案:

答案 0 :(得分:3)

如果静态分析器是cppcheck,那么它就会发出警告,因为glibc中的一个错误已被修复:http://sources.redhat.com/bugzilla/show_bug.cgi?id=13138

答案 1 :(得分:2)

编辑,因为我错过了用静态代码分析器提供的事实

如果格式%dint的大小匹配,那么溢出的内容不应该是通过指针写入val的内容,因为它应该始终为int。尝试将指针传递给long int并查看分析器是否仍然发出警告。尝试将%d更改为%ld,保留long int指针,并查看是否再次发出警告。

我认为标准应该说一些%d,它需要的类型。也许分析师担心某些系统int可能比%d的意思更短?这听起来很奇怪。


运行用gcc编译的例子(我有python 2.6.6)我获得了

Traceback (most recent call last):
  File "./feed.py", line 4, in <module>
    p.communicate("9"*50000000000000)
OverflowError: cannot fit 'long' into an index-sized integer
Number not large enough.

然后我尝试运行它:

perl -e 'print "1"x6000000000000000;' |./test

并修改C部分以写

printf("%d Number not large enough.\n", val);

我获得输出

5513204 Number not large enough.

每次运行时数字都会发生变化......从不发生段错误...... GNU scanf实现是安全的......虽然结果数字是错误的......

答案 2 :(得分:1)

处理整数的第一步是隔离数字序列。如果该序列比预期的长,它可能会溢出固定长度的缓冲区,从而导致分段错误。

你可以用双打达到类似的效果。推到极端,你可以写1后跟一千个零,指数为-1000(净值为1)。实际上,几年前我在测试时,Solaris处理了1000位数字;它有点超过1024,它遇到了麻烦。

因此,有一个QoI元素 - 实施质量。还有一个元素是“遵循C标准,scanf()在遇到非数字之前不能停止阅读”。这些是相互冲突的目标。