scanf 的宽度规范与 scanf_s 的区别

Difference between scanf's width specification and scanf_s

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

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

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

scanf("%9s", a);

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

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

而且如果 scanf 的宽度规范阻止缓冲区溢出,为什么我们调用原始 scanf "unsafe"?

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

如果您想使用针对 C99(或更早版本)的编译器,请使用 scanf()。 对于 C11 Standardscanf_s() 比 scanf() 更难使用,以提高针对 buffer overflows.

的安全性

So what is basically different between two ways?

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

的情况下必须传递参数

And if scanf's width specification is blocking buffer overflow, why do we call original scanf "unsafe"?

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

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

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

scanf_s 通过可变参数传递字段宽度解决了字符数组情况下的缓冲区溢出问题,就像在 printf 中(在 scanf() 字段中一样)宽度必须为 embedded into format string)。此外,字段宽度在 scanf_s 中是强制性的,但在 scanf 中是可选的。