【发布时间】:2011-08-17 22:05:58
【问题描述】:
我对@987654321@ 不好的建议有模糊的记忆。我知道如果我使用字段宽度说明符它不会溢出缓冲区,那么我的记忆只是在和我开玩笑吗?
【问题讨论】:
标签: c security buffer-overflow scanf
我对@987654321@ 不好的建议有模糊的记忆。我知道如果我使用字段宽度说明符它不会溢出缓冲区,那么我的记忆只是在和我开玩笑吗?
【问题讨论】:
标签: c security buffer-overflow scanf
是的。如果你指定了字符串宽度,那么就不会出现与缓冲区溢出相关的问题。
无论如何,就像@Mehrdad 向我们展示的那样,如果缓冲区大小未在编译时确定,则可能会出现问题。我想限制可以提供给 sscanf 的字符串的长度,可以解决这个问题。
【讨论】:
sscanf_s?
我认为这取决于您如何使用它:如果您正在扫描类似int 的内容,那很好。如果您正在扫描字符串,则不是(除非我忘记了宽度字段?)。
编辑:
扫描字符串并不总是安全的。
如果您的缓冲区大小是一个常数,那么您当然可以将其指定为%20s 之类的东西。但是如果它不是一个常量,你需要在格式字符串中指定它,你需要这样做:
char format[80]; //Make sure this is big enough... kinda painful
sprintf(format, "%%%ds", cchBuffer - 1); //Don't miss the percent signs and - 1!
sscanf(format, input); //Good luck
这是可能的,但非常很容易出错,就像我在之前的编辑中所做的那样(忘记处理空终止符)。您甚至可能会溢出格式字符串缓冲区。
【讨论】:
char buffer[2]; sscanf("Oops!", "%s", &buffer);
sprintf之类的东西来构造格式字符串,这是大多数人不想经历的痛苦。
sscanf 可能被认为不好的原因是因为它不需要您为字符串参数指定最大字符串宽度,如果从源字符串读取的输入更长,这可能会导致溢出。所以准确的答案是:如果您在格式字符串中正确指定宽度是安全的,否则不是。
【讨论】:
请注意,只要您的缓冲区至少与 strlen(input_string)+1 一样长,%s 或 %[ 说明符就不会溢出。如果您想强制执行更严格的限制,您还可以在说明符中使用字段宽度,或者您可以使用%*s 和%*[ 来禁止分配,而是在前后使用%n 来获取原始字符串中的偏移量,并且然后使用它们从输入字符串中就地读取生成的子字符串。
【讨论】:
有两点需要注意。
正如其他人所提到的,如果您指定的大小小于或等于格式字符串中的输出缓冲区大小,那么您是安全的。
这里你需要确保它是一个空终止字符串,或者你不会读取超过输入缓冲区大小。
如果输入字符串不是以 null 结尾的,sscanf 可能会读取超出缓冲区的边界并在未分配内存时崩溃。
【讨论】:
所有scanf 函数都有基本的设计缺陷,只有部分可以修复。它们不应在生产代码中使用。
如果一个值超出了您存储该值的变量的可表示范围,则数值转换具有完全的恶魔飞出未定义行为。I am not making this up。允许 C 库使您的程序崩溃,因为有人键入了太多的输入数字。即使它没有崩溃,它也没有义务做任何明智的事情。没有解决方法。
正如其他几个答案所指出的,%s 与臭名昭著的gets 一样危险。 可能可以通过使用“m”修饰符或字段宽度来避免这种情况,但是您必须记住对要转换的每个文本字段都这样做,并且您必须连接字段宽度转换为格式字符串 - 您不能将 sizeof(buff) 作为参数传递。
如果输入与格式字符串不完全匹配,sscanf 不会告诉您在放弃之前它在输入缓冲区中有多少 字符。这意味着唯一实用的错误恢复策略是丢弃整个输入缓冲区。 可以如果您正在处理的文件是某种记录的简单线性数组(例如,对于 CSV 文件,“跳过格式错误的行并继续下一个”是明智的错误恢复策略),但如果输入的结构比这更多,你就完蛋了。
在 C 语言中,如果使用 lex 和 yacc 来解析不够复杂的作业,通常最好使用 POSIX 正则表达式 (regex.h) 或手动字符串解析来完成。 strto* 数字转换函数 do 在溢出时具有明确且有用的行为,do 告诉您它们消耗的输入字符有多少,string.h 有很多手动解析器的便捷功能(strchr、strcspn、strsep 等)。
【讨论】: