为什么GCC 9.1.0有时会抱怨对strncpy()的使用?

7

这是一个40行的MCVE(Minimal, Complete, Verifiable Example)或者接近最小代码量 — 源自一个拥有1675行的源码文件,原始文件包含32个头文件(其中大多数又包含了其他多个头文件), 使用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 */
}

编译

使用 GCC 9.1.0 进行编译时(在运行 macOS 10.14.5 Mojave 的 Mac 上,或在运行 RedHat 5.x 的 Linux 虚拟机上——不要问为什么!),使用选项 -DSUPPRESS_BUG 不会出现错误,但是如果使用选项 -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
$

当我使用GCC 8.3.0编译时,没有报告错误。
问题:
一个问题的两面:
1.为什么当使用-USUPPRESS_BUG编译代码时,GCC 9.1.0会抱怨使用strncpy()? 2.当使用-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;

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

由于SUPPRESS_BUG术语可以猜到,我倾向于认为这是GCC的bug,但我还是有点谨慎,尚未宣称它是一个bug。


有关原始代码的更多信息:函数本身长达540行;strncpy()块出现在函数的第170行左右;与name相对应的变量在函数中更深处使用,并用作一些函数调用的参数,其中一些函数将name作为参数并为该函数提供返回值。 这更符合-DSUPPRESS_BUG代码,但在“真实代码”中,该错误并没有被抑制。


在测试代码中,将 strncpy() 调用更改为使用 SERVERNAME_LEN + 1sizeof(name) 中的任何一个都可以消除警告。遗憾的是,在“真实代码”中,两者都无法消除警告(但错误会更改为 error: ' __builtin_strncpy' 指定边界129等于目标大小 [-Werror=stringop-truncation] 并指向第二个 strncpy() 而不是第一个)。我今天晚些时候将把它整合到问题中。 - Jonathan Leffler
顺便说一句:即使是gcc 8.1也会抱怨:https://www.godbolt.org/z/YyFJcL。7.3及更早版本则不会。 - Jabberwocky
@Jabberwocky:有趣—对我来说,如果使用-USUPPRESS_BUG编译测试代码(或没有定义SUPPRESS_BUG),GCC 7.3.0、8.2.0和8.3.0都不会抱怨。而且它们在处理“真正的代码”时也一样安静。这些都是我构建的编译器——我在 Stack Overflow 上记录了一个配方(在 Mac 上安装 GNU GCC),我在 Linux 上也使用基本相同的配方。 - Jonathan Leffler
我有一个问题:在处理过程中,这条消息是在哪个阶段生成的?当代码处于RTL时?SSA?通用? - alinsoar
2个回答

4
这是一个GCC bug,跟踪编号为PR88780。根据马丁的评论,此警告在GCC 8之前不存在。
GCC随附此已知bug,因为它不被认为是版本发布关键问题。
老实说,我不确定这是否是bug。重点是,已知存在误报情况。如果您想帮助GCC项目,可以在strncpy / Wstringop-truncation bugs中找到最合适的bug,并在那里发布您的示例。如果您进一步将其最小化(例如使用creduce),则会更有帮助;最小化编译字符串也将受到赞赏(我想这应该相当简单)。

2
几个与strncpy相关的编译警告在GCC 9.0中被发现并在这里这里报告。
其中一个是问题中提到的错误,似乎出现在文件string_fortified.h中:
/usr/include/bits/string_fortified.h:106:10: warning: ‘__builtin_strncpy’ output may be truncated copying 16 bytes from a string of length 16 [-Wstringop-truncation]
  106 |   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这个问题的最初回答是在2019年4月15日给出的:
感谢您的报告,由于GCC 9仍在开发中,我们在当前稳定版本的GCC 7.4或GCC 8.3中没有看到上述错误。我们感谢您提前通知,并将接受针对GCC 9修复问题的PR,但目前我们的目标编译器是稳定版的gcc。
因此,我认为这些错误可能是版本9和9.1不稳定的结果。希望当这些版本变得稳定时,它们将被消除。

1
GCC 9.1.0于2019年5月3日发布。我正在使用发布代码,而不是发布之前的最新版本。但有趣的是,有人注意到了似乎是同样的问题并抱怨了,但却被拒绝了。 - Jonathan Leffler
我有一个问题: 在处理过程中,这条消息是在哪个阶段生成的?当代码处于RTL、SSA还是Generic阶段? - alinsoar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接