安全使用strcpy函数

4

我们公司的编码标准禁止使用普通的 strcpy,因为它有可能会导致缓冲区溢出。我在查看我们代码中链接到的第三方库的源代码时发现了一个这样的strcpy用法:

for (int i = 0; i < newArgc; i++)
{   
     newArgv[i] = new char[strlen(argv[i]) + 1];
     strcpy(newArgv[i], argv[i]);       
}

由于在为要复制到的缓冲区分配内存时使用了strlen,所以这看起来很好。有可能有人利用这个常规strcpy吗,还是像我想象的那样安全?

我曾看到过对strcpy的幼稚使用导致缓冲区溢出的情况,但这似乎并没有这种情况,因为它总是使用strlen分配了正确大小的空间给缓冲区,然后使用argv[]作为源将其复制到该缓冲区中,而这应始终以空值结尾。

我很好奇,是否有人运行这段代码时,能够利用调试器来利用这个漏洞,或者如果有人试图攻击我们的二进制文件(使用此库源链接在其编译版本中),是否还有其他策略可以利用这种strcpy的用法。感谢您的意见和专业知识。


11
如果有人拥有以调试器运行你的代码的特权,那么调用或不调用哪个库函数就不重要了。这个人获得了机器的 root 权限,可以随心所欲地做任何事情。 - nvoigt
旁注:上面的代码看起来像是一种天真的复制程序参数的方式,因为它为每个参数都进行了单独的内存分配。它应该做的是为所有参数分配一个内存块。 - Maxim Egorushkin
1
在这种情况下,memcpy()会更有效率,但无论如何,你最好使用std::string - Slava
2
@ThomasMatthews 请不要推荐strncpy,它在很多方面比strcpy更糟糕。它并不总是以空字符结尾,而当它这样做时,它还会浪费时间用空字节填充缓冲区的剩余部分。 - nos
2
@ThomasMatthews 无论问题是什么,strncpy通常都不是答案。它不能保证放置尾随的 '\0',并且在复制小字符串时确保分配了过多的缓冲区。strcpy_sstrlcpy都是更好的选择(而且我真的很烦恼它们都不是强制性的)。(@nos: snap!) - Martin Bonner supports Monica
显示剩余4条评论
4个回答

6

可以安全地使用strcpy,只是需要非常努力的工作(这就是为什么您的编码标准禁止它)。

然而,您发布的代码不是漏洞。无法使用它覆盖内存位; 我不会费心重新编写它。(如果您决定重写,请改用std :: string。)


使用 strcpy 是不难的,但是将正确的松散代码改为正确使用它是有难度的。 - Pete Becker

3

这段代码存在多个问题:

我们假设没有数据竞争,但我们不能确定。

无论如何,如果您坚持手动管理字符串,则在此处使用 strcpy() 仅会导致轻微的性能下降,这是唯一的“问题”。


比较已复制到 n 的字符数量并不比比较被复制的元素与 \0 本身慢。我在这里没有看到所声称的性能损失。 - Pete Becker
@PeteBecker:你正在使用错误且过于简单的计算模型。如果你知道要复制多少字节,你可以使用专门的指令和/或一次移动更大的块。 - Deduplicator
@Deduplicator:鉴于贝克先生曾经是一家编写了广泛使用的C++运行时库的公司的一半,我相信他很清楚。a)您可以在strcpy中使用某些专用指令;b)对是否对齐等进行测试可能会产生很多性能优势;c)始终如此,对于性能,唯一的方法是使用真实数据(长度,对齐方式)进行测量才能得出结论。 - Martin Bonner supports Monica
@MartinBonner:不,它们使用长度。当然,在特定情况下是否重要,不能没有广泛的现实测试就做出明确的决定,这还有什么新鲜事... - Deduplicator

0
作为一般的strcpy替代惯用语,假设您可以接受打印格式化函数的轻微开销,请使用snprintf:
snprintf(dest, dest_total_buffer_length, "%s", source);

例如

snprintf(newArgv[i], strlen(argv[i]) + 1, "%s", argv[i]);

这是安全的,简单的,而且您不需要考虑+1/-1尺寸调整。


0

偏离编码标准总是可能的,但必须要好好记录为什么这样做。

strcpy 的主要问题在于它没有长度限制。当注意到这一点时,这不是问题,但这意味着 strcpy 总是需要配合一些保护代码。许多经验不足的编码人员都陷入了这个陷阱,因此编码指南开始实践。

安全处理字符串复制的可能方法包括:

  • 检查字符串长度
  • 使用安全变体,如 strlcpy,或在旧版 Microsoft 编译器上使用 strncpy_s。

strncpy_s作为一个选项被包含在C11标准中:http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1969.htm和https://embeddedgurus.com/barr-code/2017/08/cs-strcpy_s-c11s-more-secure-version-of-strcpy/。 - Jetski S-type
感谢关于C11中strncpy_s是可选的额外信息。您提供的链接还提到,2017年“没有主流的C标准库实现它”。当标准发布6年后仍未实现时,我猜它永远不会被实现了。 - ckielstra
是的,我同意。遗憾的是标准并不反映实际使用情况。 - Jetski S-type

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