为什么strlcpy和strlcat被认为是不安全的?

时间:2010-01-22 04:13:22

标签: c security strncpy strlcpy

我了解strlcpystrlcat被设计为strncpystrncat的安全替代品。但是,有些人仍然认为他们是insecure, and simply cause a different type of problem

有人可以举例说明如何使用strlcpystrlcat(即总是 null终止其字符串的函数)会导致安全问题吗?

Ulrich Drepper和James Antill表示这是事实,但绝不提供例子或澄清这一点。

8 个答案:

答案 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参数实际上不是接收结果的缓冲区的大小,而是它剩下部分的大小(也就是终结符)隐含地计算)。这可能会令人困惑,因为为了将该大小与缓冲区的大小联系起来,程序员必须记住执行一些额外的计算,这通常用于批评strncatstrlcat负责处理这些问题,更改界面以便不需要额外的计算(至少在调用代码中)。再一次,我看到一个可以批评这一点的唯一依据是该功能不是标准的。此外,由于基于重新扫描的字符串连接的概念有限,因此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的观点是,由于你必须destlensourcelen左右(或重新计算它们,这是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函数有两个“问题”:

  1. 您必须检查返回值 避免截断。
  2. c1x标准草稿编写者和Drepper认为程序员不会检查返回值。 Drepper说我们应该以某种方式知道长度并使用memcpy并完全避免字符串函数。标准委员会认为,除非_TRUNCATE标志另有说明,否则安全strcpy应在截断时返回非零值。这个想法是人们更有可能使用if(strncpy_s(...))。

    1. 不能用于非字符串。
    2. 有些人认为即使在输入虚假数据时,字符串函数也不会崩溃。这会影响标准功能,例如strlen,在正常情况下会发生段错误。新标准将包括许多此类功能。检查当然会有性能损失。

      建议的标准功能的优点是,您可以通过 strl 功能了解错过了多少数据。

答案 5 :(得分:3)

我不认为strlcpystrlcat被认为是不安全,或者至少它不是为什么它们不包含在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 */
相关问题