call to ‘__wmemcpy_chk_warn’: "wmemcpy called with length bigger than size of destination buffer"
Asked Answered
H

1

10

I have this snippet of code (lets name it 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;
}

GCC (version 12.2.1) called with -O1 and C++20 conformity (g++ problem.cpp -Werror -O1 -std=c++20) leads to this error:

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

Interestingly, I cannot trigger this error, either by using std::string (in contrast to std::wstring), using lower resize values, or anything below C++20 or O1.

To me, this snippet of code does not look suspicious. Unfortunately, I can't reproduce this with Godbolt. Maybe Godbolt uses another and/or not fortified glibc version? I'm using glibc version 2.36 on Gentoo Linux, but I have seen this on Ubuntu as well. However, before I suspect a bug in glibc or the compiler, I'm reaching out here, if this is in fact a false positive.

Any ideas?

EDIT: In the meantime, I could reproduce a similar error using Godbolt: https://godbolt.org/z/joPaYKK11

Handgun answered 9/3, 2023 at 19:10 Comment(7)
I can reproduce locally on archlinux glibc2.37 g++12.21. with g++ -O -D_FORTIFY_SOURCE=2 -std=c++20Discard
regarding C++20, I think that is connected to the newly added constexpr to the resize functionHandgun
This seems like a rather fundamental problem with how glibc's fortify is implemented. Here is a reduced test case. The problem is, that the wmemcpy call is going to be invalid if called and the compiler knows this, because it sees the length being set. However, in reality g, which is invisible, guarantees that the if condition is not satisfied. But glibc uses __attribute__(warning) whenever the object sizes and length argument are known at compile time to not match. The attribute produces a warning whenever the code is not eliminated by optimization.Ogpu
In your actual code f() is basically what you get after inlining of the str constructors, except that there is one nested function call which isn't inlined (g in the reduced example) and so its effect unknown to the compiler.Ogpu
@Ogpu I just ran into the same problem. You seem to understand the problem better than I do. Can you make a bug report with your minimal example? I am not quite sure if it is better to address glibc or libstdc++.Triple
@BenjaminBuch It should probably address glibc, but I don't think they can do much about this. It comes down to there being a call to memcpy that is guaranteed to have UB if called, but is not actually ever evaluated. The compiler fails however to notice this and so the warning is still emitted. In general, a warning in this case doesn't seem unreasonable and an acceptable false positive. I guess one could also ask libstdc++/gcc to optimize the code so that everything gets inlined and the compiler will notice the memcpy never being evaluated, but the warning isn't really their problem.Ogpu
I filed gcc.gnu.org/bugzilla/show_bug.cgi?id=109299 to libstdc++ since a warning on normal usage of std::wstring is clearly a problem of the standard library implementation.Triple
T
2

This is a bug in the libstdc++.

The developers reacted very quickly and fixed the problem for 12.3.

Thanks to srohmen, KamilCuk and user17732522 for the helpful discussion in question and comments above!

The warning came from a branch of code that is never executed in the case that triggered the warning. The fix is to tell the compiler this by inserting __builtin_unreachable().

C++23 introduces std::unreachable which also has the task to tell the compiler that this branch in the code will never be executed. Note that it is not necessarily equivalent to GCC's __builtin_unreachable(), since std::unreachable only guarantees undefined behavior! In practice you have to test whether std::unreachable solves a concrete problem or whether you have to fall back on a compiler-specific well-defined builtin.

Triple answered 28/3, 2023 at 9:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.