微软的SDL和memcpy弃用

10

正如一些人可能已经知道的那样,微软已经将memcpy()从他们的安全开发生命周期中禁止使用,并用memcpy_s()替代。

void *memcpy(void *dest, const void *src, size_t n);

/* simplified signature */
errno_t memcpy_s(void *dst, size_t dstsize, const void *src, size_t n);

所以,如果您的代码过去是这样的:

if (in_len > dst_len) {
    /* error */
}
memcpy(dst, src, in_len);

它变成:

if (memcpy_s(dst, dst_len, src, src_len)) {
    /* error */
}

或者,进行截断:

memcpy(dst, src, min(in_len, dst_len));

(void)memcpy_s(dst, dst_len, src, src_len);
问题:额外的长度参数如何使代码更安全?要使用memcpy(),我应该已经知道所有四个参数并将适当的长度作为第三个参数传递。什么阻止我犯同样的错误,即计算目标缓冲区大小时出现误差,并传递错误的dst_size值?我看不出它与memcpy()有任何不同,也不明白为什么它正在被弃用。是否有任何常见的用例是我没有考虑到的?我在这里漏掉了什么?

请注意,memcpy_s()是C11 Annex K的一部分,它只是C11规范的可选部分。几个编译器实现者已经拒绝了Annex K,并且永远不会实现它。因此,只有在定义了__STDC_LIB_EXT1__时才使用它。Redhat建议将其从未来的C规范中删除。 - DavidJ
7个回答

14

即使在“安全”版本中,您也可以错误地获取参数。微软似乎认为您总是会使用类似以下的内容:

errno_t e = memcpy_s (&dstbuff, sizeof(dstbuff), &srcbuff, sizeof(srcbuff));

并检查错误。

但这只对那些不懂该语言如何运作的人有帮助。在我看来,这群人要么不应该使用该语言,要么应该学习如何正确地使用它。

这种支持方式从长远来看不会给他们带来任何好处,因为他们的代码将无法移植。

也许微软进行了一些分析,发现很多人误用 memcpy() 导致了很多问题,所以他们认为这样做可以解决问题。但是,如果真是这样的话,我认为更好的解决方案是教育开发人员,而不是强制他们使用非标准特性,这些特性在标准编译器中将无法使用。


3
我完全同意,Pax。这种拐杖从长远来看对他们没有任何好处。 - ninesided
3
第一部分正确,第二部分错误——它不是一个拐杖,实际上更适用于那些认为自己是“语言如何运作”的专家,并且总是在寻找捷径。正如@ninesided所说的新的“安全”版本让你思考你自己正在做什么,并(希望能够)停止试图绕开语言。 - AviD
但是我应该强调,你所说的关于一般模式依赖于使用sizeof(buf)的观点非常好,并且应该被鼓励(当然,除非你没有从缓冲区开头进行复制...) - AviD
1
无论你处理堆还是栈,甚至无论你是从缓冲区的开头复制,都不重要。你/需要/知道剩余多少空间,如果你知道,memcpy是安全的。 - Matthew Flaschen
@AviD,我已经知道自己在做什么了,那些不懂的人没有权利使用 C :-) - 它是编程语言中的电锯,那些没有经验和谨慎使用链锯的人会割断他们的肢体。 - paxdiablo
3
鼓励程序员编写不可移植的代码非常有用...对于微软而言。 - Jeremy Friesner

8
复制信息总是不良的设计,因为它只会增加犯错的可能性。当涉及到API设计时,微软一直有着令人震惊的记录,并且过去只是靠着卓越的文档才得以挽救。让人欣慰的是,他们无法删除原始的memcpy()函数 - 它是ANSI C的一部分。

1
+1 我同意。我认为微软等低级“硬核”程序员应处理的主要安全问题是缓冲区溢出。其他任何事情都可以(应该)由强类型语言(C#,Java,Ada等)进行检查。 - ATorras
1
他们无法删除原始的memcpy()函数,但他们可以让它输出警告信息。如果他们愿意,他们可以使这些警告难以或不可能被忽略。 - Steve Jessop

6

您说得很对。如果您正在跟踪两个缓冲区的长度,那么使用memcpy是安全的。如果没有跟踪长度,使用memcpy_s也无法保护您。


4

我认为你并没有错过什么,我认为你提供的文章片段已经涵盖了这个问题:

即使没有其他的原因,memcpy_s也会让你思考目标缓冲区的大小。


1
根据微软的说法,这将使编写代码质量检查变得更加容易 - 您可以检查以确保程序员不将相同的值传递给两个大小参数(许多人可能仍然因懒惰而这样做)。另一件事情(实际上与代码安全无关)是你可以使用这种方法清理你的代码,因为在你的代码中要检查的内容更少了 - memcpy_s函数将为您检查目标缓冲区中是否有足够的空间,消除了其中一个检查。
最重要的是,memcpy_s返回一个错误代码,指示整个复制是否成功,而使用 memcpy 则无法确保这一点。 这就是微软认为memcpy_s memcpy 更加安全的原因。

我认为依赖memcpy_s来进行普通逻辑检查是不好的实践,我相信即使那些支持该函数的人也会同意这一点。 - Matthew Flaschen
我同意 - 这种推理不是我的,而是微软的 - 我已经更新了我的评论,使其更加清晰。第二部分是我的观点,但我认为代码清晰度并不是禁止使用此函数的好理由。 - a_m0d

0

C 语言汇编是为那些知道自己在做什么的人准备的,我记得直接从 K&R 那里读到过类似的话。


-1

有时我真的感到绝望,现在世界上真的没有多少像我们一样真正理解机器、热爱移动位操作的程序员了。虽然有点离题,但我喜欢知道正在发生什么事情,绝对不希望出现任何垃圾收集器悄悄在后台试图整理那些懒惰程序员凌乱的堆积。我的意思是,匹配 free() 调用和 malloc()/strdup() 调用有多难?确保你已经分配了足够的缓冲区空间以便您可以安全地调用 memcpy() 有多难?答案是:并不十分困难,但99.9%的程序员真的不关心他们所做的事情,因为他们只是为了钱而不是写出一段优美而富有格调的代码的热情。

结束怒吼。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接