strlcpy
和strlcat
被设计为strncpy
和strncat
的安全替代品。但是,有些人仍然认为它们不安全,只会导致另一种类型的问题。有人可以举一个使用
strlcpy
或strlcat
的示例(即始终为null终止的函数)的例子。 乌尔里希·德普珀(Ulrich Drepper)和詹姆斯·安提尔(James Antill)声明这是真的,但从未提供示例或澄清这一点。
#1 楼
首先,strlcpy
从未被用作strncpy
的安全版本(而strncpy
从未被用作strcpy
的安全版本)。这两个功能是完全无关的。 strncpy
是一个完全与C字符串(即以null终止的字符串)无关的函数。它的名称中带有str...
前缀的事实只是一个历史错误。 strncpy
的历史和用途是众所周知的且有据可查。这是一个功能,用于处理在某些历史版本的Unix文件系统中使用的所谓的“固定宽度”字符串(不适用于C字符串)。如今,有些程序员对它的名称感到困惑,并认为strncpy
某种程度上应该用作有限长度的C字符串复制功能(strcpy
的“安全”同级),实际上这完全是胡说八道,并导致不良的编程习惯。当前形式的C标准库没有任何功能可用于有限长度的C字符串复制。这就是strlcpy
所适合的地方。strlcpy
确实是为使用C字符串而创建的真正的有限长度复制功能。 strlcpy
正确执行了有限长度复制功能应做的所有事情。唯一可以针对的批评是遗憾的是,这不是标准。其次,另一方面,
strncat
确实是一个可与C字符串一起使用并执行有限长度的串联的函数(它确实是strcat
的“安全”同级)。为了正确使用此函数,程序员必须格外小心,因为此函数接受的size参数实际上并不是接收结果的缓冲区的大小,而是其剩余部分的大小(也包括终止符)被隐式计算)。这可能会造成混淆,因为为了将该大小与缓冲区的大小绑定在一起,程序员必须记住要执行一些额外的计算,这常常被用来批评strncat
。 strlcat
解决了这些问题,更改了接口,因此不需要额外的计算(至少在调用代码中)。同样,我看到的唯一可以批评这一点的依据是该功能不是标准的。同样,由于基于重新扫描的字符串串联的概念的可用性有限,因此在专业代码中,strcat
组的功能很少出现。 关于这些功能如何导致安全问题……它们根本不可能。它们不会导致比C语言本身“导致安全问题”更大的程度的安全问题。您会看到,很长一段时间以来,人们一直强烈认为C ++语言必须朝着发展成某种怪异的Java风格的方向发展。这种情绪有时也会蔓延到C语言的领域,从而导致对C语言功能和C标准库的功能相当无知和被迫批评。我怀疑在这种情况下,我们可能也会处理类似的问题,尽管我当然希望事情并没有那么糟糕。
#2 楼
Ulrich的批评基于这样的想法,即程序未检测到的字符串截断会通过错误的逻辑导致安全问题。因此,为了安全起见,您需要检查截断。对字符串连接执行此操作意味着您正在执行以下检查:if (destlen + sourcelen > dest_maxlen)
{
/* Bug out */
}
现在,如果程序员记得的话,
strlcat
可以有效地执行此检查检查结果-因此可以安全地使用它:if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
/* Bug out */
}
Ulrich的要点是,因为必须要有
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
的完整大小)。评论
Drepper代码的可读性很差。使用strlcpy(或任何str函数),我直接知道我正在复制0终止的C字符串。对于memcpy,它可以是任何类型的内存,在尝试理解代码时,我还有一个补充维度可以检查。我有一个旧版应用程序来调试所有用memcpy完成的地方,这是要纠正的真正的PITA。移植到专用的String函数后,它更容易阅读(并且更快,因为可以删除很多不必要的strlen)。
–帕特里克·施吕特(PatrickSchlüter)
2010-11-26 15:51
@domen:因为要复制的大小是已知的,所以memcpy()就足够了(并且可能比strcpy()更有效)。
–caf
2014年11月19日下午4:22
好吧,在字符串操作中使用它会令人困惑。据我所知,效率取决于实现方式,并且不是标准化的。
–domen
2014年11月19日在9:07
@domen:memcpy()是一个字符串操作-毕竟是在
–caf
2014年11月20日下午2:58
@domen我同意存在混淆的可能性,但是事实是,无论如何,使用C字符串还是可以处理原始内存。可以说,如果人们根本不再认为C具有“字符串”(与任何其他连续的内存块不同),那么我们所有人都会变得更好。
–mtraceur
18年2月2日,下午3:27
#3 楼
当人们说“strcpy()
很危险,请改用strncpy()
”(或类似的关于strcat()
的声明等,但我将在这里使用strcpy()
作为我的重点)时,它们表示strcpy()
中没有边界检查。因此,字符串过长将导致缓冲区溢出。他们是正确的。在这种情况下使用strncpy()
将防止缓冲区溢出。 我觉得
strncpy()
确实不能解决错误:它解决了一个好的程序员可以轻松避免的问题。作为C程序员,您必须了解尝试复制字符串之前的目标大小。这也是
strncpy()
和strlcpy()
的最后一个参数中的假设:您可以为它们提供该大小。复制字符串之前,您还可以知道源大小。然后,如果目的地不够大,请不要致电strcpy()
。重新分配缓冲区或执行其他操作。为什么我不喜欢
strncpy()
?strncpy()
在大多数情况下是一个不好的解决方案:您的字符串将被截断而不会发出任何通知-我宁愿自己编写额外的代码来弄清楚这一点,然后采取我想采取的行动,而不是让某个函数为我决定要做什么。strncpy()
效率很低。它写入目标缓冲区中的每个字节。您不需要目的地末尾的那几千个''strlcpy()
'
'
。如果目的地不够大,它不会写一个终止的
strncpy()
。因此,无论如何您都必须这样做。这样做的复杂性不值得麻烦。现在,我们来到
strl*
。从strncpy()
所做的更改使它变得更好,但是我不确定*((char *)mempcpy(dst, src, n)) = 0;
的特定行为是否可以保证它们的存在:它们过于具体。您仍然必须知道目标大小。它比strlcpy()
更有效,因为它不一定要写入目标中的每个字节。但这解决了一个可以解决的问题:strlcat()
。我认为没有人说
strncpy()
或strlcpy()
会导致安全问题,他们(和我)都在说他们可以例如,当您希望写入完整的字符串而不是字符串的一部分时,会导致错误。这里的主要问题是:要复制多少字节?程序员必须知道这一点,如果他不知道,则
strlcpy()
或strlcat()
不会救他。strlcat()
和q4312079q不是标准的,ISO C和POSIX都不是。因此,不可能在可移植程序中使用它们。实际上,q4312079q具有两个不同的变体:对于长度为0的边缘情况,Solaris实现与其他实现不同。这使得它的使用性比其他方面要小。评论
在许多体系结构上,strlcpy比memcpy更快,尤其是如果memcpy复制不必要的尾随数据时。 strlcpy还会返回丢失的数据量,这可能使您可以更快地恢复并减少代码量。
–user14554
2010年1月22日,下午5:15
@jbcreix:我的意思是应该没有数据丢失,并且memcpy调用中的n是要写入的确切字节数,因此效率也不是什么大问题。
– Alok Singhal
2010-1-22在11:06
那你怎么得到那个n?您可以事先知道的唯一n是缓冲区大小。当然,如果您建议每次使用memcpy和strlen都需要重新实现strlcpy时也可以,但是为什么要停止在strlcpy上,也不需要memcpy函数,您可以逐个复制字节。在正常情况下,参考实现只循环一次数据,这对于大多数体系结构来说更好。但是,即使最好的实现使用strlen + memcpy,也仍然没有理由不必一次又一次地重新实现安全的strcpy。
–user14554
2010-1-22在11:30
Alok,使用strlcpy时,只在好情况下(应为多数)扫过输入字符串一次,在坏情况下扫过输入字符串两次,而使用strlen + memcpy总是将输入遍历两次。如果在实践中有所作为,则另当别论。
–帕特里克·施吕特(PatrickSchlüter)
2010-11-26 15:44
“ *((char *)mempcpy(dst,src,n))= 0;” -正确-是,显然正确,否。无法通过代码审查...
–mattnz
13年7月9日在1:48
#4 楼
我认为Ulrich和其他人认为这会给人一种错误的安全感。意外地将字符串截断可能会对代码的其他部分产生安全影响(例如,如果文件系统路径被截断,则程序可能未在预期的文件上执行操作)。评论
例如,电子邮件客户端可能会将电子邮件附件的文件名从恶意软件.exe.jpg截断为恶意软件.exe。
–克里斯·彼得森
2011年4月2日,0:33
@ChrisPeterson这就是为什么优秀的开发人员总是检查返回值的原因,对于strl *函数,要知道数据是否被截断并采取相应的措施。
–汤姆·林特(Tom Lint)
14年7月14日在11:34
“乌尔里希(Ulrich)和其他人认为这会给人一种错误的安全感……。”-大声笑...同时,乌尔里希(Ulrich)和朋友们定期出现在BugTraq和Full Disclosure上,这是他们一次过的尝试。他们应该使用更安全的功能,并避免大多数问题。然后他们可以开始告诉其他人如何编写更安全的代码...
– jww
18-3-4在19:23
#5 楼
有两个与使用strl函数有关的“问题”:必须检查返回值
以避免被截断。
c1x标准草案作者和Drepper认为,程序员不会检查返回值。 Drepper说我们应该以某种方式知道长度并使用memcpy并完全避免使用字符串函数。标准委员会认为,除非
_TRUNCATE
标志另有说明,否则安全的strcpy应该在截断时返回非零值。这个想法是人们更可能使用if(strncpy_s(...))。不能在非字符串上使用。
某些人认为即使馈入虚假数据,字符串函数也绝不会崩溃。这会影响标准功能,例如在正常情况下会断断续续的strlen。新标准将包括许多此类功能。当然,检查会降低性能。
建议的标准函数的缺点是,您可以知道strl函数会丢失多少数据。
评论
请注意,strncpy_s不是strncpy的安全版本,而实际上是strlcpy的替代品。
–user14554
2010-1-22的5:29
#6 楼
我不认为strlcpy
和strlcat
是不安全的,或者至少这不是它们不包含在glibc中的原因-毕竟,glibc包含strncpy甚至strcpy。他们的批评
根据Damien Miller的安全可移植性论文:
strlcpy和strlcat API正确检查了目标缓冲区的边界,
在所有情况下均以nul-terminate并返回源字符串的长度,
允许检测截断。该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中可用:
https://libbsd.freedesktop.org/
它们被打包在Debian和Ubuntu和其他发行版中。您也可以只获取一份副本并在您的项目中使用-它是简短的并且在许可的许可下:
http://cvsweb.openbsd.org/cgi-bin/cvsweb/src /lib/libc/string/strlcpy.c?rev=1.11
#7 楼
安全性不是布尔值。 C函数并非完全“安全”或“不安全”,“安全”或“不安全”。如果使用不正确,C中的简单分配操作可能会“不安全”。当程序员提供正确使用的必要保证时,可以安全地(安全地)使用strlcpy()和strlcat(),就像安全地使用strcpy()和strcat()一样。要点所有这些C字符串函数,无论是标准的还是非标准的,都使它们易于使用安全级别。安全使用strcpy()和strcat()并非易事;多年来,C程序员将其弄错的次数证明了这一点,随之而来的是令人讨厌的漏洞和利用。 strlcpy()和strlcat()以及就此而言,安全使用strncpy()和strncat(),strncpy_s()和strncat_s()有点容易,但是仍然很简单。他们不安全/不安全吗?如果使用不当,只不过是memcpy()。
评论
我不同意。如果strlcpy和strlcat超出目标缓冲区大小限制,它们将报告某种错误情况,那将是很好的。尽管您可以检查返回的长度以进行测试,但这并不明显。但是我认为这是一个小批评。 “他们鼓励使用C字符串,所以它们是不好的”的说法很愚蠢。
–恶毒
2012年10月4日18:12
“这些功能如何导致安全问题” –之前我认为这里的问题是某些C函数比其他C函数更难正确使用。有些人错误地认为存在一个特殊的难度阈值,低于该阈值的功能是“安全的”,而高于该阈值的功能是“不安全的”。这些人通常还认为strcpy高于阈值,因此是“不安全的”,并且他们首选的字符串复制功能(无论是strlcpy,strcpy_s还是什至strncpy)都低于阈值,因此是“安全的”。
–史蒂夫·杰索普(Steve Jessop)
2012年11月19日11:37
有很多理由不喜欢strlcpy / strlcat,但是您没有任何陈述。 C ++和Java的讨论是无关紧要的。这个答案对问题的实质没有帮助。
–约翰·里普利(John Ripley)
13年3月11日在20:19
@约翰·里普利(John Ripley):首先,我并没有“陈述任何一个”,仅仅是因为我不知道有什么理由不喜欢strlcpy / strlcat。人们可能会“不喜欢”零终止字符串的一般概念,但这不是问题所在。如果您知道“有很多理由不喜欢strlcpy / strlcat”,那么您应该写自己的答案,而不是期望我能够读懂别人的想法。
– AnT
13 Mar 12 '13 at 0:05
@John Ripley:其次,这个问题专门针对strlcpy / strlcat的一些所谓的“安全问题”。尽管我相信我理解这是什么意思,但我个人拒绝将其视为传统C语言领域内的“安全问题”。我在回答中说过。
– AnT
13 Mar 12 '13 at 0:07