【问题标题】:GCC false positive warning on strncpy usage?GCC 对 strncpy 使用的误报警告?
【发布时间】:2021-06-29 23:22:59
【问题描述】:

关于 strncpy 警告的另一个问题。

代码如下:

#include <cstring>

extern char g_dest[16];
extern char g_src[16];

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
    return x;
}

g++ 8.3版编译:

$ g++ --version
g++ (GCC) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
strncpy-warning2.cxx: In function ‘char* mycopy()’:
strncpy-warning2.cxx:8:24: error: ‘char* strncpy(char*, const char*, size_t)’ output may be truncated copying 15 bytes from a string of length 15 [-Werror=stringop-truncation]
     char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
                ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors

警告可以通过以下任一方式解决:

  1. 使 g_src 比 g_dest 短:
$ cat strncpy-warning2.cxx
#include <cstring>

extern char g_dest[16];
extern char g_src[15];

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
    return x;
}
$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
$
  1. 或者在g_dest的末尾显式添加一个NUL终止符:
$ cat strncpy-warning2.cxx
#include <cstring>

extern char g_dest[16];
extern char g_src[16];

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 );
    g_dest[ sizeof( g_dest ) - 1 ] = '\0';
    return x;
}
$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
$
  1. 或者如果我将 g_src 设为指针而不是数组:
$ cat strncpy-warning2.cxx
#include <cstring>

extern char g_dest[16];
extern char* g_src2;

char* mycopy()
{
    char * x = strncpy ( g_dest, g_src2, sizeof ( g_dest ) - 1 );
    return x;
}
$ g++ -Wall -Wextra -Werror -O3 -c strncpy-warning2.cxx
$

我知道 GCC 正试图警告 strncpy 使用的潜在错误,但是,我不明白为什么我们不能在 2 个相同大小的数组上执行 strncpy?为什么警告信息是the output maybe truncated ? (即我找不到 output 可能是 truncated 的示例,除非 GCC 假定 g_src 不一定是有效的以 NUL 结尾的 c 字符串。)

通过谷歌搜索,我在 GCC bugzilla 中看到了类似的案例:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87028,但是,我不确定这是否相同,因为最后标记为 FIXED,但显然我的问题仍然存在GCC 10.2。

谢谢!

【问题讨论】:

  • strncpy(),尽管它的名字,不是设计用于字符串...char dst[4], src[4] = "foo"; strncpy(dst, src, 3); 将离开dst[3] 与它开始的任何随机垃圾,即dst 不是字符串。
  • 谢谢pmg。通过再次阅读 strncpy 的手册页,我明白了你的意思:“警告:如果 src 的前 n 个字节中没有空字节,则放在 dest 中的字符串不会以空值结尾。”。这就是为什么如果我在末尾明确添加 NUL,警告就消失了?
  • @LiuWei — 是的。 strncpy() 之后的赋值确保结果是一个字符串。
  • 感谢@Jonathan Leffler,看起来每当 strncpy 使用触发警告时,我至少应该在大多数用例中使用 memcpy。 (这对可读性和可维护性也更好,恕我直言)
  • 初学者认为strncpystrcpy 的“安全”版本,但事实并非如此,除非“安全”意味着完全不直观的东西。使用起来很棘手,应该避免使用。

标签: c++ c g++ gcc-warning


【解决方案1】:

最好的(最有效、最简单、最容易理解成功和错误情况的)是在源和目标大小相同且相当小的情况下使用 memcpy。 GCC 不会就此发出警告。

如果你只是想让 GCC 不抱怨你的 strncpy() 调用,即使它不安全,你可以将它括在括号中,如下所示:

char * x = (strncpy ( g_dest, g_src, sizeof ( g_dest ) - 1 ));

GCC 将其理解为“我知道自己在做什么,不要试图从自己身上拯救我。”

【讨论】:

  • 感谢约翰的两个提示!看起来memcpy 至少对于我的用例来说,即使不是 100%,也是更好的选择。
  • @LiuWei 不客气。如果答案解决了您的问题,您可以通过单击左侧的复选标记“接受”答案。欢迎使用 Stack Overflow。
  • 我试图接受@pmg 的评论作为答案,但看起来这是不可能的。所以我接受了这个。 :)
【解决方案2】:

传递给strncpy 的长度是要复制的最大字节数,包括可能的空终止符,而不是排除空终止符的“字符串长度”。使用sizeof g_dest,而不是sizeof ( g_dest ) - 1

如果g_src包含一个15个非空字符和一个空终止符的字符串,那么strncpy(g_dest, g_src, sizeof g_dest - 1);不会复制它;它只会复制 15 个字符而不复制空终止符。它也不会附加一个尚未存在并在长度内复制的空终止符(尽管它会将空值附加到给定的长度)。

这是编译器警告您的内容。 strncpy 应该传递缓冲区的完整长度,而不是“字符串长度”。

【讨论】:

【解决方案3】:

在这种情况下,我宁愿使用strcpy()。我尝试在其他项目上多次运行strncpy(),并且我认为它受到编译器的保护,因为当您在没有'\0' 运算符的情况下通过for-loop 创建字符串时。也许我错了,但这个理由对当时的我来说已经足够了。

【讨论】:

    猜你喜欢
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 2018-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多