据我所知,strlcpy
和strlcat
被设计为安全的替代品strncpy
和strncat
。但是,有些人仍然认为他们没有安全感,只会引起另一种类型的问题。
有人可以举一个使用strlcpy
或strlcat
(即始终为null的函数终止其字符串)如何导致安全问题的示例吗?
乌尔里希·德雷珀(Ulrich Drepper)和詹姆斯·安提尔(James Antill)指出,这是事实,但从未提供示例或阐明这一点。
据我所知,strlcpy
和strlcat
被设计为安全的替代品strncpy
和strncat
。但是,有些人仍然认为他们没有安全感,只会引起另一种类型的问题。
有人可以举一个使用strlcpy
或strlcat
(即始终为null的函数终止其字符串)如何导致安全问题的示例吗?
乌尔里希·德雷珀(Ulrich Drepper)和詹姆斯·安提尔(James Antill)指出,这是事实,但从未提供示例或阐明这一点。
Answers:
首先,strlcpy
从未被用作的安全版本strncpy
(strncpy
也从未被用作的安全版本strcpy
)。这两个功能是完全无关的。strncpy
是一个完全与C字符串(即以N结尾的字符串)无关的函数。str...
它的名称中带有前缀的事实只是一个历史错误。的历史和目的strncpy
是众所周知的且有据可查。这是一个功能,用于处理在某些历史版本的Unix文件系统中使用的所谓的“固定宽度”字符串(不适用于C字符串)。如今,有些程序员对它的名称感到困惑,并认为它strncpy
应该以某种方式用作有限长度的C字符串复制功能(strcpy
),实际上是完全废话,并会导致不良的编程习惯。当前形式的C标准库没有任何功能可用于有限长度的C字符串复制。这正是strlcpy
适合的地方。strlcpy
确实是为使用C弦而创建的真正的有限长度复制功能。strlcpy
正确执行有限长度复制功能应做的所有事情。可以针对的唯一批评是,遗憾的是,这不是标准的。
其次,strncat
另一方面,确实是一个与C字符串一起使用并执行有限长度的串联的函数(它确实是的“安全”兄弟strcat
)。为了正确使用此函数,程序员必须格外小心,因为此函数接受的size参数实际上并不是接收结果的缓冲区的大小,而是其剩余部分的大小(也包括终止符)被隐式计算)。这可能会造成混淆,因为为了将该大小与缓冲区的大小绑定在一起,程序员必须记住要执行一些额外的计算,而这些计算通常被用来批评strncat
。strlcat
解决了这些问题,更改了接口,因此不需要额外的计算(至少在调用代码中)。同样,我看到的唯一可以批评这一点的依据是该功能不是标准的。此外,strcat
由于基于重新扫描的字符串连接的基本概念的可用性有限,因此在专业代码中通常不会看到group函数。
至于这些功能如何导致安全问题……它们根本不可能。它们所导致的安全问题的程度不能超过C语言本身“导致安全问题”的程度。您会看到,很长一段时间以来,人们一直强烈认为C ++语言必须朝着发展为某种怪异的Java风格的方向发展。这种情绪有时也会蔓延到C语言的领域,从而导致对C语言功能和C标准库的功能相当无知和被迫批评。我怀疑在这种情况下,我们可能也会处理类似的问题,尽管我当然希望事情并没有那么糟糕。
strcpy
是高于阈值,因此“不安全”,并且它们的优选字符串复制函数(无论是strlcpy
,strcpy_s
甚至strncpy
)低于阈值,因此“安全”。
strlcpy/strlcat
。人们可能会“不喜欢”零终止字符串的一般概念,但这不是问题所在。如果您知道“有很多不喜欢的理由strlcpy/strlcat
”,则可能应该写出自己的答案,而不是期望我能够读懂别人的想法。
strlcpy/strlcat
。虽然我相信我了解这是什么意思,但我个人拒绝承认这是传统C语言领域内的“安全问题”。我在回答中说过。
Ulrich的批评基于这样的想法,即程序未检测到的字符串截断会通过错误的逻辑导致安全问题。因此,为了安全起见,您需要检查截断。对字符串连接执行此操作意味着您正在执行以下检查:
if (destlen + sourcelen > dest_maxlen)
{
/* Bug out */
}
现在,strlcat
如果程序员记得要检查结果,则可以有效地执行此检查-因此您可以安全地使用它:
if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
/* Bug out */
}
李晶的观点是,因为你必须有destlen
和sourcelen
周围(或重新计算它们,这是strlcat
有效的呢),你可能也仅仅使用了更高效memcpy
呢:
if (destlen + sourcelen > dest_maxlen)
{
goto error_out;
}
memcpy(dest + destlen, source, sourcelen + 1);
destlen += sourcelen;
(在上面的代码中,dest_maxlen
是可以存储在dest
其中的字符串的最大长度-小于dest
缓冲区的大小。 dest_bufferlen
是的完整大小dest buffer
)。
memcpy
它可以是任何类型的内存,我有一个补充维试图理解代码时检查。我有一个旧版应用程序来调试所有用memcpy完成的地方,这是要纠正的真正的PITA。移植到专用的String函数后,它更易于阅读(并且速度更快,因为strlen
可以删除很多不必要的东西)。
memcpy()
就足够了(并且可能比效率更高strcpy()
)。
memcpy()
是一个字符串操作-毕竟是在中声明的<string.h>
。
当人们说“strcpy()
是危险的,请strncpy()
改用”(或类似的类似说法strcat()
,但我将在strcpy()
这里使用)作为重点时,它们表示没有界限strcpy()
。因此,过长的字符串将导致缓冲区溢出。他们是正确的。strncpy()
在这种情况下使用将防止缓冲区溢出。
我觉得这strncpy()
确实不能解决错误:它解决了一个好的程序员可以轻松避免的问题。
作为C程序员,在尝试复制字符串之前,您必须知道目标大小。那是在strncpy()
和strlcpy()
的最后一个参数中:您可以为它们提供该大小。复制字符串之前,您还可以知道源大小。然后,如果目的地不够大,请不要致电strcpy()
。重新分配缓冲区,或执行其他操作。
为什么我不喜欢 strncpy()
?
strncpy()
在大多数情况下是一个不好的解决方案:您的字符串将被截断而不会发出任何通知-我宁愿自己编写额外的代码来弄清楚这一点,然后采取我想采取的行动,而不是让某个函数来决定我该怎么办。strncpy()
效率很低。它写入目标缓冲区中的每个字节。您不需要'\0'
目的地末尾的那数千个。'\0'
如果目的地不够大,它不会写一个终止符。因此,无论如何您都必须这样做。这样做的复杂性不值得麻烦。现在,我们来strlcpy()
。strncpy()
所做的更改使它变得更好,但是我不确定strl*
凭单的特定行为是否存在:它们太具体了。您仍然必须知道目标大小。它比strncpy()
未必写入目标中的每个字节要有效得多。但这解决了一个可以通过以下方法解决的问题:*((char *)mempcpy(dst, src, n)) = 0;
。
我认为没有人这么说strlcpy()
或strlcat()
会导致安全问题,他们(和我)说的话可能会导致错误,例如,当您希望写入完整的字符串而不是其中的一部分时,我认为。
这里的主要问题是:要复制多少个字节?程序员必须知道这一点,如果他不知道,strncpy()
或者strlcpy()
不会救他。
strlcpy()
并且strlcat()
不是标准的,ISO C和POSIX都不是。因此,不可能在可移植程序中使用它们。实际上,strlcat()
有两种不同的变体:对于长度为0的极端情况,Solaris实现与其他实现不同。这使得它的实用性甚至比其他方面低。
n
在我的memcpy调用中是要写入的确切字节数,因此效率也不是什么大问题。
strlcpy
每次使用时都需要重新实现memcpy
,strlen
那也可以,但是为什么要在处停止strlcpy
,您也不需要任何memcpy
功能,因此可以逐个复制字节。在通常情况下,参考实现只循环一次数据,这对于大多数体系结构来说更好。但是,即使最好的实现使用strlen
+ memcpy
,也仍然没有理由不必一次又一次地重新实现安全的strcpy。
strlcpy
在好情况下(应为多数)只扫过输入字符串一次,在坏情况下只扫两次,而strlen
+memcpy
则总是扫过两次。如果在实践中有所作为,则另当别论。
我认为Ulrich和其他人认为这会给人一种错误的安全感。意外地将字符串截断可能会对代码的其他部分产生安全影响(例如,如果文件系统路径被截断,则程序可能未在预期的文件上执行操作)。
malware.exe.jpg
为malware.exe
。
有两个与使用strl函数有关的“问题”:
c1x标准草拟者和Drepper认为,程序员不会检查返回值。Drepper说我们应该以某种方式知道长度并使用memcpy并完全避免使用字符串函数。标准委员会认为,除非有另外声明,否则安全的strcpy应该在截断时返回非零值_TRUNCATE
。这个想法是人们更可能使用if(strncpy_s(...))。
有人认为,即使输入虚假数据,字符串函数也绝不会崩溃。这会影响标准功能,例如在正常情况下会断断续续的strlen。新标准将包括许多此类功能。这些检查当然会影响性能。
建议的标准函数的好处是,您可以知道strl函数遗漏了多少数据。
我不认为strlcpy
并且strlcat
被认为是不安全的,或者至少这不是为什么它们不包含在glibc中的原因-毕竟,glibc包含strncpy甚至strcpy。
他们得到的批评是据称他们效率低下,而不是没有安全感。
根据Damien Miller的《安全可移植性》论文:
strlcpy和strlcat API会正确检查目标缓冲区的边界,在所有情况下均以nul结尾,并返回源字符串的长度,从而可以检测到截断。该API已被大多数现代操作系统和许多独立软件包所采用,包括OpenBSD(起源于此),Sun Solaris,FreeBSD,NetBSD,Linux内核,rsync和GNOME项目。值得注意的例外是GNU标准C库glibc [12],其维护者坚定地拒绝包含这些改进的API,并对其进行标记 “效率极低的BSD废话”。[4],尽管有以前的证据表明它们在大多数情况下比它们替代的API更快[13]。结果,OpenBSD端口树中存在的100多个软件包维护着自己的strlcpy和/或strlcat替代品或等效的API,这不是理想的状态。
这就是为什么它们在glibc中不可用,但是在Linux上它们不可用并不是事实。它们可在Linux的libbsd中获得:
它们打包在Debian和Ubuntu和其他发行版中。您也可以只获取一份副本并在您的项目中使用-它是简短的,并得到许可:
安全性不是布尔值。C函数并非完全“安全”或“不安全”,“安全”或“不安全”。如果使用不正确,C中的简单分配操作可能会“不安全”。当程序员提供正确使用的必要保证时,可以安全地(安全地)使用strlcpy()和strlcat(),就像可以安全地使用strcpy()和strcat()一样。
所有这些C字符串函数(标准的和非标准的)的要点是它们使安全/安全使用变得容易的级别。安全使用strcpy()和strcat()并非易事;多年来,C程序员将其弄错的次数证明了这一点,随之而来的是令人讨厌的漏洞和利用。strlcpy()和strlcat()以及就此而言,安全使用strncpy()和strncat(),strncpy_s()和strncat_s()有点容易,但是仍然很简单。他们不安全/不安全吗?如果使用不当,只不过是memcpy()。
strlcpy
并且strlcat
会报告某种错误情况,那将是很好的。尽管您可以检查返回的长度以进行测试,但这并不明显。但是我认为这是一个小批评。“他们鼓励使用C字符串,所以它们是不好的”的说法很愚蠢。