https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92893

            Bug ID: 92893
           Summary: Unhelpful -Wstringop-overflow warning
           Product: gcc
           Version: 10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: sbergman at redhat dot com
  Target Milestone: ---

Recent GCC 10 trunk produces a -Wstringop-overflow warning that at least
gcc-c++-9.2.1-1.fc31.x86_64 does not produce.  Unfortunately the minimal
reproducer I came up with is still a bit lengthy:

> $ cat test.cc
> #include <cstring>
> 
> struct View {
>   View(char const * s): s_(s), n_(std::strlen(s)) {}
>   char const * s_;
>   std::size_t n_;
> };
> 
> template<typename T> struct Add {
>   static char * add(char * buf, T const &);
> };
> 
> template<int N> struct Add<char const[N]> {
>   static char * add(char * buf, char const s[N]) {
>     std::memcpy(buf, s, N);
>     return buf + N;
>   }
> };
> 
> template<> struct Add<View> {
>   static char * add(char * buf, View const & v) {
>     std::memcpy(buf, v.s_, v.n_);
>     return buf + v.n_;
>   }
> };
> 
> template<typename T1, typename T2> struct Cat {
>   Cat(T1 const & x, T2 const & y): x_(x), y_(y) {}
>   char * add(char * buf) const
>   { return Add<T2>::add(Add<T1>::add(buf, x_), y_); }
>   T1 const & x_;
>   T2 const & y_;
> };
> 
> template<typename T1, typename T2> struct Add<Cat<T1, T2>> {
>   static char * add(char * buf, Cat<T1, T2> const & c) { return c.add(buf); }
> };
> 
> Cat<char const[1], View> catA(char const(&x)[1], View const & y) {
>   return {x, y};
> }
> 
> Cat<Cat<char const[1], View>, char const[4]> catB(
>   Cat<char const[1], View> const & x, char const (&y)[4])
> {
>   return {x, y};
> }
> 
> struct Buf { char buf[1]; };
> 
> Buf * get();
> 
> struct Str {
>   template<typename T1, typename T2> Str(Cat<T1, T2> c)
>   { c.add(get()->buf); }
> };
> 
> void f(char const * p) { Str(catB(catA("", View(p)), "xxx")); }

> $ g++ -O2 -c test.cc
> In member function ‘char* Cat<T1, T2>::add(char*) const [with T1 = Cat<const 
> char [1], View>; T2 = const char [4]]’,
>     inlined from ‘Str::Str(Cat<T1, T2>) [with T1 = Cat<const char [1], View>; 
> T2 = const char [4]]’ at test.cc:55:10,
>     inlined from ‘void f(const char*)’ at test.cc:58:26:
> test.cc:15:16: warning: writing 4 bytes into a region of size 1 
> [-Wstringop-overflow=]
>    15 |     std::memcpy(buf, s, N);
>       |     ~~~~~~~~~~~^~~~~~~~~~~

The warning only appears with this specific case where catB takes a char[4],
not for other char[N].

Buff::buf is meant to be a trailing flexible array member.  In the original
code, it needs to stay that way for compatibility reasons, but also if it is
replaced with the canonical GCC-extended flexible array member syntax,

> struct Buf { char buf[0]; };

the warning remains (this time as "writing 4 bytes into a region of size 0").

At least in the original code, I found no range of code that could be wrapped
in

> #pragma GCC diagnostic push
> #pragma GCC diagnostic ignored "-Wstringop-overflow"
[...]
> #pragma GCC diagnostic push

short of disabling the warning completely.  But maybe there is a way to
annotate the call to c.add(get()->buf) in the Str constructor, to break the
connection that Buf::buf is nominally of size 1 there?

Reply via email to