所以我在一些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
答案 0 :(得分:3)
如果静态分析器是cppcheck,那么它就会发出警告,因为glibc中的一个错误已被修复:http://sources.redhat.com/bugzilla/show_bug.cgi?id=13138
答案 1 :(得分:2)
编辑,因为我错过了用静态代码分析器提供的事实
如果格式%d
与int
的大小匹配,那么溢出的内容不应该是通过指针写入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()
在遇到非数字之前不能停止阅读”。这些是相互冲突的目标。