5

来自man gets

永远不要使用gets()。因为在事先不知道数据的情况下不可能知道gets()会读取多少个字符,并且因为gets()会继续存储超过缓冲区末尾的字符,所以使用起来非常危险。它已被用来破坏计算机安全。请改用 fgets()。

几乎在我看到scanf的所有地方都以应该有相同问题的方式使用(缓冲区溢出/缓冲区溢出scanf("%s",string):. 这种情况下存在这个问题吗?为什么手册页中没有关于它的引用scanf?为什么 gcc 在编译时不发出警告-Wall

ps:我知道有一种方法可以在格式字符串中指定字符串的最大长度scanf

char str[10];
scanf("%9s",str);

编辑:我不是要确定前面的代码是否正确。我的问题是:如果scanf("%s",string)总是错误的,为什么没有警告并且手册页中没有任何关于它的内容?

4

5 回答 5

5

答案很简单,没有人在 GCC 中编写代码来产生该警告。

正如您所指出的,针对特定情况"%s"(没有字段宽度)的警告是非常合适的。

但是,请记住,这仅适用于scanf()vscanf()fscanf()的情况vfscanf()。此格式说明符可以与sscanf()and完全安全vsscanf(),因此在这种情况下不应发出警告。这意味着您不能简单地将其添加到现有的“scanf-style-format-string”分析代码中;您必须将其拆分为“fscanf-style-format-string”和“sscanf-style-format-string”选项。

我敢肯定,如果您为最新版本的 GCC 制作补丁,它很有可能被接受(当然,您也需要为 glibc 头文件提交补丁)。

于 2010-06-05T03:45:41.740 回答
4

使用gets()永远不安全。 scanf()正如您在问题中所说,可以安全使用。但是,确定您是否安全地使用它对于编译器来说是一个更难解决的问题(例如,如果您scanf()在一个函数中调用一个函数,其中您将缓冲区和字符数作为参数传递,它将无法告诉); 在这种情况下,它必须假设您知道自己在做什么。

于 2010-06-04T20:29:47.277 回答
3

当编译器查看 的格式化字符串时scanf,它会看到一个字符串!这是假设在运行时未输入格式化字符串。如果在编译时输入,一些编译器(如 GCC)有一些额外的功能来分析格式化字符串。这个额外的功能并不全面,因为在某些情况下需要运行时开销,这对于像 C 这样的语言来说是不可以的。例如,在这种情况下,您能否在不插入一些额外隐藏代码的情况下检测到不安全的用法:

char* str;
size_t size;
scanf("%z", &size);
str = malloc(size);
scanf("%9s"); // how can the compiler determine if this is a safe call?!

当然,scanf如果您指定要读取的字符数,并且有足够的内存来保存字符串,则有一些方法可以编写安全代码。在 的情况下gets,无法指定要读取的字符数。

于 2010-06-04T20:35:21.943 回答
1

我不确定为什么 scanf 的手册页没有提到缓冲区溢出的可能性,但是 vanilla scanf 不是一个安全的选择。一个相当过时的链接 - http://blogs.msdn.com/b/rsamona/archive/2005/10/24/484449.aspx显示了这种情况。另外,检查这个(不是 gcc,但信息丰富) - http://blogs.msdn.com/b/parthas/archive/2006/12/06/application-crash-on-replacing-sscanf-with-sscanf-s。 aspx

于 2010-06-04T20:35:31.363 回答
-4

可能很简单,scanf 将根据读取的数据量在堆上分配空间。由于它不会分配缓冲区然后读取,直到读取到空字符,所以它不会冒覆盖缓冲区的风险。相反,它会读入自己的缓冲区,直到找到空字符,并且可能会在读取结束时将该缓冲区复制到另一个正确大小的缓冲区中。

于 2010-06-04T20:33:39.790 回答