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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2114896/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:13:23  来源:igfitidea点击:

Why are strlcpy and strlcat considered insecure?

csecuritystrncpystrlcpy

提问by Anonymous

I understand that strlcpyand strlcatwere designed as secure replacements for strncpyand strncat. However, some people are still of the opinion that they are insecure, and simply cause a different type of problem.

据我所知,strlcpystrlcat被设计为安全的替代品strncpystrncat。然而,有些人仍然认为他们不安全,只是导致不同类型的问题

Can someone give an example of how using strlcpyor strlcat(i.e. a function that alwaysnull terminates its strings) can lead to security problems?

有人可以举例说明使用strlcpyor strlcat(即始终为空终止其字符串的函数)如何导致安全问题?

Ulrich Drepper and James Antill state this is true, but never provide examples or clarify this point.

Ulrich Drepper 和 James Antill 表示这是真的,但从未提供示例或澄清这一点。

回答by AnT

Firstly, strlcpyhas never been intended as a secure version of strncpy(and strncpyhas never been intended as a secure version of strcpy). These two functions are totally unrelated. strncpyis a function that has no relation to C-strings (i.e. null-terminated strings) at all. The fact that it has the str...prefix in its name is just a historical blunder. The history and purpose of strncpyis well-known and well-documented. This is a function created for working with so called "fixed width" strings (not with C-strings) used in some historical versions of Unix file system. Some programmers today get confused by its name and assume that strncpyis somehow supposed to serve as limited-length C-string copying function (a "secure" sibling of strcpy), which in reality is complete nonsense and leads to bad programming practice. C standard library in its current form has no function for limited-length C-string copying whatsoever. This is where strlcpyfits in. strlcpyis indeed a true limited-length copying function created for working with C-strings. strlcpycorrectly does everything a limited-length copying function should do. The only criticism one can aim at it is that it is, regretfully, not standard.

首先,strlcpy从未打算作为 的安全版本strncpystrncpy也从未打算作为 的安全版本strcpy)。这两个功能完全不相关。strncpy是一个与 C 字符串(即以空字符结尾的字符串)完全没有关系的函数。str...它的名称中有前缀的事实只是一个历史错误。的历史和目的strncpy是众所周知的和有据可查的。这是一个为处理某些历史版本的 Unix 文件系统中使用的所谓“固定宽度”字符串(而不是 C 字符串)而创建的函数。今天的一些程序员被它的名字弄糊涂了,并假设它strncpy以某种方式应该作为有限长度的 C 字符串复制函数(一个“安全”兄弟strcpy),这实际上完全是胡说八道,会导致不良的编程习惯。当前形式的 C 标准库没有任何用于有限长度 C 字符串复制的功能。这就是strlcpy适合的地方。strlcpy确实是为使用 C 字符串而创建的真正的有限长度复制函数。strlcpy正确完成有限长度复制功能应该做的所有事情。唯一可以针对它的批评是,遗憾的是,它不是标准的。

Secondly, strncaton the other hand, is indeed a function that works with C-strings and performs a limited-length concatenation (it is indeed a "secure" sibling of strcat). In order to use this function properly the programmer has to take some special care, since the size parameter this function accepts is not really the size of the buffer that receives the result, but rather the size of its remaining part (also, the terminator character is counted implicitly). This could be confusing, since in order to tie that size to the size of the buffer, programmer has to remember to perform some additional calculations, which is often used to criticize the strncat. strlcattakes care of these issues, changing the interface so that no extra calculations are necessary (at least in the calling code). Again, the only basis I see one can criticise this on is that the function is not standard. Also, functions from strcatgroup is something you won't see in professional code very often due to the limited usability of the very idea of rescan-based string concatenation.

其次,strncat另一方面,确实是一个与 C 字符串一起工作并执行有限长度连接的函数(它确实是 的“安全”兄弟strcat)。为了正确使用这个函数,程序员必须特别小心,因为这个函数接受的大小参数实际上并不是接收结果的缓冲区的大小,而是其剩余部分的大小(还有终止符被隐式计算)。这可能会令人困惑,因为为了将该大小与缓冲区的大小联系起来,程序员必须记住执行一些额外的计算,这通常用于批评strncat.strlcat处理这些问题,更改接口以便不需要额外的计算(至少在调用代码中)。同样,我看到有人可以批评这一点的唯一依据是该功能不标准。此外,strcat由于基于重新扫描的字符串连接这一想法的可用性有限,因此您在专业代码中不会经常看到来自组的函数。

As for how these functions can lead to security problems... They simply can't. They can't lead to security problems in any greater degree than the C language itself can "lead to security problems". You see, for quite a while there was a strong sentiment out there that C++ language has to move in the direction of developing into some weird flavor of Java. This sentiment sometimes spills into the domain of C language as well, resulting in rather clueless and forced criticism of C language features and the features of C standard library. I suspect that we might be dealing with something like that in this case as well, although I surely hope things are not really that bad.

至于这些功能如何导致安全问题......他们根本不能。它们导致安全问题的程度不会超过 C 语言本身“导致安全问题”的程度。你看,在很长一段时间里,有一种强烈的情绪,即 C++ 语言必须朝着发展成某种奇怪的 Java 风格的方向发展。这种情绪有时也会蔓延到 C 语言的领域,导致对 C 语言特性和 C 标准库特性的无知和强行批评。我怀疑在这种情况下我们也可能会处理类似的事情,尽管我当然希望事情不是那么糟糕。

回答by caf

Ulrich's criticism is based on the idea that a string truncation that is not detected by the program can lead to security issues, through incorrect logic. Therefore, to be secure, you need to check for truncation. To do this for a string concatenation means that you are doing a check along the lines of this:

Ulrich 的批评是基于这样一种想法,即程序未检测到的字符串截断可能会通过不正确的逻辑导致安全问题。因此,为了安全起见,您需要检查截断。对字符串连接执行此操作意味着您正在按照以下方式进行检查:

if (destlen + sourcelen > dest_maxlen)
{
    /* Bug out */
}

Now, strlcatdoes effectively do this check, if the programmer remembers to check the result - so you canuse it safely:

现在,strlcat如果程序员记得检查结果,确实可以有效地执行此检查 - 这样您就可以安全地使用它:

if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
    /* Bug out */
}

Ulrich's point is that since you have to have destlenand sourcelenaround (or recalculate them, which is what strlcateffectively does), you might as well just use the more efficient memcpyanyway:

Ulrich 的观点是,由于您必须拥有destlensourcelen周围(或重新计算它们,这是strlcat有效的方法),因此您最好memcpy还是使用更有效的方法:

if (destlen + sourcelen > dest_maxlen)
{
    goto error_out;
}
memcpy(dest + destlen, source, sourcelen + 1);
destlen += sourcelen;

(In the above code, dest_maxlenis the maximum length of the string that can be stored in dest- one less than the size of the destbuffer. dest_bufferlenis the full size of the dest buffer).

(在上面的代码中,dest_maxlen是可以存储的字符串的最大长度dest- 比dest缓冲区的大小少一。 dest_bufferlen是 的完整大小dest buffer)。

回答by Alok Singhal

When people say, "strcpy()is dangerous, use strncpy()instead" (or similar statements about strcat()etc., but I am going to use strcpy()here as my focus), they mean that there is no bounds checking in strcpy(). Thus, an overly long string will result in buffer overruns. They are correct. Using strncpy()in this case will prevent buffer overruns.

当人们说“strcpy()是危险的,请strncpy()改用”(或关于strcat()等的类似陈述,但我将在strcpy()这里用作我的重点)时,他们的意思是没有边界检查strcpy()。因此,过长的字符串将导致缓冲区溢出。他们是正确的。strncpy()在这种情况下使用将防止缓冲区溢出。

I feel that strncpy()really doesn't fix bugs: it solves a problem that can be easily avoided by a good programmer.

我觉得这strncpy()真的不能修复错误:它解决了一个优秀程序员可以轻松避免的问题。

As a C programmer, you mustknow the destination size before you are trying to copy strings. That is the assumption in strncpy()and strlcpy()'s last parameters too: you supply that size to them. You can also know the source size before you copy strings. Then, if the destination is not big enough, don't call strcpy(). Either reallocate the buffer, or do something else.

作为 C 程序员,在尝试复制字符串之前,您必须知道目标大小。这也是strncpy()andstrlcpy()的最后一个参数中的假设:您向它们提供该大小。您还可以在复制字符串之前知道源大小。然后,如果目的地不够大,就不要调用strcpy()。要么重新分配缓冲区,要么做其他事情。

Why do I not like strncpy()?

为什么我不喜欢strncpy()

  • strncpy()is a bad solution in most cases: your string is going to be truncated without any notice—I would rather write extra code to figure this out myself and then take the course of action that I want to take, rather than let some function decide for me about what to do.
  • strncpy()is very inefficient. It writes to every byte in the destination buffer. You don't need those thousands of '\0'at the end of your destination.
  • It doesn't write a terminating '\0'if the destination is not big enough. So, you must do so yourself anyway. The complexity of doing this is not worth the trouble.
  • strncpy()在大多数情况下是一个糟糕的解决方案:您的字符串将在没有任何通知的情况下被截断 - 我宁愿编写额外的代码来解决这个问题,然后采取我想要采取的行动,而不是让某个函数决定我该怎么做。
  • strncpy()效率很低。它写入目标缓冲区中的每个字节。'\0'在目的地的尽头,您不需要那些成千上万的人。
  • '\0'如果目的地不够大,它不会写终止。所以,无论如何你必须自己这样做。这样做的复杂性不值得麻烦。

Now, we come to strlcpy(). The changes from strncpy()make it better, but I am not sure if the specific behavior of strl*warrants their existence: they are far too specific. You still have to know the destination size. It is more efficient than strncpy()because it doesn't necessarily write to every byte in the destination. But it solves a problem that can be solved by doing: *((char *)mempcpy(dst, src, n)) = 0;.

现在,我们来到strlcpy(). 的变化strncpy()使它变得更好,但我不确定 的特定行为是否strl*保证它们的存在:它们太具体了。您仍然必须知道目标大小。它比strncpy()因为它不一定写入目标中的每个字节更有效。但是它解决了一个可以通过做来解决的问题:*((char *)mempcpy(dst, src, n)) = 0;

I don't think anyone says that strlcpy()or strlcat()can lead to security issues, what they (and I) are saying that they can result in bugs, for example, when you expect the complete string to be written instead of a part of it.

我认为没有人会这样说strlcpy()strlcat()会导致安全问题,他们(和我)所说的会导致错误,例如,当您希望写入完整的字符串而不是其中的一部分时。

The main issue here is: how many bytes to copy? The programmer must know this and if he doesn't, strncpy()or strlcpy()won't save him.

这里的主要问题是:要复制多少字节?程序员必须知道这一点,如果他不知道,strncpy()或者strlcpy()不会救他。

strlcpy()and strlcat()are not standard, neither ISO C nor POSIX. So, their use in portable programs is impossible. In fact, strlcat()has two different variants: the Solaris implementation is different from the othersfor edge cases involving length 0. This makes it even less useful than otherwise.

strlcpy()并且strlcat()不是标准的,既不是 ISO C 也不是 POSIX。因此,它们在便携式程序中的使用是不可能的。事实上,它strlcat()有两种不同的变体:对于涉及长度为 0 的边缘情况,Solaris 实现与其他实现不同。这使得它比其他方式更没用。

回答by jamesdlin

I think Ulrich and others think it'll give a false sense of security. Accidentally truncating strings canhave security implications for other parts of the code (for example, if a file system path is truncated, the program might not be performing operations on the intended file).

我认为 Ulrich 和其他人认为这会给人一种虚假的安全感。意外截断字符串可能会对代码的其他部分产生安全影响(例如,如果文件系统路径被截断,程序可能不会对预期文件执行操作)。

回答by jamesdlin

There are two "problems" related to using strl functions:

使用 strl 函数有两个“问题”:

  1. You have to check return values to avoid truncation.
  1. 您必须检查返回值以避免截断。

The c1x standard draft writers and Drepper, argue that programmers won't check the return value. Drepper says we should somehow know the length and use memcpy and avoid string functions altogether, The standards committee argues that the secure strcpy should return nonzero on truncation unless otherwise stated by the _TRUNCATEflag. The idea is that people are more likely to use if(strncpy_s(...)).

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

  1. Cannot be used on non-strings.
  1. 不能用于非字符串。

Some people think that string functions should never crash even when fed bogus data. This affects standard functions such as strlen which in normal conditions will segfault. The new standard will include many such functions. The checks of course have a performance penalty.

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

The upside over the proposed standard functions is that you can know how much data you missed with strlfunctions.

建议的标准函数的好处是您可以知道使用strl函数丢失了多少数据。

回答by rsp

I don't think strlcpyand strlcatare consider insecureor it least it isn't the reason why they're not included in glibc - after all, glibc includes strncpy and even strcpy.

我不认为strlcpystrlcat是考虑不安全,或者至少这不是他们为什么不包含在glibc的原因-毕竟,glibc的包括strncpy()函数,甚至strcpy的。

The criticism they got was that they are allegedly inefficient, not insecure.

他们受到的批评是据称他们效率低下,而不是不安全

According to the Secure Portabilitypaper by Damien Miller:

根据Damien Miller的Secure Portability论文:

The strlcpy and strlcat API properly check the target buffer's bounds, nul-terminate in all cases and return the length of the source string, allowing detection of truncation. This API has been adopted by most modern operating systems and many standalone software packages, including OpenBSD (where it originated), Sun Solaris, FreeBSD, NetBSD, the Linux kernel, rsync and the GNOME project. The notable exception is the GNU standard C library, glibc [12], whose maintainer steadfastly refuses to include these improved APIs, labelling them “horribly inefficient BSD crap”[4], despite prior evidence that they are faster is most cases than the APIs they replace [13]. As a result, over 100 of the software packages present in the OpenBSD ports tree maintain their own strlcpy and/or strlcat replacements or equivalent APIs - not an ideal state of affairs.

strlcpy 和 strlcat API 正确检查目标缓冲区的边界,在所有情况下都终止并返回源字符串的长度,允许检测截断。该 API 已被大多数现代操作系统和许多独立软件包采用,包括 OpenBSD(它的起源地)、Sun Solaris、FreeBSD、NetBSD、Linux 内核、rsync 和 GNOME 项目。值得注意的例外是 GNU 标准 C 库 glibc [12],其维护者坚决拒绝包含这些改进的 API,将它们标记为 “极其低效的 BSD 垃圾”[4],尽管先前的证据表明它们在大多数情况下比它们替换的 API 更快 [13]。因此,OpenBSD 端口树中存在的 100 多个软件包维护自己的 strlcpy 和/或 strlcat 替代品或等效的 API - 这不是理想的情况。

That is why they are not available in glibc, but it is not true that they are not available on Linux. They are available on Linux in libbsd:

这就是为什么它们在 glibc 中不可用,但它们在 Linux 上不可用的原因是不正确的。它们在 Linux 上的 libbsd 中可用:

They're packaged in Debian and Ubuntu and other distros. You can also just grab a copy and use in your project - it's short and under a permissive license:

它们打包在 Debian 和 Ubuntu 以及其他发行版中。您也可以只获取一份副本并在您的项目中使用 - 它很短并且在许可协议下:

回答by rswindell

Security is not a boolean. C functions are not wholly "secure" or "insecure", "safe" or "unsafe". When used incorrectly, a simple assignment operation in C can be "insecure". strlcpy() and strlcat() may be used safely (securely) just as strcpy() and strcat() can be used safely when the programmer provides the necessary assurances of correct usage.

安全性不是布尔值。C 函数并非完全“安全”或“不安全”、“安全”或“不安全”。如果使用不当,C 中的简单赋值操作可能是“不安全的”。strlcpy() 和 strlcat() 可以安全地(安全地)使用,就像当程序员提供正确使用的必要保证时可以安全地使用 strcpy() 和 strcat() 一样。

The main point with all of these C string functions, standard and not-so-standard, is the level to which they make safe/secure usage easy. strcpy() and strcat() are not trivial to use safely; this is proven by the number of times that C programmers have gotten it wrong over the years and nasty vulnerabilities and exploits have ensued. strlcpy() and strlcat() and for that matter, strncpy() and strncat(), strncpy_s() and strncat_s(), are a biteasier to use safely, but still, non-trivial. Are they unsafe/insecure? No more than memcpy() is, when used incorrectly.

所有这些 C 字符串函数(标准的和不那么标准的)的要点是它们使安全/安全使用变得容易的程度。strcpy() 和 strcat() 安全使用并非易事;多年来,C 程序员犯错的次数以及令人讨厌的漏洞和漏洞利用的次数证明了这一点。是strlcpy()和strlcat(),并为此事,strncpy和strncat函数(),strncpy_s()和strncat_s(),是更容易安全地使用,不过,不平凡。他们不安全/不安全吗?如果使用不当,就不会超过 memcpy() 。