提问人:William Pursell 提问时间:12/22/2022 最后编辑:William Pursell 更新时间:12/30/2022 访问量:603
是 'scanf(“%d”, ...)“和”得到“一样糟糕?
Is `scanf("%d", ...)` as bad as `gets`?
问:
多年来,一直被普遍贬低为不安全的功能。(规范的 SO 问题是:为什么 gets 函数如此危险,以至于不应该使用它?该功能非常糟糕,以至于已从 C11 语言标准中删除。支持者(如果有的话)会争辩说,如果你知道输入的结构,使用它是完全可以的。gets
gets
gets
为什么那些贬低并承认依赖输入结构是愚蠢的人允许使用作为转换说明符?这是一个社会学问题,真正的问题是:为什么格式字符串不安全?gets
%d
scanf
%d
scanf
答:
如果格式字符串包含原始转换说明符(“raw”表示“没有最大字段宽度”),则如果输入流包含的字符串是无法放入 .例如,字符串不能在 的平台上表示,其中 .该语言仅保证 an 足够大以容纳范围 -32767 到 +32767,因此任何包含该字符串的输入流都可能导致未定义的行为。可以使用 来避免这种潜在的未定义行为。大多数现代平台的值 INT_MAX 远大于 32767,因此实际上转换说明符上的宽度修饰符可以大于 4,但应为平台确定(在编译时或运行时),并且它必须存在于格式字符串中。scanf
%d
int
5294967296
int
sizeof(int) == 4
C
int
32768
%4d
如果不添加宽度修饰符,则不妨只使用将一行读入缓冲区并用于解析值。这(也许)会使错误对读者来说更加明显。gets
sscanf
评论
gets()
"%s"
scanf
fscanf
函数 ¶10: [...]如果此对象没有适当的类型,或者转换结果无法在对象中表示,则行为未定义。
众所周知,前者无法控制/检测导致UB的缓冲液溢出。它本来可以有一个大小参数。gets()
除了@William Pursel关于范围的好答案。int
scanf(“%d”, ...)
: 输入不限于一行。
gets()
阅读 1 行。 在 中,首先使用可能包含多行的前导空格。"%d"
scanf()
scanf(“%d”, ...)
: 不读取整行。
与 不同,在输入后保留任何输入。这通常包括 .不阅读整行通常为后续问题埋下种子。gets()
scanf("%d", ...)
int
'\n'
根据目标,不会抱怨尾随非数字文本。scanf("%d", ...)
C 语言缺乏一种强大的方法来读取一行。IMO、、、、扩展都缺少一些功能。fgets()
gets_s()
scanf(anything)
getline()
我会争取一个总是读一行,总是形成一个字符串并返回(文件末尾,输入错误),成功时为 1,太小时为 0。int scan_line(size_t sz, char *buf /*, size_t *length_read*/)
buf
EOF
sz
或者(更值得商榷)可以改进:*scanf()
添加传递和朋友的能力。这是非常需要的。
size
"%s"
定义了溢出时的行为。
int
类似于在空白处扫描,但不是.不影响返回值。
"%#\n"
'\n'
类似于在 1 中扫描的东西。对返回值有贡献。可以使用前导空格来允许可选的前导非空格。
"%\n"
'\n'
"% \n"
'\n'
报价始终只显示 1 行。
*scanfln()
评论
scanf
"%s"
"%8s"
scanf_s()
"%*[ \t]"
"%1[\n]"
errno
ERANGE
printf()
*
%ms
%s
char *
%ms
char **
*
scanf()
@
printf()
scanf()
*
printf()
*
scanf()
-1
scanf_s()
不,没有.scanf("%d", …)
gets
gets
因为它变得很糟糕,因为几乎在任何环境中都无法安全地使用它。缓冲区溢出是可能的,无法预防,并且很可能导致任意的不良后果。
另一方面,可能发生的最糟糕的事情是整数溢出。虽然这在理论上也是未定义的行为,但在实践中,它几乎总是导致 (a) 安静的环绕,(b) 溢出到 或 ,或 (c) 可能终止调用程序的运行时异常。scanf("%d", …)
INT_MAX
INT_MIN
很难想象攻击者可以使用 .另一方面,涉及的漏洞利用是司空见惯的。scanf("%d", …)
gets
(虽然不是提出的问题,但确实与.这是一个公平的问题,为什么前者并不总是像后者那样受到贬低。scanf("%s", …)
gets
评论
gets
没有任何方法可以防止缓冲区溢出错误。
因为没有办法使缓冲区溢出错误(它类型与格式字符串匹配)。scanf("%d", &x);
现在,以防万一
char s[5];
scanf("%s", s);
存在缓冲区溢出的危险(当用户类型使用超过 4 个字符时),但修复此代码以防止缓冲区溢出很容易:
char s[5];
scanf("%4s", s);
现在这个版本不能缓冲溢出。
请注意,中继容易出错,因此请防止与格式字符串相关的常见错误威胁警告作为错误。scanf
基本上没有办法防止无效(到长)用户输入。此外,在不破坏二进制或源代码兼容性的情况下,也无法修复它。
如果是更高级的格式,字符串可以保护您的表单缓冲区溢出,这可以通过静态分析工具强制执行。gets
scanf
上一个:奇怪的 grep 行为
下一个:awk 中 NF 减少后
评论
gets
scanf("%d", ...)