1
scanf_s("%s", a, 10);

此代码将保护我们的程序免受缓冲区溢出攻击。

但是没有scanf_s,我们可以这样写:

scanf("%9s", a);

我认为这段代码也会阻止缓冲区溢出。这是真的?

那么两种方式的基本区别是什么?

如果 scanf 的宽度规范是阻止缓冲区溢出,为什么我们称原始 scanf 为“不安全”?

4

1 回答 1

3

scanf_s()C99 标准(或以前的标准)没有描述。

如果您想使用面向 C99(或以前)的编译器,请使用 scanf()。因为C11 Standard,scanf_s()比 scanf() 更难使用,以提高针对buffer overflows.

那么两种方式的基本区别是什么?

scanf_s()是更安全的 scanf() 版本。在指定目的地的参数之后,必须提供目的地的大小。该程序在复制缓冲区之前检查缓冲区是否具有指定的大小,以确保没有覆盖并且没有运行恶意代码。在 的情况下必须传递参数scanf_s()

如果 scanf 的宽度规范是阻止缓冲区溢出,为什么我们称原始 scanf 为“不安全”?

可与scanf函数一起使用的格式说明符支持显式字段宽度设置,这限制了输入的最大大小并防止缓冲区溢出。但是scanf()特性很难使用,因为字段宽度必须是embedded into format string(没有办法通过可变参数传递它,因为它可以在 中完成printf)。scanf 在这方面确实设计得很差。但是,任何声称 scanf 在字符串缓冲区溢出安全性方面被无可救药地破坏的说法都是完全虚假的,并且通常是由懒惰的程序员提出的。

真正的问题scanf()具有完全不同的性质,即使它也与溢出有关。当 scanf 函数用于将数字的十进制表示转换为算术类型的值时,它不提供算术溢出保护。如果发生溢出,scanf 会产生未定义的行为。因此,在 C 标准库中执行转换的唯一正确方法是使用strto族函数。

因此,综上所述,问题scanf在于很难正确安全地使用字符串缓冲区。并且不可能安全地用于算术输入。后者才是真正的问题。前者只是一种不便。

scanf_s通过通过可变参数传递字段宽度来解决字符数组情况下的缓冲区溢出问题,因为它可以在printfscanf()字段宽度必须是embedded into format string)中完成。此外,字段宽度在 中是必需的,scanf_s但在 中是可选的scanf

于 2016-11-04T11:26:24.393 回答