调用‘__wmemcpy_chk_warn’: “wmemcpy使用的长度大于目标缓冲区大小”

10
我有这段代码(让我们称之为problem.cpp):
#include <string>

using str = std::wstring;
static str foo(str text = str())
{
    text.resize(4);
    return text;
}

int main()
{
    str a = foo();
    return 0;
}

使用带有-O1和C++20兼容性的GCC(版本12.2.1)调用(g++ problem.cpp -Werror -O1 -std=c++20)会导致以下错误:

In file included from /usr/include/features.h:490,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/x86_64-pc-linux-gnu/bits/os_defines.h:39,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/x86_64-pc-linux-gnu/bits/c++config.h:655,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/string:38,
                 from problem.cpp:1:
In function ‘wchar_t* wmemcpy(wchar_t*, const wchar_t*, size_t)’,
    inlined from ‘static constexpr std::char_traits<wchar_t>::char_type* std::char_traits<wchar_t>::copy(char_type*, const char_type*, std::size_t)’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/char_traits.h:558:16,
    inlined from ‘constexpr std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = wchar_t; _Traits = std::char_traits<wchar_t>; _Alloc = std::allocator<wchar_t>]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/basic_string.h:675:23,
    inlined from ‘str foo(str)’ at problem.cpp:7:9,
    inlined from ‘int main()’ at problem.cpp:12:14:
/usr/include/bits/wchar2.h:39:10: error: call to ‘__wmemcpy_chk_warn’ declared with attribute warning: wmemcpy called with length bigger than size of destination buffer [-Werror=attribute-warning]
   39 |   return __glibc_fortify_n (wmemcpy, __n, sizeof (wchar_t),
      |          ^~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors

有趣的是,我无法通过使用 std::string(与 std::wstring 相比)、使用较小的 resize 值或任何低于 C++20 或 O1 的东西来触发此错误。

对我而言,这段代码片段看起来并不可疑。不幸的是,我无法在 Godbolt 上复现此问题。也许 Godbolt 使用了另一个和/或未加强的 glibc 版本?我正在 Gentoo Linux 上使用 glibc 2.36,但我也在 Ubuntu 上看到过这种情况。然而,在怀疑 glibc 或编译器存在漏洞之前,我想在这里打听一下是否真的是误报。

有什么想法吗?

编辑:与此同时,我已经可以使用 Godbolt 复现类似的错误: https://godbolt.org/z/joPaYKK11


1
我可以在archlinux glibc2.37 g++12.21上本地复现,使用 g++ -O -D_FORTIFY_SOURCE=2 -std=c++20 命令。 - KamilCuk
4
这似乎是glibc的fortify实现方式中的一个根本性问题。这里是一个简化的测试用例。问题在于,如果调用wmemcpy,则会无效,编译器知道这一点,因为它看到了长度的设置。然而,在现实中,不可见的g保证if条件不满足。但是,每当对象大小和长度参数在编译时已知但不匹配时,glibc使用__attribute__(warning)。该属性会在代码未被优化消除时产生警告。 - user17732522
2
在您的实际代码中,f() 基本上是在 str 构造函数内联后得到的结果,除了有一个嵌套的函数调用没有被内联(在简化的示例中为 g),因此其效果对编译器来说是未知的。 - user17732522
@user17732522 我也遇到了同样的问题。你似乎比我更理解这个问题。你能否提供一个最小化的例子来报告这个错误?我不太确定是更好地处理glibc还是libstdc++。 - Benjamin Buch
1
我已经向libstdc++提交了https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109299,因为对`std::wstring`的正常使用发出警告显然是标准库实现中存在的问题。 - Benjamin Buch
显示剩余2条评论
1个回答

2

这是 libstdc++ 中的一个 bug。

开发人员非常迅速地做出了反应并为12.3修复了问题。 感谢 srohmen, KamilCukuser17732522 在上面的问题和评论中提供有用的讨论! 警告来自于在触发警告的情况下从未执行的代码分支。修复方法是通过插入 __builtin_unreachable() 来告诉编译器这一点。 C++23 引入了 std::unreachable,它也有任务告诉编译器该代码分支永远不会被执行。请注意,它不一定等同于 GCC 的 __builtin_unreachable(),因为 std::unreachable 仅保证未定义行为!实际上,您必须测试是否 std::unreachable 解决了具体问题,或者您必须回退到特定于编译器的明确定义内置。

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