分类: C/C++
2011-12-16 13:53:20
以前,我知道scanf很危险,使用不当,会造成非法内存访问,程序崩溃。比如:
1) 使用%s,没限定输入长度或长度不对,导致读取的字符串超出缓冲区大小。
2) 使用的格式和实际数据类型不相符;
3) 格式符个数和实际参数个数不相符;
现在,我才知道,我所知道的scanf危险,并不是它的全部。我一直以为,使用scanf读取整数、浮点数还是安全的,没想到其实也未必。比如:
#include
int main()
{
int a = 0;
scanf("%d", &a);
return 0;
}
这段代码很普通吧,有谁知道这段代码其实也可能出现程序崩溃?至于你知不知道,反正我是不知道。
空口白话,不足为信,大家回头自己编译一下上述这段代码,然后用这条命令测试一下:
perl -e 'print "5"x2100000' | ./a.out
在我机器上的测试结果:
[zbc@haifen ~]$ perl -e 'print "5"x2100000' | ./a.out
Segmentation fault (core dumped)
GDB看到的程序堆栈:
(gdb) bt
#0 0x00657d83 in _IO_vfscanf_internal () from /lib/libc.so.6
#1 0x00660e1b in scanf () from /lib/libc.so.6
#2 0x080483a9 in main () at test_i.c:5
怎么办?其实改起来并不难。在每个格式符中都带上长度限定就可以了。比如上述scanf("%d", &a)改为scanf(“%50d”, &a),崩溃现象就消失了。
可以看出,并不是scanf往a里面写时造成了溢出,而应该是scanf内部缓冲区溢出。
PS:
这个问题,是我使用cppcheck时,cppcheck报告出来的一个错误。一开始我还以为有点夸张了,自己一验证,才知道原来真是这样的。
测试平台:
[zbc@haifen ~]$ uname -a
Linux haifen 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 athlon i386 GNU/Linux
[zbc@haifen ~]$ gcc -v
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,java,f95,ada --enable-java-awt=gtk --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --host=i386-redhat-linux
Thread model: posix
gcc version 4.0.0 20050519 (Red Hat 4.0.0-8)