这是一个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 + 1
或sizeof(name)
中的任何一个都可以消除警告。遗憾的是,在“真实代码”中,两者都无法消除警告(但错误会更改为error: ' __builtin_strncpy' 指定边界129等于目标大小 [-Werror=stringop-truncation]
并指向第二个strncpy()
而不是第一个)。我今天晚些时候将把它整合到问题中。 - Jonathan Leffler-USUPPRESS_BUG
编译测试代码(或没有定义SUPPRESS_BUG
),GCC 7.3.0、8.2.0和8.3.0都不会抱怨。而且它们在处理“真正的代码”时也一样安静。这些都是我构建的编译器——我在 Stack Overflow 上记录了一个配方(在 Mac 上安装 GNU GCC),我在 Linux 上也使用基本相同的配方。 - Jonathan Leffler