为什么 GCC 9.1.0 有时会抱怨 strncpy() 的这种用法?

Jon*_*ler 6 c gcc

这是一个 40 行的 MCVE(最小的、完整的、可验证的示例)——或接近最小的——从最初包含 32 个头文件的 1675 行源文件(其中大部分包含多个其他头文件——用gcc -H列表 464编译它)来自项目和系统的标题,其中许多是多次)。该文件是以前在没有警告的情况下编译的工作代码(GCC 8.3.0),但不是使用 GCC 9.1.0。所有结构、函数、类型、变量名称都已更改。

pf31.c

#include <string.h>

enum { SERVERNAME_LEN = 128 };

typedef struct ServerQueue
{
    char server_name[SERVERNAME_LEN + 1];
    struct ServerQueue *next;
} ServerQueue;

extern int function_under_test(char *servername);

#ifdef SUPPRESS_BUG
extern int function_using_name(char *name);
#endif /* SUPPRESS_BUG */

extern int GetServerQueue(const char *servername, ServerQueue *queue);

int
function_under_test(char *servername)
{
    ServerQueue queue;
    char name[SERVERNAME_LEN + 1];

    if (GetServerQueue(servername, &queue) != 0)
        return -1;
    char *name_in_queue = queue.server_name;

    if (name_in_queue)
        strncpy(name, name_in_queue, SERVERNAME_LEN);
    else
        strncpy(name, servername, SERVERNAME_LEN);
    name[SERVERNAME_LEN] = '\0';

#ifdef SUPPRESS_BUG
    return function_using_name(name);
#else
    return 0;
#endif /* SUPPRESS_BUG */
}
Run Code Online (Sandbox Code Playgroud)

汇编

当使用 GCC 9.1.0 编译时(在运行 macOS 10.14.5 Mojave 的 Mac 上,或在运行 RedHat 5.x 的 Linux VM 上——不要问!),使用选项-DSUPPRESS_BUGI get no error,但使用选项-USUPPRESS_BUG,我得到一个错误:

$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -DSUPPRESS_BUG  -c pf31.c
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -USUPPRESS_BUG  -c pf31.c
In file included from /usr/include/string.h:417,
                 from pf31.c:1:
pf31.c: In function ‘function_under_test’:
pf31.c:30:9: error: ‘__builtin_strncpy’ output may be truncated copying 128 bytes from a string of length 128 [-Werror=stringop-truncation]
   30 |         strncpy(name, name_in_queue, SERVERNAME_LEN);
      |         ^~~~~~~
cc1: all warnings being treated as errors
$
Run Code Online (Sandbox Code Playgroud)

当我使用 GCC 8.3.0 进行编译时,没有报告任何错误。

一个问题的两个方面:

  • 为什么 GCC 9.1.0strncpy()在编译代码时会抱怨使用-USUPPRESS_BUG
  • 为什么在编译代码时不抱怨-DSUPPRESS_BUG
    • 推论:有没有办法解决这个不需要的警告,它适用于较旧的 GCC 版本以及 9.1.0。我还没有找到。还有一个强烈的元素“我认为没有必要,因为这是strncpy()用来限制复制的数据量,这就是它的设计目的”。

另一个变种

我有另一个无错误的变体,改变了的签名function_under_test()- 这是一组差异:

11c11
< extern int function_under_test(char *servername);
---
> extern int function_under_test(char *servername, ServerQueue *queue);
20c20
< function_under_test(char *servername)
---
> function_under_test(char *servername, ServerQueue *queue)
22d21
<     ServerQueue queue;
25c24
<     if (GetServerQueue(servername, &queue) != 0)
---
>     if (GetServerQueue(servername, queue) != 0)
27c26
<     char *name_in_queue = queue.server_name;
---
>     char *name_in_queue = queue->server_name;
Run Code Online (Sandbox Code Playgroud)

无论是否SUPPRESS_BUG定义,这都可以干净地编译。

正如您可以从SUPPRESS_BUG术语中猜到的那样,我倾向于认为这是 GCC 中的错误,但我对声称它是一个错误持谨慎态度。


关于原始代码的更多信息:函数本身有 540 行;该strncpy()块出现在函数中大约 170 行处;对应的变量name在多个函数调用中被进一步使用,其中一些name作为参数并为函数提供返回值。这更多地对应于-DSUPPRESS_BUG代码,除了在“真实代码”中,错误没有被抑制。

P.W*_*P.W 2

strncpy在 GCC 9.0 中发现了几个与 相关的编译警告,并在此处此处报告。

\n\n

其中之一是问题中提到的错误,该错误似乎出现在文件中string_fortified.h

\n\n
/usr/include/bits/string_fortified.h:106:10: warning: \xe2\x80\x98__builtin_strncpy\xe2\x80\x99 output may be truncated copying 16 bytes from a string of length 16 [-Wstringop-truncation]\n  106 |   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));\n      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n
Run Code Online (Sandbox Code Playgroud)\n\n

2019年4月15日对此的答复是:

\n\n
\n

感谢您的报告,但 GCC 9 仍在开发中。我们在当前稳定的 GCC 7.4 或 GCC 8.3 中没有看到上述错误。我们感谢您的提前通知,并将接受 PR 来修复 GCC 9 的问题,但目前我们的目标编译器是 gcc 稳定的。

\n
\n\n

所以我相信这些错误可能是由于版本 9 和 9.1 不是稳定版本造成的。希望当这些版本稳定后它们会被淘汰。

\n