关于c ++:为什么strncpy不安全?

Why is strncpy insecure?

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


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


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

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

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


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

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


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

最重要的是,strncpy不被视为不安全,也从未被视为不安全。可以附加到该功能的唯一"不安全性"主张是C内存模型和C语言本身普遍不安全的广泛主张。 (但这显然是一个完全不同的话题)。

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

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

例如,可以对除法运算符提出完全相同的主张。众所周知,关于C语言的最常提出的问题之一是"我假设1/2将评估为0.5,但我却得到了0。为什么?"但是,我们并不认为除法运算符不安全是因为语言初学者往往会误解其行为。

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

strncpy函数就是这样。就像初学者要花时间了解伪随机数生成器实际做什么一样,花时间也要花些时间来了解strncpy实际做什么。需要花费时间来了解strncpy是转换函数,该函数旨在将零终止的字符串转换为固定宽度的字符串。需要花费时间来了解strncpy与"安全字符串复制"完全无关,并且不能被有意义地用于此目的。

当然,语言学生学习strncpy的目的通常比用除法运算符解决问题要花费更长的时间。但是,这是针对strncpy的任何"不安全"声明的基础。

附言链接在接受的答案中的CERT文档专门致力于:证明典型的不称职滥用strncpy功能作为strcpy的"安全"版本的不安全性。绝不是要声明strncpy本身是不安全的。


Git 2.19(Q3 2018)的pathc发现,滥用strcat()之类的系统API函数太容易了; strncpy(); ...,并禁止在此代码库中使用这些功能。

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

banned.h: mark strcat() as banned

The strcat() function has all of the same overflow problems as strcpy().
And as a bonus, it's easy to end up accidentally quadratic, as each subsequent call has to walk through the existing string.

The last strcat() call went away in f063d38 (daemon: use
cld->env_array when re-spawning, 2015-09-24, Git 2.7.0).
In general, strcat() can be replaced either with a dynamic string
(strbuf or xstrfmt), or with xsnprintf if you know the length is bounded.