24

我正在研究Windows平台的遗留代码。当我在中编译代码时VS2013,它会给出以下警告:

错误 C4996:“ fopen”:此函数或变量可能不安全。考虑fopen_s改用。要禁用弃用,请使用 _CRT_SECURE_NO_WARNINGS。详情请参见在线帮助。”

它也会给出类似的警告sprintf。我知道sprintf_s由于缓冲区溢出,它比 sprintf 更安全。

但是fopen_s怎么能比 更安全fopen,因为fopen不接受缓冲区,所以没有缓冲区溢出的机会。任何人都可以提供一个fopen不安全且fopen_s安全的案例吗?

4

2 回答 2

27

在这种s情况下, 不代表“安全”,它代表“安全增强”。对于fopen_s,在尝试打开文件之前检查参数的有效性。

使用fopen,您可以为文件名传递一个 NULL 指针,并且所有内容很可能会分崩离析。fopen_s没有这个问题(a)

请记住,这些边界检查接口fopen_s是 ISO 标准的可选部分,详见附录 K(无论如何都在 C11 中)。实现不需要提供它们,老实说,fopen如果您知道自己作为编码人员在做什么,那么 和许多其他所谓的不安全功能是完全安全的。

有趣的是,它fopen_s会为您捕获 NULL 指针,但不会捕获无效指针,因此它的安全性增强而不是安全 - 如果您传递无效但非 NULL 指针,您仍然会造成一些损害。

只要您传递正确的大小,其他强制您提供目标缓冲区大小的“安全”功能也是安全的。传递一些太大的东西,所有的赌注都被取消了。


(a)来自C11 K.3.5.2.1 The fopen_s function

errno_t fopen_s (
    FILE * restrict * restrict streamptr,
    const char * restrict      filename,
    const char * restrict      mode);

运行时约束

streamptr、文件名或模式都不能是空指针。

如果存在运行时约束冲突,fopen_s 不会尝试打开文件。此外,如果 streamptr 不是空指针,则 fopen_s 将 *streamptr 设置为空指针。

相比之下,C11 7.20.5.3 The fopen function文件名和模式必须都指向一个字符串,但没有指定如果提供 NULL 指针会发生什么(大多数实现可能会因空指针取消引用而崩溃)。

于 2013-10-16T05:55:51.857 回答
1

回到 VS2005 出来的时候,我认为这只是微软在他们的“让我们做专有功能而不是给人”方面走得太远了snprintf(),因为如果传递一个 NULL 指针,两者都会(默认情况下)引发一个 win32 异常(虽然fopen会引发STATUS_ACCESS_VIOLATIONfopen_s会引发STATUS_INVALID_PARAMETER)。这意味着除非添加特定于 Win32 的代码来处理异常,否则两者都会导致程序立即崩溃。

然而,看一眼 CRT 源代码确实发现了一个小区别:使用的共享标志fopen是完全允许的,而使用的共享标志fopen_s 禁止其他进程打开文件进行写入。这样,fopen_s更安全,因为这意味着文件不会在您的进程的脚下更改。

于 2020-10-25T11:09:31.217 回答