This code (reduced from the libstdc++) produces spurious -Wstringop-overflow diagnostics since r11-3827 when compiled with -Wsystem-headers: #include <iostream> template class std::basic_iostream<char>; In file included from /usr/include/c++/10/bits/nested_exception.h:40, from /usr/include/c++/10/exception:148, from /usr/include/c++/10/ios:39, from /usr/include/c++/10/ostream:38, from /usr/include/c++/10/iostream:39, from inst.cc:1: In member function ‘void std::basic_ios<_CharT, _Traits>::swap(std::basic_ios<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’, inlined from ‘void std::basic_istream<_CharT, _Traits>::swap(std::basic_istream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:634:18, inlined from ‘void std::basic_iostream<_CharT, _Traits>::swap(std::basic_iostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:882:29, inlined from ‘std::basic_iostream<_CharT, _Traits>& std::basic_iostream<_CharT, _Traits>::operator=(std::basic_iostream<_CharT, _Traits>&&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:876:6: /usr/include/c++/10/bits/move.h:198:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] In file included from /usr/include/c++/10/iostream:40, from inst.cc:1: /usr/include/c++/10/istream: In member function ‘std::basic_iostream<_CharT, _Traits>& std::basic_iostream<_CharT, _Traits>::operator=(std::basic_iostream<_CharT, _Traits>&&) [with _CharT = char; _Traits = std::char_traits<char>]’: /usr/include/c++/10/istream:824:11: note: at offset 224 to object ‘std::basic_iostream<char>::<anonymous>’ with size 16 declared here In file included from /usr/include/c++/10/bits/nested_exception.h:40, from /usr/include/c++/10/exception:148, from /usr/include/c++/10/ios:39, from /usr/include/c++/10/ostream:38, from /usr/include/c++/10/iostream:39, from inst.cc:1: In member function ‘void std::basic_ios<_CharT, _Traits>::swap(std::basic_ios<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’, inlined from ‘void std::basic_istream<_CharT, _Traits>::swap(std::basic_istream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:634:18, inlined from ‘void std::basic_iostream<_CharT, _Traits>::swap(std::basic_iostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:882:29, inlined from ‘std::basic_iostream<_CharT, _Traits>& std::basic_iostream<_CharT, _Traits>::operator=(std::basic_iostream<_CharT, _Traits>&&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:876:6: /usr/include/c++/10/bits/move.h:199:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] In file included from /usr/include/c++/10/iostream:40, from inst.cc:1: /usr/include/c++/10/istream: In member function ‘std::basic_iostream<_CharT, _Traits>& std::basic_iostream<_CharT, _Traits>::operator=(std::basic_iostream<_CharT, _Traits>&&) [with _CharT = char; _Traits = std::char_traits<char>]’: /usr/include/c++/10/istream:824:11: note: at offset 224 to object ‘std::basic_iostream<char>::<anonymous>’ with size 16 declared here In file included from /usr/include/c++/10/bits/nested_exception.h:40, from /usr/include/c++/10/exception:148, from /usr/include/c++/10/ios:39, from /usr/include/c++/10/ostream:38, from /usr/include/c++/10/iostream:39, from inst.cc:1: In member function ‘void std::basic_ios<_CharT, _Traits>::swap(std::basic_ios<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’, inlined from ‘void std::basic_istream<_CharT, _Traits>::swap(std::basic_istream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:634:18, inlined from ‘void std::basic_iostream<_CharT, _Traits>::swap(std::basic_iostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:882:29: /usr/include/c++/10/bits/move.h:198:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] In file included from /usr/include/c++/10/iostream:40, from inst.cc:1: /usr/include/c++/10/istream: In member function ‘void std::basic_iostream<_CharT, _Traits>::swap(std::basic_iostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’: /usr/include/c++/10/istream:824:11: note: at offset 224 to object ‘std::basic_iostream<char>::<anonymous>’ with size 16 declared here In file included from /usr/include/c++/10/bits/nested_exception.h:40, from /usr/include/c++/10/exception:148, from /usr/include/c++/10/ios:39, from /usr/include/c++/10/ostream:38, from /usr/include/c++/10/iostream:39, from inst.cc:1: In member function ‘void std::basic_ios<_CharT, _Traits>::swap(std::basic_ios<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’, inlined from ‘void std::basic_istream<_CharT, _Traits>::swap(std::basic_istream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:634:18, inlined from ‘void std::basic_iostream<_CharT, _Traits>::swap(std::basic_iostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’ at /usr/include/c++/10/istream:882:29: /usr/include/c++/10/bits/move.h:199:11: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] In file included from /usr/include/c++/10/iostream:40, from inst.cc:1: /usr/include/c++/10/istream: In member function ‘void std::basic_iostream<_CharT, _Traits>::swap(std::basic_iostream<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]’: /usr/include/c++/10/istream:824:11: note: at offset 224 to object ‘std::basic_iostream<char>::<anonymous>’ with size 16 declared here
Created attachment 49452 [details] preprocessed source, unreduced, gzipped
Reduced: #include <iostream> template void std::basic_iostream<char>::swap(basic_iostream&); The preprocessed code is no smaller, but this should only generate code for the problematic function, not the entire class and all its bases.
I can confirm the warning but I'm not sure the bug is in the middle end code. Let me CC Jason for his comments. The warning triggers for the MEM_REF below: MEM[(char &)_10 + 224] = _24; in the following GIMPLE ("type" is char). The destination of the access is this_3(D)->D.45669 which is the basic_istream subobject. Its size reported by DECL_SIZE_UNIT() in component_ref_size() is 16. The initial offset (i.e., _10) is indeterminate but it's taken to be in the range bounded by the size of the object, or [0, 16]. Given that, the constant offset 224 is determined to be out of bounds. The change introduced in r11-3827 that triggers the warning is the assumption that unless determined otherwise, an indeterminate offset into an object must be bounded by the object's size. But sizeof (basic_istream) is 280, not 16. This is also what TYPE_SIZE_UNIT() reports, so the bug here is either in DECL_SIZE_UNIT(), or in using it in lieu of TYPE_SIZE_UNIT(). I'd expect the two to return the same result so this seems like a front end bug to me but I will defer to Jason. std::basic_iostream<char>::swap (struct basic_iostream * const this, struct basic_iostream & __rhs) { struct basic_istream * _1; struct basic_istream * _2; int (*) () * _7; long int _8; sizetype _9; struct basic_ios * _10; int (*) () * _11; long int _12; sizetype _13; struct basic_ios & _14; long int _15; long int _16; struct ios_base * _17; struct ios_base * _18; struct locale * _19; struct locale * _20; struct basic_ostream * _21; struct basic_ostream * _22; char _23; char _24; bool _25; bool _26; <bb 2> [local count: 1073741824]: _1 = &this_3(D)->D.45669; _2 = &__rhs_4(D)->D.45669; _7 = MEM[(struct basic_istream *)this_3(D)]._vptr.basic_istream; _8 = MEM[(long int *)_7 + -24B]; _9 = (sizetype) _8; _10 = _1 + _9; _11 = MEM[(struct basic_istream *)__rhs_4(D)]._vptr.basic_istream; _12 = MEM[(long int *)_11 + -24B]; _13 = (sizetype) _12; _14 = _2 + _13; _17 = &_10->D.42253; _18 = &_14->D.42253; std::ios_base::_M_swap (_17, _18); _19 = &_10->D.42253._M_ios_locale; std::basic_ios<char>::_M_cache_locale (_10, _19); _20 = &_14->D.42253._M_ios_locale; std::basic_ios<char>::_M_cache_locale (_14, _20); _21 = MEM[(struct basic_ostream * &)_10 + 216]; _22 = MEM[(struct basic_ostream * &)_14 + 216]; MEM[(struct basic_ostream * &)_10 + 216] = _22; MEM[(struct basic_ostream * &)_14 + 216] = _21; _23 = MEM[(type &)_10 + 224]; <<< -Wstringop-overflow _24 = MEM[(type &)_14 + 224]; MEM[(char &)_10 + 224] = _24; MEM[(char &)_14 + 224] = _23; _25 = MEM[(type &)_10 + 225]; _26 = MEM[(type &)_14 + 225]; MEM[(bool &)_10 + 225] = _26; MEM[(bool &)_14 + 225] = _25; _15 = MEM[(type &)this_3(D) + 8]; _16 = MEM[(type &)__rhs_4(D) + 8]; MEM[(long int &)this_3(D) + 8] = _16; MEM[(long int &)__rhs_4(D) + 8] = _15; return; }
Here's a small test case that causes a bogus false positive with patched GCC (https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558775.html). $ cat t.C && gcc -O2 -S -Wall t.C struct A { char a[32]; }; struct B: virtual A { }; struct C: B { }; struct D { B &b; D (B&); }; D::D (B &b): b (b) { } void f (void*); void g () { C c; D d (c); f (&d); } In constructor ‘D::D(B&)’, inlined from ‘void g()’ at t.C:18:9: t.C:11:14: warning: subscript 0 is outside array bounds of ‘B’ [-Warray-bounds] 11 | D::D (B &b): b (b) { } | ^~~~~ t.C: In function ‘void g()’: t.C:3:8: note: source object ‘C::<anonymous>’ of size 8 3 | struct C: B { }; | ^
(In reply to Martin Sebor from comment #3) > I can confirm the warning but I'm not sure the bug is in the middle end > code. Let me CC Jason for his comments. > > The warning triggers for the MEM_REF below: > > MEM[(char &)_10 + 224] = _24; > > in the following GIMPLE ("type" is char). The destination of the access is > this_3(D)->D.45669 which is the basic_istream subobject. Its size reported > by DECL_SIZE_UNIT() in component_ref_size() is 16. The initial offset > (i.e., _10) is indeterminate but it's taken to be in the range bounded by > the size of the object, or [0, 16]. Given that, the constant offset 224 is > determined to be out of bounds. What you're seeing is that the DECL_SIZE of the base subobject excludes any virtual bases, because they are laid out in the most derived class, while the TYPE_SIZE of the base type includes them. CLASSTYPE_SIZE of the base type should match the DECL_SIZE. It's definitely OK for a program to refer to a virtual base through any of the derived classes. > The change introduced in r11-3827 that triggers the warning is the assumption > that unless determined otherwise, an indeterminate offset into an object must > be bounded by the object's size. So this assumption is invalid for base subobjects of types with virtual bases; such an offset is only bounded by the most-derived object's size.
*** Bug 98083 has been marked as a duplicate of this bug. ***
The master branch has been updated by Martin Sebor <msebor@gcc.gnu.org>: https://gcc.gnu.org/g:b76f83e3859f738809d3aa8bd9dc14e10fc40e24 commit r11-5628-gb76f83e3859f738809d3aa8bd9dc14e10fc40e24 Author: Martin Sebor <msebor@redhat.com> Date: Tue Dec 1 15:10:30 2020 -0700 PR middle-end/97595 - bogus -Wstringop-overflow due to DECL_SIZE_UNIT underreporting field size gcc/ChangeLog: PR middle-end/97595 * tree.c (component_ref_size): Fail when DECL_SIZE != TYPE_SIZE. * tree.h (DECL_SIZE, TYPE_SIZE): Update comment. gcc/testsuite/ChangeLog: PR middle-end/97595 * g++.dg/warn/Warray-bounds-14.C: New test. * g++.dg/warn/Wstringop-overflow-6.C: New test.
Fixed in r11-5628.
See pr22488 for the underlying problem with the difference between DECL_SIZE and TYPE_SIZE of classes with virtual bases.