为什么strncpy不安全?


69

我正在寻找找出为什么strncpy被认为是不安全的。是否有人对此有任何形式的文档或使用它的漏洞利用示例?


4
据我了解,strcpy是不安全的,而strncpy是该版本的安全版本。
马克W

29
名称上没有_s。没有_s就无法保证安全
肖格9年

2
如果您使用的是c ++,请不要使用它,而应使用std :: string。
lothar

7
strncpy并非被设计为strcpy的安全版本,但已被滥用。有关说明,请参见此处:lysator.liu.se/c/rat/d11.html。现在可以争论strncpy的设计是否错误。历史是C委员会的借口:)如果您需要安全版本的strcpy,我建议您使用其他功能。
Johannes Schaub-litb

1
@stimms:当您说“对strncpy_s更感兴趣”时,您是说strncpy_s()可能不安全,还是为什么strncpy()与strncpy_s()不安全?strncpy_s()是strncpy()的“安全”版本,它所做的一切(不同于strncpy())都要求您指定目标缓冲区的长度。
蒂姆(Tim)

Answers:


43

看一下这个网站; 这是一个相当详细的解释。基本上,strncpy()不需要NUL终止,因此容易受到各种攻击。


1
好点子。因此,即使使用strncpy,也要确保缓冲区以“ \ 0”结尾。
风铃草

14
+1,但请注意,当您忘记手动添加空终止符时,strncpy仅不安全。如果您记不起,请考虑包装函数,该函数在调用strncpy之后始终将最终字符设置为'\ 0'。
ojrac

2
@RBerteig,请注意,MS strncpy_s和company不是1:1 strncpy替换,因为它们不会将缓冲区零填充。尽管IMHO strl *功能更通用,更快捷,但它们还是更好的脚本。@ojrac,它仍然很容易被滥用,因为您必须记住两者复制(bufsize-1)而不是(bufsize),或者将“ rm -Rf / a”转换为“ rm -Rf / ”。

7
“ NULL终止”不正确。ASCII字符\0通常写为“ NUL”,以避免与NULL指针混淆。
克里斯·卢茨

4
@Billy ONeal,opengroup.org / onlinepubs / 007908775 / xsh / strncpy.htmlopengroup.org / onlinepubs / 009695399 / functions / strncpy.html和C1x草案不同意您的看法。除非ANSI C另有说明,否则非零填充不是标准行为。再说一次,微软根本不支持C。他们所拥有的只是一个C ++编译器。

12

最初的问题显然是strcpy(3)不是内存安全的操作,因此攻击者提供的字符串可能比缓冲区长,这会覆盖堆栈上的代码,并且如果精心安排,还可以执行攻击者的任意代码。

但是strncpy(3)的另一个问题是,它不会在每种情况下都在目标位置提供空终止。(想象一个源字符串长于目标缓冲区。)将来的操作可能会期望在大小相等的缓冲区之间符合C nul终止的字符串,并且当将结果复制到第三个缓冲区时,下游会发生故障。

使用strncpy(3)比strcpy(3)更好,但是像strlcpy(3)这样的东西仍然更好。


1
strncpy不是好过strcpystrlcpy在可用的地方很有用,在没有的地方很容易重写。
chqrlie

7

为了安全地使用strncpy,必须(1)手动将空字符粘贴到结果缓冲区上;(2)事先知道缓冲区以空值结尾,然后将(length-1)传递给strncpy,或者(3)知道缓冲区将永远不会使用不会将其长度绑定到缓冲区长度的任何方法进行复制。

重要的是要注意,strncpy将对缓冲区中的所有内容进行零填充(超过复制的字符串),而其他受长度限制的strcpy变体则不会。在某些情况下,这可能会浪费性能,但在其他情况下,则可能会带来安全优势。例如,如果使用strlcpy将“ supercalifragilisticexpalidocious”复制到缓冲区中,然后再复制“ it”,则缓冲区将保存“ it ^ ercalifragilisticexpalidocious ^”(使用“ ^”表示零字节)。如果将缓冲区复制为固定大小的格式,则多余的数据可能会随之标记。


4

该问题基于“已加载”的前提,这使问题本身无效。

这里的底线strncpy是不被认为是不安全的,并且从未被认为是不安全的。可以附加到该功能的唯一“不安全性”主张是C内存模型和C语言本身普遍不安全的广泛主张。(但这显然是一个完全不同的话题)。

在C语言领域中,固有的某种“不安全性”的误导性信念strncpy源于广泛使用strncpy的“安全字符串复制”的可疑模式,即该功能无法实现且从未打算这样做。这种用法确实很容易出错。但是,即使在“容易出错”和“不安全”之间打上等号,仍然是使用问题(即缺乏教育问题)而不是strncpy问题。

基本上,可以说,唯一的问题strncpy是不幸的命名,这使新手程序员假设他们了解此功能的作用,而不是实际阅读规范。一个不称职的程序员在看函数名时会认为它strncpy是的“安全版本” strcpy,而实际上这两个函数是完全不相关的。

例如,可以对除法运算符提出完全相同的主张。如您所知,关于C语言的最常提出的问题之一是“我认为这1/20.5得出结果,但我0反而得到了。为什么?” 但是,我们并不认为除法运算符是不安全的,仅仅是因为语言初学者往往会误解其行为。

再举一个例子,我们不会将伪随机数生成器函数称为“不安全的”,仅仅是因为不称职的程序员通常对它们的输出不是真正随机的事实感到不愉快。

这正是它是如何与strncpy功能。就像初学者要花一些时间来学习伪随机数生成器的实际功能一样,花时间也要让他们学习strncpy实际的功能。学习这strncpy是一个转换函数需要花费一些时间,该函数旨在将零终止的字符串转换为固定宽度的字符串。学习strncpy与“安全字符串复制”完全无关并且不能有效地用于此目的需要时间。

当然,语言学生学习目的的时间通常strncpy比与除法运算符解决问题所需的时间更长。但是,这是对的任何“不安全”主张的基础strncpy

PS接受的答案中链接的CERT文档专用于:证明典型的无能的滥用strncpy功能(作为的“安全”版本)不安全strcpy。绝不打算声称它strncpy本身是不安全的。


2
当人们说“strncpy不安全”时,他们的意思是“它没有成功之道”。没错
Ben Voigt 2014年

2

Git 2.19(Q3 2018)的路径发现,很容易滥用系统API函数,例如strcat(); strncpy(); ...,并禁止在此代码库中使用这些功能。

请参阅Jeff King()的commit e488b7acommit cc8fdaecommit 1b11b64(2018年7月24日)和commit c8af66a(2018年7月26日(由Junio C Hamano合并--commit e28daf2中,2018年8月15日)peff
gitster

banned.h:标记strcat()为禁止

strcat()函数具有与相同的所有溢出问题strcpy()
另外,由于每次后续调用都必须遍历现有字符串,因此很容易以偶然的平方结束。

最后一个strcat()调用在f063d38消失了(守护进程:重新生成时使用cld-> env_array,2015-09-24,Git 2.7.0)。
通常,strcat()可以用动态字符串(strbufxstrfmt)替换,也可以用xsnprintf长度限制的替换。


这个答案对之前的答案有什么帮助?
chqrlie

1
@chqrlie只是在OP运行9年之后的一个例子,这些功能仍然非常危险(并且补丁说明了原因),以至于它们被C代码库禁止(例如,这里是Git的代码)
VonC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.