标准库中的哪些函数必须(应该)避免?

发布于 2024-08-27 02:49:20 字数 121 浏览 9 评论 0 原文

我在 Stack Overflow 上读到,某些 C 函数已经“过时”或“应该避免”。您能给我一些此类功能的示例以及原因吗?

这些功能有哪些替代方案?

我们可以安全地使用它们吗?有什么好的做法吗?

I've read on Stack Overflow that some C functions are "obsolete" or "should be avoided". Can you please give me some examples of this kind of function and the reason why?

What alternatives to those functions exist?

Can we use them safely - any good practices?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

水波映月 2024-09-03 02:49:21

另请查看 Microsoft 的禁止的 API 列表。这些 API(包括此处已列出的许多 API)被 Microsoft 代码禁止使用,因为它们经常被滥用并导致安全问题。

您可能不同意所有这些,但它们都值得考虑。当某个 API 的滥用导致了许多安全漏洞时,他们将其添加到列表中。

Also check out Microsoft's list of banned APIs. These are APIs (including many already listed here) that are banned from Microsoft code because they are often misused and lead to security problems.

You may not agree with all of them, but they are all worth considering. They add an API to the list when its misuse has led to a number of security bugs.

热鲨 2024-09-03 02:49:21

安全地使用 scanf 是非常困难的。充分利用 scanf 可以避免缓冲区溢出,但在读取不适合请求类型的数字时,您仍然容易受到未定义行为的影响。在大多数情况下,fgets 后跟自解析(使用 sscanfstrchr 等)是更好的选择。

但我不会说“始终避免 scanf”。 scanf 有其用途。举个例子,假设您想要读取 10 个字节长的 char 数组中的用户输入。您想要删除尾随的换行符(如果有)。如果用户在换行符之前输入超过 9 个字符,您希望将前 9 个字符存储在缓冲区中并丢弃所有内容,直到下一个换行符。你可以这样做:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

一旦你习惯了这个习惯用法,它就会比以下内容更短并且在某些方面更干净:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}

It is very hard to use scanf safely. Good use of scanf can avoid buffer overflows, but you are still vulnerable to undefined behavior when reading numbers that don't fit in the requested type. In most cases, fgets followed by self-parsing (using sscanf, strchr, etc.) is a better option.

But I wouldn't say "avoid scanf all the time". scanf has its uses. As an example, let's say you want to read user input in a char array that's 10 bytes long. You want to remove the trailing newline, if any. If the user enters more than 9 characters before a newline, you want to store the first 9 characters in the buffer and discard everything until the next newline. You can do:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

Once you get used to this idiom, it's shorter and in some ways cleaner than:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}
空城缀染半城烟沙 2024-09-03 02:49:21

不要忘记 sprintf - 它是许多问题的根源。这是事实,因为替代方案 snprintf 有时具有不同的实现,这可能会使您的代码不可移植。

  1. linux: http://linux.die.net/man/3/snprintf

  2. Windows:http: //msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

在情况 1 (linux) 中,返回值是存储整个缓冲区所需的数据量(如果它小于给定缓冲区的大小,则输出被截断)

在情况 2 (windows) 中,返回值为负数以防输出被截断的数字。

一般来说,您应该避免以下函数:

  1. 缓冲区溢出安全(这里已经提到了很多函数)

  2. 线程安全/不可重入(例如 strtok)

在每个函数的手册中,您应该搜索关键字,例如:safe、sync、async、thread、buffer、bugs

Don't forget about sprintf - it is the cause of many problems. This is true because the alternative, snprintf has sometimes different implementations which can make you code unportable.

  1. linux: http://linux.die.net/man/3/snprintf

  2. windows: http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

In case 1 (linux) the return value is the amount of data needed to store the entire buffer (if it is smaller than the size of the given buffer then the output was truncated)

In case 2 (windows) the return value is a negative number in case the output is truncated.

Generally you should avoid functions that are not:

  1. buffer overflow safe (a lot of functions are already mentioned in here)

  2. thread safe/not reentrant (strtok for example)

In the manual of each functions you should search for keywords like: safe, sync, async, thread, buffer, bugs

话少情深 2024-09-03 02:49:21

几乎所有处理 NUL 终止字符串的函数都可能不安全。
如果您从外部世界接收数据并通过 str*() 函数对其进行操作,那么您将陷入灾难

Almost any function that deals with NUL terminated strings is potentially unsafe.
If you are receiving data from the outside world and manipulating it via the str*() functions then you set yourself up for catastrophe

陌生 2024-09-03 02:49:21

在所有字符串复制/移动场景中 - strcat()、strncat()、strcpy()、strncpy() 等 - 如果强制执行几个简单的启发式,事情就会好得多(更安全) :

    1. 在添加数据之前始终用 NUL 填充缓冲区。
    2. 将字符缓冲区声明为 [SIZE+1],并带有宏常量。

例如,给定:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

我们可以

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

相对安全地 使用如下代码:即使我们在编译时初始化了 Buffer,memset() 也应该出现在 strncpy() 之前,因为我们不知道在调用我们的函数之前其他代码放入了哪些垃圾。 strncpy() 会将复制的数据截断为“1234567890”,并且不会以 NUL 结尾。然而,由于我们已经用 NUL 填充了整个缓冲区 - sizeof(Buffer),而不是 BUFSIZE - 只要我们使用 BUFSIZE 限制写入,就保证最终会出现终止 NUL 的“超出范围”常量,而不是 sizeof(Buffer)。

Buffer 和 BUFSIZE 同样适用于 snprintf():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

尽管 snprintf() 专门只写入 BUFIZE-1 字符,后跟 NUL,但它可以安全地工作。因此,我们在 Buffer 末尾“浪费”了一个无关的 NUL 字节...我们防止了缓冲区溢出和未终止的字符串情况,而内存成本却相当小。

我对 strcat() 和 strncat() 的调用更为强硬:不要使用它们。安全地使用 strcat() 很困难,而且 strncat() 的 API 非常违反直觉,以至于正确使用它所需的努力会抵消任何好处。我建议使用以下插入项:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

创建一个 strcat() 插入项很诱人,但不是一个好主意:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

因为 target 可能是一个指针(因此 sizeof () 并没有返回我们需要的信息)。对于代码中的 strcat() 实例,我没有一个好的“通用”解决方案。

我经常遇到的“strFunc() 感知”程序员的一个问题是尝试使用 strlen() 来防止缓冲区溢出。如果保证内容以 NUL 结尾,那就没问题。否则,在到达要保护的“有问题”代码之前,strlen() 本身可能会导致缓冲区溢出错误(通常会导致分段冲突或其他核心转储情况)。

In all the string-copy/move scenarios - strcat(), strncat(), strcpy(), strncpy(), etc. - things go much better (safer) if a couple simple heuristics are enforced:

   1. Always NUL-fill your buffer(s) before adding data.
   2. Declare character-buffers as [SIZE+1], with a macro-constant.

For example, given:

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

we can use code like:

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

relatively safely. The memset() should appear before the strncpy(), even though we initialized Buffer at compile-time, because we don't know what garbage other code placed into it before our function was called. The strncpy() will truncate the copied data to "1234567890", and will not NUL-terminate it. However, since we have already NUL-filled the entire buffer - sizeof(Buffer), rather than BUFSIZE - there is guaranteed to be a final "out-of-scope" terminating NUL anyway, as long as we constrain our writes using the BUFSIZE constant, instead of sizeof(Buffer).

Buffer and BUFSIZE likewise work fine for snprintf():

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

Even though snprintf() specifically writes only BUFIZE-1 characters, followed by NUL, this works safely. So we "waste" an extraneous NUL byte at the end of Buffer...we prevent both buffer-overflow and unterminated string conditions, for a pretty small memory-cost.

My call on strcat() and strncat() is more hard-line: don't use them. It is difficult to use strcat() safely, and the API for strncat() is so counter-intuitive that the effort needed to use it properly negates any benefit. I propose the following drop-in:

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

It is tempting to create a strcat() drop-in, but not a good idea:

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

because target may be a pointer (thus sizeof() does not return the information we need). I don't have a good "universal" solution to instances of strcat() in your code.

A problem I frequently encounter from "strFunc()-aware" programmers is an attempt to protect against buffer-overflows by using strlen(). This is fine if the contents are guaranteed to be NUL-terminated. Otherwise, strlen() itself can cause a buffer-overrun error (usually leading to a segmentation violation or other core-dump situation), before you ever reach the "problematic" code you are trying to protect.

嘿咻 2024-09-03 02:49:21

atoi 不是线程安全的。根据手册页的建议,我使用 strtol 代替。

atoi is not thread safe. I use strtol instead, per recommendation from the man page.

焚却相思 2024-09-03 02:49:20

已弃用的函数
不安全
这种函数的一个完美示例是 gets(),因为没有办法告诉它目标缓冲区有多大。因此,任何使用 gets() 读取输入的程序都存在缓冲区溢出漏洞。出于类似的原因,应该使用 strncpy() 代替 strcpy()strncat() 代替 strcat()

还有一些例子包括 tmpfile()mktemp() 函数由于 覆盖临时文件的潜在安全问题被更安全的 mkstemp() 函数取代。

不可重入
其他示例包括 gethostbyaddr()gethostbyname() 是不可重入的(因此不能保证线程安全)并已被取代通过可重入 getaddrinfo()freeaddrinfo()

您可能会注意到这里的一种模式......缺乏安全性(可能是由于未能在签名中包含足够的信息来安全地实现它)或不可重入是弃用的常见来源。

过时、不可移植
其他一些函数只是被弃用,因为它们重复了功能并且不像其他变体那样可移植。例如,bzero() 已被弃用,取而代之的是 memset()

线程安全和重入
您在帖子中询问了有关线程安全和重入的问题。有一点细微的差别。如果函数不使用任何共享的可变状态,则该函数是可重入的。因此,例如,如果它需要的所有信息都传递到函数中,并且所需的任何缓冲区也传递到函数中(而不是由对该函数的所有调用共享),那么它是可重入的。这意味着不同的线程通过使用独立的参数,不会冒意外共享状态的风险。可重入是比线程安全更有力的保证。如果一个函数可以被多个线程同时使用,那么它就是线程安全的。如果满足以下条件,则函数是线程安全的:

  • 它是可重入的(即,它在调用之间不共享任何状态),或者:
  • 它是不可重入的,但它根据共享状态的需要使用同步/锁定。

一般来说,在 单一 UNIX 规范IEEE 1003.1(即“POSIX”),任何不能保证可重入的函数都不能保证线程安全。因此,换句话说,只有保证可重入的函数才可以在多线程应用程序中可移植地使用(无需外部锁定)。然而,这并不意味着这些标准的实现不能选择使不可重入函数成为线程安全的。例如,Linux 经常向不可重入函数添加同步,以增加线程安全性的保证(超出单一 UNIX 规范的保证)。

字符串(以及一般的内存缓冲区)
您还询问字符串/数组是否存在一些基本缺陷。有些人可能会认为情况确实如此,但我认为不,这种语言没有根本性的缺陷。 C 和 C++ 要求您单独传递数组的长度/容量(它不像其他一些语言那样是“.length”属性)。这本身并不是一个缺陷。任何 C 和 C++ 开发人员只需在需要时将长度作为参数传递即可编写正确的代码。问题在于,多个需要此信息的 API 未能将其指定为参数。或者假设将使用某个 MAX_BUFFER_SIZE 常量。此类 API 现在已被弃用,并被允许指定数组/缓冲区/字符串大小的替代 API 所取代。

Scanf(回答你的最后一个问题)
就我个人而言,我使用 C++ iostreams 库(std::cin、std::cout、<< 和 >> 运算符、std::getline、std::istringstream、std::ostringstream 等),所以我通常不处理这个问题。不过,如果我被迫使用纯 C,我个人会使用 fgetc() getchar()strtol()strtoul() 等并手动解析内容,因为我不太喜欢可变参数或格式字符串。也就是说,据我所知, [f]scanf 没有问题()[f]printf() 等只要您自己制作格式字符串,就永远不会传递任意格式字符串或允许将用户输入用作格式字符串,并且您可以使用 (如果适用)。 (请注意,应使用 snprintf() 代替 sprintf(),但这与未能指定目标的大小有关缓冲区而不是使用格式字符串)。我还应该指出,在 C++ 中, boost:: format 提供类似 printf 的格式,无需可变参数。

Deprecated Functions
Insecure
A perfect example of such a function is gets(), because there is no way to tell it how big the destination buffer is. Consequently, any program that reads input using gets() has a buffer overflow vulnerability. For similar reasons, one should use strncpy() in place of strcpy() and strncat() in place of strcat().

Yet some more examples include the tmpfile() and mktemp() function due to potential security issues with overwriting temporary files and which are superseded by the more secure mkstemp() function.

Non-Reentrant
Other examples include gethostbyaddr() and gethostbyname() which are non-reentrant (and, therefore, not guaranteed to be threadsafe) and have been superseded by the reentrant getaddrinfo() and freeaddrinfo().

You may be noticing a pattern here... either lack of security (possibly by failing to include enough information in the signature to possibly implement it securely) or non-reentrance are common sources of deprecation.

Outdated, Non-Portable
Some other functions simply become deprecated because they duplicate functionality and are not as portable as other variants. For example, bzero() is deprecated in favor of memset().

Thread Safety and Reentrance
You asked, in your post, about thread safety and reentrance. There is a slight difference. A function is reentrant if it does not use any shared, mutable state. So, for example, if all the information it needs is passed into the function, and any buffers needed are also passed into the function (rather than shared by all calls to the function), then it is reentrant. That means that different threads, by using independent parameters, do not risk accidentally sharing state. Reentrancy is a stronger guarantee than thread safety. A function is thread safe if it can be used by multiple threads concurrently. A function is thread safe if:

  • It is reentrant (i.e. it does not share any state between calls), or:
  • It is non-reentrant, but it uses synchronization/locking as needed for shared state.

In general, in the Single UNIX Specification and IEEE 1003.1 (i.e. "POSIX"), any function which is not guaranteed to be reentrant is not guaranteed to be thread safe. So, in other words, only functions which are guaranteed to be reentrant may be portably used in multithreaded applications (without external locking). That does not mean, however, that implementations of these standards cannot choose to make a non-reentrant function threadsafe. For example, Linux frequently adds synchronization to non-reentrant functions in order to add a guarantee (beyond that of the Single UNIX Specification) of threadsafety.

Strings (and Memory Buffers, in General)
You also asked if there is some fundamental flaw with strings/arrays. Some might argue that this is the case, but I would argue that no, there is no fundamental flaw in the language. C and C++ require you to pass the length/capacity of an array separately (it is not a ".length" property as in some other languages). This is not a flaw, per-se. Any C and C++ developer can write correct code simply by passing the length as a parameter where needed. The problem is that several APIs that required this information failed to specify it as a parameter. Or assumed that some MAX_BUFFER_SIZE constant would be used. Such APIs have now been deprecated and replaced by alternative APIs that allow the array/buffer/string sizes to be specified.

Scanf (In Answer to Your Last Question)
Personally, I use the C++ iostreams library (std::cin, std::cout, the << and >> operators, std::getline, std::istringstream, std::ostringstream, etc.), so I do not typically deal with that. If I were forced to use pure C, though, I would personally just use fgetc() or getchar() in combination with strtol(), strtoul(), etc. and parse things manually, since I'm not a huge fan of varargs or format strings. That said, to the best of my knowledge, there is no problem with [f]scanf(), [f]printf(), etc. so long as you craft the format strings yourself, you never pass arbitrary format strings or allow user input to be used as format strings, and you use the formatting macros defined in <inttypes.h> where appropriate. (Note, snprintf() should be used in place of sprintf(), but that has to do with failing to specify the size of the destination buffer and not the use of format strings). I should also point out that, in C++, boost::format provides printf-like formatting without varargs.

酒中人 2024-09-03 02:49:20

人们再次像咒语一样重复可笑的断言,即 str 函数的“n”版本是安全版本。

如果这就是他们的目的,那么他们总是会以空值终止字符串。

函数的“n”版本是为与固定长度字段(例如早期文件系统中的目录条目)一起使用而编写的,其中仅当字符串未填充该字段时才需要 nul 终止符。这也是为什么这些函数具有奇怪的副作用的原因,如果仅用作替换,这些副作用毫无意义地低效 - 以 strncpy() 为例:

如果s2指向的数组是a
短于 n 个字节的字符串,
空字节被附加到副本中
s1 指向的数组,直到 n
全部字节均已写入。

由于分配给处理文件名的缓冲区通常为 4k 字节,这可能会导致性能大幅下降。

如果您想要“所谓的”安全版本,那么获取 - 或编写您自己的 - strl 例程(strlcpy、strlcat 等),它们总是 nul 终止字符串并且没有副作用。请注意,尽管这些并不真正安全,因为它们可以默默地截断字符串 - 这很少是任何实际程序中的最佳操作方案。在某些情况下这是可以的,但在很多情况下这可能会导致灾难性的结果(例如打印医疗处方)。

Once again people are repeating, mantra-like, the ludicrous assertion that the "n" version of str functions are safe versions.

If that was what they were intended for then they would always null terminate the strings.

The "n" versions of the functions were written for use with fixed length fields (such as directory entries in early file systems) where the nul terminator is only required if the string does not fill the field. This is also the reason why the functions have strange side effects that are pointlessly inefficient if just used as replacements - take strncpy() for example:

If the array pointed to by s2 is a
string that is shorter than n bytes,
null bytes are appended to the copy in
the array pointed to by s1, until n
bytes in all are written.

As buffers allocated to handle filenames are typically 4kbytes this can lead to a massive deterioration in performance.

If you want "supposedly" safe versions then obtain - or write your own - strl routines (strlcpy, strlcat etc) which always nul terminate the strings and don't have side effects. Please note though that these aren't really safe as they can silently truncate the string - this is rarely the best course of action in any real-world program. There are occasions where this is OK but there are also many circumstances where it could lead to catastrophic results (e.g. printing out medical prescriptions).

愛上了 2024-09-03 02:49:20

这里的几个答案建议使用 strncat() 而不是 strcat();我建议也应该避免使用 strncat() (和 strncpy())。它存在一些问题,使其难以正确使用并导致错误:

  • strncat() 的长度参数与(但不完全准确 - 请参阅第三点)可容纳的最大字符数相关。被复制到目标而不是目标缓冲区的大小。这使得 strncat() 比应有的更难使用,特别是在将多个项目连接到目标的情况下。
  • 很难确定结果是否被截断(这可能重要也可能不重要),
  • 很容易出现相差一的错误。正如 C99 标准所述,“因此,s1 指向的数组中最终可以出现的最大字符数为 strlen(s1)+n+1”,类似于 strncat( s1, s2, n)

strncpy() 的调用也存在一个问题,可能会导致您尝试以直观方式使用它时出现错误 - 它不保证目的地是空终止的。为了确保您必须通过自己在缓冲区的最后一个位置放置 '\0' 来专门处理这种极端情况(至少在某些情况下)。

我建议使用 OpenBSD 的 strlcat()strlcpy() 之类的东西(尽管我知道有些人不喜欢这些函数;我相信它们更容易安全使用)比 strncat()/strncpy())。

以下是 Todd Miller 和 Theo de Raadt 对于 strncat()strncpy() 问题的一些看法:

strncpy()strncat() 用作 strcpy() 的安全版本时会遇到几个问题strcat()。这两个函数都以不同且不直观的方式处理 NUL 终止和长度参数,甚至让经验丰富的程序员感到困惑。它们也没有提供简单的方法来检测何时发生截断。 ...在所有这些问题中,由长度参数引起的混乱和 NUL 终止的相关问题是最重要的。当我们审核 OpenBSD 源代码树是否存在潜在安全漏洞时,我们发现 strncpy()strncat() 的滥用现象十分猖獗。虽然并非所有这些都会导致可利用的安全漏洞,但他们明确表示,在安全字符串操作中使用 strncpy()strncat() 的规则被广泛误解。< /p>

OpenBSD的安全审计发现这些功能的缺陷“猖獗”。与gets()不同,这些函数可以安全地使用,但在实践中却存在很多问题,因为接口混乱、不直观且难以正确使用。我知道微软也做了分析(虽然我不知道他们可能发布了多少数据),并因此禁止(或至少非常强烈地阻止 - “禁止”可能不是绝对的)使用 strncat()strncpy() (以及其他函数)。

一些包含更多信息的链接:

Several answers here suggest using strncat() over strcat(); I'd suggest that strncat() (and strncpy()) should also be avoided. It has problems that make it difficult to use correctly and lead to bugs:

  • the length parameter to strncat() is related to (but not quite exactly - see the 3rd point) the maximum number of characters that can be copied to the destination rather than the size of the destination buffer. This makes strncat() more difficult to use than it should be, particularly if multiple items will be concatenated to the destination.
  • it can be difficult to determine if the result was truncated (which may or may not be important)
  • it's easy to have an off-by-one error. As the C99 standard notes, "Thus, the maximum number of characters that can end up in the array pointed to by s1 is strlen(s1)+n+1" for a call that looks like strncat( s1, s2, n)

strncpy() also has an issue that can result in bugs you try to use it in an intuitive way - it doesn't guarantee that the destination is null terminated. To ensure that you have to make sure you specifically handle that corner case by dropping a '\0' in the buffer's last location yourself (at least in certain situations).

I'd suggest using something like OpenBSD's strlcat() and strlcpy() (though I know that some people dislike those functions; I believe they're far easier to use safely than strncat()/strncpy()).

Here's a little of what Todd Miller and Theo de Raadt had to say about problems with strncat() and strncpy():

There are several problems encountered when strncpy() and strncat() are used as safe versions of strcpy() and strcat(). Both functions deal with NUL-termination and the length parameter in different and non-intuitive ways that confuse even experienced programmers. They also provide no easy way to detect when truncation occurs. ... Of all these issues, the confusion caused by the length parameters and the related issue of NUL-termination are most important. When we audited the OpenBSD source tree for potential security holes we found rampant misuse of strncpy() and strncat(). While not all of these resulted in exploitable security holes, they made it clear that the rules for using strncpy() and strncat() in safe string operations are widely misunderstood.

OpenBSD's security audit found that bugs with these functions were "rampant". Unlike gets(), these functions can be used safely, but in practice there are a lot of problems because the interface is confusing, unintuitive and difficult to use correctly. I know that Microsoft has also done analysis (though I don't know how much of their data they may have published), and as a result have banned (or at least very strongly discouraged - the 'ban' might not be absolute) the use of strncat() and strncpy() (among other functions).

Some links with more information:

愁杀 2024-09-03 02:49:20

绝对不应该使用的标准库函数:

setjmp.h

  • setjmp()。与 longjmp() 一起,这些函数被广泛认为使用起来极其危险:它们会导致意大利面条式编程,它们会带来多种形式的未定义行为,它们可能会在程序环境中导致意外的副作用,例如影响存储在堆栈上的值。参考文献:MISRA-C:2012 规则 21.4,CERT C MSC22-C
  • longjmp()。请参阅setjmp()

stdio.h

  • gets()。该函数已从 C 语言中删除(按照 C11),因为按照设计它是不安全的。该函数在 C99 中已被标记为已过时。请改用fgets()。参考文献:ISO 9899:2011 K.3.5.4.1,另请参阅注释 404。

stdlib.h

  • atoi() 系列函数。它们没有错误处理,但只要发生错误就会调用未定义的行为。完全多余的函数可以用 strtol() 系列函数替换。参考文献:MISRA-C:2012 规则 21.7。

string.h

  • strncat()。有一个经常被误用的尴尬界面。这主要是一个多余的功能。另请参阅 strncpy() 的备注。
  • strncpy()。此函数的目的绝不是成为 strcpy() 的更安全版本。它的唯一目的始终是处理 Unix 系统上的古老字符串格式,并且将其包含在标准库中是一个已知的错误。此函数很危险,因为它可能会留下没有空终止的字符串,并且众所周知,程序员经常错误地使用它。参考文献:为什么 strlcpy 和 strlcat 被认为不安全?,还有更多详细说明如下:strcpy 危险吗?应该使用什么代替?

应谨慎使用的标准库函数:

assert.h

  • assert()。带有开销,通常不应在生产代码中使用。最好使用特定于应用程序的错误处理程序,它显示错误但不一定关闭整个程序。

signal.h

  • signal()。参考文献:MISRA-C:2012 规则 21.5,CERT C SIG32-C

stdarg.h

  • va_arg() 函数系列。 C 程序中存在变长函数几乎总是表明程序设计不佳。除非您有非常具体的要求,否则应避免使用。

stdio.h
一般来说,不建议将整个库用于生产代码,因为它存在许多行为定义不明确和类型安全性差的情况。

  • fflush()。非常适合用于输出流。如果用于输入流,则调用未定义的行为。
  • gets_s()。 C11 边界检查接口中包含 gets() 的安全版本。根据 C 标准建议,最好使用 fgets() 代替。参考文献:ISO 9899:2011 K.3.5.4.1。
  • printf() 系列函数。占用大量资源的函数会带来大量未定义的行为和较差的类型安全性。 sprintf() 也存在漏洞。在生产代码中应避免使用这些函数。参考文献:MISRA-C:2012 规则 21.6。
  • scanf() 函数系列。请参阅有关 printf() 的注释。此外,如果使用不当,scanf() 很容易出现缓冲区溢出。如果可能的话,首选使用fgets()。参考文献: CERT C INT05-C,MISRA-C:2012 规则 21.6。
  • tmpfile() 系列函数。伴随着各种漏洞问题。参考文献: CERT C FIO21-C

stdlib.h

  • malloc() 函数系列。非常适合在托管系统中使用,但要注意 C90 中的众所周知的问题,因此 不要投射结果malloc() 系列函数不应该在独立应用程序中使用。参考文献:MISRA-C:2012 规则 21.3。

    另请注意,如果您使用 realloc() 的结果覆盖旧指针,则 realloc() 是危险的。如果函数失败,就会造成泄漏。

  • 系统()。虽然会带来大量开销,而且虽然可移植,但通常最好使用系统特定的 API 函数。带有各种定义不明确的行为。参考文献:CERT C ENV33-C

string.h

  • strcat()。请参阅 strcpy() 的备注。
  • strcpy()。完全可以使用,除非要复制的数据大小未知或大于目标缓冲区。如果没有检查传入数据的大小,则可能会出现缓冲区溢出。这不是 strcpy() 本身的错误,而是调用应用程序的错误 - strcpy() 不安全主要是 微软创造的神话
  • strtok()。更改调用者字符串并使用内部状态变量,这可能使其在多线程环境中不安全。

Standard library functions that should never be used:

setjmp.h

  • setjmp(). Together with longjmp(), these functions are widely recogniced as incredibly dangerous to use: they lead to spaghetti programming, they come with numerous forms of undefined behavior, they can cause unintended side-effects in the program environment, such as affecting values stored on the stack. References: MISRA-C:2012 rule 21.4, CERT C MSC22-C.
  • longjmp(). See setjmp().

stdio.h

  • gets(). The function has been removed from the C language (as per C11), as it was unsafe as per design. The function was already flagged as obsolete in C99. Use fgets() instead. References: ISO 9899:2011 K.3.5.4.1, also see note 404.

stdlib.h

  • atoi() family of functions. These have no error handling but invoke undefined behavior whenever errors occur. Completely superfluous functions that can be replaced with the strtol() family of functions. References: MISRA-C:2012 rule 21.7.

string.h

  • strncat(). Has an awkward interface that are often misused. It is mostly a superfluous function. Also see remarks for strncpy().
  • strncpy(). The intention of this function was never to be a safer version of strcpy(). Its sole purpose was always to handle an ancient string format on Unix systems, and that it got included in the standard library is a known mistake. This function is dangerous because it may leave the string without null termination and programmers are known to often use it incorrectly. References: Why are strlcpy and strlcat considered insecure?, with a more detailed explanation here: Is strcpy dangerous and what should be used instead?.

Standard library functions that should be used with caution:

assert.h

  • assert(). Comes with overhead and should generally not be used in production code. It is better to use an application-specific error handler which displays errors but does not necessarily close down the whole program.

signal.h

stdarg.h

  • va_arg() family of functions. The presence of variable-length functions in a C program is almost always an indication of poor program design. Should be avoided unless you have very specific requirements.

stdio.h
Generally, this whole library is not recommended for production code, as it comes with numerous cases of poorly-defined behavior and poor type safety.

  • fflush(). Perfectly fine to use for output streams. Invokes undefined behavior if used for input streams.
  • gets_s(). Safe version of gets() included in C11 bounds-checking interface. It is preferred to use fgets() instead, as per C standard recommendation. References: ISO 9899:2011 K.3.5.4.1.
  • printf() family of functions. Resource heavy functions that come with lots of undefined behavior and poor type safety. sprintf() also has vulnerabilities. These functions should be avoided in production code. References: MISRA-C:2012 rule 21.6.
  • scanf() family of functions. See remarks about printf(). Also, - scanf() is vulnerable to buffer overruns if not used correctly. fgets() is preferred to use when possible. References: CERT C INT05-C, MISRA-C:2012 rule 21.6.
  • tmpfile() family of functions. Comes with various vulnerability issues. References: CERT C FIO21-C.

stdlib.h

  • malloc() family of functions. Perfectly fine to use in hosted systems, though be aware of well-known issues in C90 and therefore don't cast the result. The malloc() family of functions should never be used in freestanding applications. References: MISRA-C:2012 rule 21.3.

    Also note that realloc() is dangerous in case you overwrite the old pointer with the result of realloc(). In case the function fails, you create a leak.

  • system(). Comes with lots of overhead and although portable, it is often better to use system-specific API functions instead. Comes with various poorly-defined behavior. References: CERT C ENV33-C.

string.h

  • strcat(). See remarks for strcpy().
  • strcpy(). Perfectly fine to use, unless the size of the data to be copied is unknown or larger than the destination buffer. If no check of the incoming data size is done, there may be buffer overruns. Which is no fault of strcpy() itself, but of the calling application - that strcpy() is unsafe is mostly a myth created by Microsoft.
  • strtok(). Alters the caller string and uses internal state variables, which could make it unsafe in a multi-threaded environment.
暗喜 2024-09-03 02:49:20

有些人会声称应该避免使用 strcpystrcat,而使用 strncpystrncat。在我看来,这有点主观。

在处理用户输入时绝对应该避免它们——毫无疑问。

在远离用户的代码中,当您只知道缓冲区足够长时,strcpystrcat 可能会更高效,因为计算要传递给其表兄弟的 n 可能是多余的。

Some people would claim that strcpy and strcat should be avoided, in favor of strncpy and strncat. This is somewhat subjective, in my opinion.

They should definitely be avoided when dealing with user input - no doubt here.

In code "far" from the user, when you just know the buffers are long enough, strcpy and strcat may be a bit more efficient because computing the n to pass to their cousins may be superfluous.

初见你 2024-09-03 02:49:20

避免使用

  • strtok 进行多线程程序,因为它不是线程安全的。
  • gets 因为它可能导致缓冲区溢出

Avoid

  • strtok for multithreaded programs as its not thread-safe.
  • gets as it could cause buffer overflow
少女的英雄梦 2024-09-03 02:49:20

可能值得再次补充的是,strncpy() 并不是其名称所暗示的 strcpy() 的通用替代品。它是为不需要 nul 终止符的固定长度字段而设计的(它最初是为与 UNIX 目录条目一起使用而设计的,但对于加密密钥字段等内容也很有用)。

然而,使用 strncat() 替代 strcpy() 是很容易的:(

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

if 测试显然可以在常见情况,您知道 dest_size 绝对非零)。

It is probably worth adding again that strncpy() is not the general-purpose replacement for strcpy() that it's name might suggest. It is designed for fixed-length fields that don't need a nul-terminator (it was originally designed for use with UNIX directory entries, but can be useful for things like encryption key fields).

It is easy, however, to use strncat() as a replacement for strcpy():

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

(The if test can obviously be dropped in the common case, where you know that dest_size is definitely nonzero).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文