我了解strlcpy
和strlcat
被设计为strncpy
和strncat
的安全替代品。但是,有些人仍然认为他们是insecure, and simply cause a different type of problem。
有人可以举例说明如何使用strlcpy
或strlcat
(即总是 null终止其字符串的函数)会导致安全问题吗?
答案 0 :(得分:101)
首先,strlcpy
从未被视为strncpy
的安全版本(而strncpy
从未被视为strcpy
的安全版本)。这两个功能完全不相关。 strncpy
是一个与C字符串(即以空字符结尾的字符串)无关的函数。它名称中带有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标准库特征的相当无知和强制批评。我怀疑在这种情况下我们可能会处理类似的事情,尽管我当然希望事情并不那么糟糕。
答案 1 :(得分:30)
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
缓冲区的大小少一个。{{1} }是dest_bufferlen
)的完整大小。
答案 2 :(得分:20)
当人们说“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()
有两种不同的变体:the Solaris implementation is different from the others用于涉及长度为0的边缘情况。这使得它比其他情况更有用。
答案 3 :(得分:11)
我认为乌尔里希和其他人认为它会给人一种虚假的安全感。意外截断字符串 会对代码的其他部分产生安全隐患(例如,如果文件系统路径被截断,程序可能无法对目标文件执行操作)。
答案 4 :(得分:7)
使用strl函数有两个“问题”:
c1x标准草稿编写者和Drepper认为程序员不会检查返回值。 Drepper说我们应该以某种方式知道长度并使用memcpy并完全避免字符串函数。标准委员会认为,除非_TRUNCATE
标志另有说明,否则安全strcpy应在截断时返回非零值。这个想法是人们更有可能使用if(strncpy_s(...))。
有些人认为即使在输入虚假数据时,字符串函数也不会崩溃。这会影响标准功能,例如strlen,在正常情况下会发生段错误。新标准将包括许多此类功能。检查当然会有性能损失。
建议的标准功能的优点是,您可以通过 strl 功能了解错过了多少数据。
答案 5 :(得分:3)
我不认为strlcpy
和strlcat
被认为是不安全,或者至少它不是为什么它们不包含在glibc中的原因 - 毕竟, glibc包括strncpy甚至strcpy。
他们得到的批评是,据称他们效率低,不安全。
根据Damien Miller撰写的Secure Portability论文:
strlcpy和strlcat API正确检查目标缓冲区的边界, nul-terminate在所有情况下都返回源字符串的长度, 允许检测截断。此API已被大多数人采用 现代操作系统和许多独立软件包, 包括OpenBSD(起源于哪里),Sun Solaris,FreeBSD,NetBSD, Linux内核,rsync和GNOME项目。值得注意的例外 是GNU标准C库,glibc [12],其维护者 坚决拒绝包含这些改进的API,标记它们 尽管事先有证据证明他们存在“强烈的低效BSD废话” [4] 大多数情况比它们取代的API更快[13]。结果是, 超过100个OpenBSD端口树中的软件包 保持自己的strlcpy和/或strlcat替换或等效 API - 不是理想的事态。
这就是为什么它们在glibc中不可用,但它们在Linux上不可用是不正确的。它们在libbsd的Linux上可用:
它们包装在Debian和Ubuntu以及其他发行版中。您也可以在项目中获取副本并使用它 - 它很短并且在许可许可下:
答案 6 :(得分:0)
安全性不是布尔值。 C函数并非完全“安全”或“不安全”,“安全”或“不安全”。如果使用不正确,则C中的简单分配操作可能会“不安全”。当程序员提供正确使用的必要保证时,可以安全地(安全地)使用strlcpy()和strlcat(),就像安全地使用strcpy()和strcat()一样。
所有这些C字符串函数(标准的和非标准的)的要点是它们使安全/安全使用变得容易的级别。安全使用strcpy()和strcat()并非易事;多年来,C程序员将其弄错的次数证明了这一点,随之而来的是令人讨厌的漏洞和利用。 strlcpy()和strlcat()以及就此而言,strncpy()和strncat(),strncpy_s()和strncat_s()是安全使用的 bit 位,但仍然是不平凡的。他们不安全/不安全吗?如果使用不当,只不过是memcpy()。
答案 7 :(得分:0)
strlcpy
可能触发 SIGSEGV
,如果 src
不是 NUL
终止的。
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */