繁体   English   中英

为什么 strcpy_s 比 strcpy 更安全?

[英]Why strcpy_s is safer than strcpy?

当我尝试使用strcpy函数时,visual studio 给了我一个错误

error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

在网上搜索和 StackOverflow 上的许多答案后,总结是strcpy_s在将大字符串复制到较短字符串时比strcpy更安全。 所以,我尝试了以下代码来处理更短的字符串:

char a[50] = "void";
char b[3];
strcpy_s(b, sizeof(a), a);
printf("String = %s", b);

代码编译成功。 但是,仍然存在运行时错误: 在此处输入图片说明

那么, scrcpy_s是如何安全的呢? 我对安全概念的理解有误吗?

为什么strcpy_s() “更安全”? 嗯,它实际上相当复杂。 (请注意,此答案忽略了已发布代码中的任何特定代码问题。)

首先,当 MSVC 告诉您诸如strcpy()类的标准函数已被“弃用”时,微软充其量是不完整的。 最坏的情况是,微软彻头彻尾地对你撒谎。 在这里将您想要的任何动机归因于 Microsoft,但是strcpy()和 MSVC 称为“不推荐使用”的许多其他函数是标准的 C 函数,它们肯定不会被除 Microsoft 之外的任何人弃用。 因此,当 MSVC 警告您需要在任何符合标准的 C 编译器中实现某个函数(其中大部分然后按要求流入 C++...)时,它会省略“由 Microsoft”部分。

微软“有帮助地”建议您使用的“更安全”功能 - 例如strcpy_s()将是标准的,因为它们是 C 标准的可选附件 K 的一部分,如果微软按照标准实施它们。

根据N1967 - 附件 K 的现场经验 - 边界检查接口

Microsoft Visual Studio 实现了这些 API 的早期版本。 但是,该实现并不完整,既不符合 C11 也不符合原始 TR 24731-1。 例如,它不提供set_constraint_handler_s函数,而是定义了一个_invalid_parameter_handler _set_invalid_parameter_handler(_invalid_parameter_handler)函数,其行为相似,但签名略有不同_invalid_parameter_handler _set_invalid_parameter_handler(_invalid_parameter_handler)兼容。 它还没有定义abort_handler_signore_handler_s函数、 memset_s函数(不是 TR 的一部分)或RSIZE_MAX宏。 Microsoft 实现也不将重叠的源和目标序列视为运行时约束违规,而是在这种情况下具有未定义的行为。

由于与规范存在大量偏差,因此不能认为 Microsoft 实现是符合标准或可移植的。

除了少数特定情况(其中strcpy()是其中之一)之外,Microsoft 版本的 Annex K 的“更安全”的边界检查功能是否更安全是值得商榷的。 根据 N1967 (粗体我的):

建议的技术勘误表

尽管自最初的提案以来已有十多年的时间,并且自 ISO/IEC TR 24731-1:2007 批准以来已近十年,并且自将边界检查接口引入 C 标准以来已近五年,但没有出现可行的符合性实现. API 继续存在争议,实施者继续拒绝实施请求。

边界检查接口的设计虽然是善意的,但有太多的问题需要纠正。 与依赖既定方法或现代技术相比,使用 API 已被视为导致质量更差、安全性更低的软件。 更有效和更少侵入性的方法已变得司空见惯,并且通常受到用户和安全专家等人的青睐。

因此,我们建议附件 K 要么从 C 标准的下一个修订版中删除,要么弃用然后删除。

但是请注意,在strcpy()的情况下, strcpy_s()实际上更类似于strncpy()因为strcpy()只是一个不进行边界检查的沼泽标准 C 字符串函数,但strncpy()是一个perverse 函数,因为它将完全填充其目标缓冲区,从源字符串中的数据开始,并使用'\\0' char值填充整个目标缓冲区。 除非源字符串填满了整个目标缓冲区,在这种情况下strncpy()不会以'\\0' char值终止它。

我再说一遍: strncpy()不保证正确终止的副本。

很难不比strncpy()更“安全”。 在这种情况下, strcpy_s()不会像strncpy()那样违反 最小惊讶原则 我会称之为“更安全”。

但是使用strcpy_s() - 以及所有其他“建议的”函数 - 会使您的代码事实上不可移植,因为 Microsoft 是任何形式的 Annex K 边界检查功能的唯一重要实现。

C 的头文件定义是:

errno_t strcpy_s(char *dest,rsize_t dest_size,const char *src)

您的示例的调用应该是:

#include <stdlib.h>

char a[50] = "void";
char b[3];
strcpy_s(b, sizeof(b), a);
printf("String = %s", b);

strcpy_s 需要目标的大小,该大小小于示例中的源。

strcpy_s(b, sizeof(b), a);

将是要走的路。

至于安全概念,现在已经完成了许多检查,以及处理错误的更好方法。

在您的示例中,如果您使用了 strcpy,则会触发缓冲区溢出。 其他函数,如 strncpy 或 strlcpy,会复制前 3 个字符而没有任何空字节终止符,这反过来会触发缓冲区溢出(这次是在读取中)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM