Bug 54924 - Warn for std::string constructor with wrong size
Summary: Warn for std::string constructor with wrong size
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.7.2
: P3 enhancement
Target Milestone: ---
Assignee: Martin Sebor
URL:
Keywords: diagnostic
Depends on: 79214
Blocks:
  Show dependency treegraph
 
Reported: 2012-10-14 17:06 UTC by David Stone
Modified: 2021-12-15 21:30 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2012-10-14 00:00:00


Attachments
Patch to use __builtin_object_size in std::string (1.25 KB, patch)
2018-02-06 21:26 UTC, Jonathan Wakely
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description David Stone 2012-10-14 17:06:23 UTC
The constructor for std::string that takes an array of char and a size assumes that the array of char you pass in is at least as large as the size you specify. In other words, std::string str('0', 100) is undefined behavior. As I show in this example, the real issue can be much more subtle if escape characters are involved:

http://stackoverflow.com/questions/164168/how-do-you-construct-a-stdstring-with-an-embedded-null/12884464#12884464

It would be nice if gcc warned when the size specified in the constructor exceeds the size of the array passed as the first argument.
Comment 1 Jonathan Wakely 2012-10-14 17:44:17 UTC
(In reply to comment #0)
> In other words, std::string str('0', 100) is undefined behavior.

I assume you mean std::string str("0", 100)

We might be able to use http://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
Comment 2 David Stone 2012-10-14 18:40:33 UTC
Yeah, sorry, I meant the (char const *, size_t) overload, not the (size_t, char) overload.
Comment 3 David Stone 2016-07-30 01:41:31 UTC
Also filed against libc++ here: https://llvm.org/bugs/show_bug.cgi?id=28777
Comment 4 Jonathan Wakely 2016-07-30 21:12:08 UTC
This seems like a job for sanitizers.
Comment 5 Martin Sebor 2017-04-17 19:33:00 UTC
A warning should be possible once bug 79234 is implemented.
Comment 6 Martin Sebor 2017-04-20 19:11:03 UTC
As a heads up, with the patch for bug 79234 applied and with -Wsystem-headers explicitly specified GCC issues the warnings below (the -Warray-bounds is printed even without the patch).  Unfortunately, with the default -Wno-system-headers, warnings from std::string and the rest of libstdc++ headers are suppressed.

$ cat t.C && /build/gcc-79234/gcc/xg++ -B /build/gcc-79234/gcc -nostdinc++ -I /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu -I /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include -I /src/gcc/79234/libstdc++-v3/libsupc++ -I /src/gcc/79234/libstdc++-v3/include/backward -I /src/gcc/79234/libstdc++-v3/testsuite/util -L /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs -O2 -S -Wall -Wsystem-headers t.C

#include <string>

std::string s ("abc", 5);

In file included from /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/stl_algobase.h:66:0,
                 from /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:39,
                 from /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/string:40,
                 from t.C:1:
/build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/stl_iterator_base_funcs.h: In function ‘(static initializers for t.C)’:
/build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/stl_iterator_base_funcs.h:104:21: warning: array subscript is above array bounds [-Warray-bounds]
       return __last - __first;
              ~~~~~~~^~~~~~~~~
In file included from /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/string:40:0,
                 from t.C:1:
In static member function ‘static std::char_traits<char>::char_type* std::char_traits<char>::copy(std::char_traits<char>::char_type*, const char_type*, std::size_t)’,
    inlined from ‘static void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_S_copy(_CharT*, const _CharT*, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:324:21,
    inlined from ‘void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_M_construct(_InIterator, _InIterator, std::forward_iterator_tag) [with _FwdIterator = const char*; _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’,
    inlined from ‘void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_M_construct(_InIterator, _InIterator) [with _InIterator = const char*; _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:220:23,
    inlined from ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:486:21,
    inlined from ‘void __static_initialization_and_destruction_0(int, int)’ at t.C:3:24,
    inlined from ‘(static initializers for t.C)’ at t.C:3:25:
/build/gcc-79234/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:292:66: warning: ‘void* __builtin_memcpy(void*, const void*, long unsigned int)’ reading 5 bytes from a region of size 4 [-Wstringop-overflow=]
  return static_cast<char_type*>(__builtin_memcpy(__s1, __s2, __n));
                                                                  ^
Comment 7 Martin Sebor 2017-04-20 23:01:07 UTC
The patch for bug 79214 should take care of the -Wno-system-header problem.
Comment 8 Martin Sebor 2017-05-04 23:50:53 UTC
Author: msebor
Date: Thu May  4 23:50:21 2017
New Revision: 247622

URL: https://gcc.gnu.org/viewcvs?rev=247622&root=gcc&view=rev
Log:
PR libstdc++/54924 - Warn for std::string constructor with wrong size
PR middle-end/79234 - warn on past the end reads by library functions

gcc/ChangeLog:

	PR middle-end/79234
	* builtins.c (check_sizes): Adjust to handle reading past the end.
	Avoid printing excessive upper bound of ranges.  Use %E to print
	tree nodes instead of converting them to %wu.
	(expand_builtin_memchr): New function.
	(compute_dest_size): Rename...
	(compute_objsize): ...to this.
	(expand_builtin_memcpy): Adjust.
	(expand_builtin_mempcpy): Adjust.
	(expand_builtin_strcat): Adjust.
	(expand_builtin_strcpy): Adjust.
	(check_strncat_sizes): Adjust.
	(expand_builtin_strncat): Adjust.
	(expand_builtin_strncpy): Adjust and simplify.
	(expand_builtin_memset): Adjust.
	(expand_builtin_bzero): Adjust.
	(expand_builtin_memcmp): Adjust.
	(expand_builtin): Handle memcmp.
	(maybe_emit_chk_warning): Check strncat just once.

gcc/testsuite/ChangeLog:

	PR middle-end/79234
	* gcc.dg/builtin-stringop-chk-8.c: New test.
	* gcc.dg/builtin-stringop-chk-1.c: Adjust.
	* gcc.dg/builtin-stringop-chk-4.c: Same.
	* gcc.dg/builtin-strncat-chk-1.c: Same.
	* g++.dg/ext/strncpy-chk1.C: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
	* gcc.dg/out-of-bounds-1.c: Same.
	* gcc.dg/pr78138.c: Same.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same.
	* gfortran.dg/mvbits_7.f90: Same.


Added:
    trunk/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/builtins.c
    trunk/gcc/testsuite/ChangeLog
    trunk/gcc/testsuite/g++.dg/ext/strncpy-chk1.C
    trunk/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
    trunk/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
    trunk/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
    trunk/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c
    trunk/gcc/testsuite/gcc.dg/out-of-bounds-1.c
    trunk/gcc/testsuite/gcc.dg/pr78138.c
    trunk/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
    trunk/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c
    trunk/gcc/testsuite/gfortran.dg/mvbits_7.f90
Comment 9 Martin Sebor 2017-05-05 00:01:21 UTC
Sadly, even with pr79214 fixed and pr79234 implemented, -Wno-system-headers still somehow defeats the warning.
Comment 10 Jonathan Wakely 2017-05-05 08:16:47 UTC
I'm starting to think we should just make libstdc++ headers 100% warning-free and stop marking them as system headers with the pragmas. When they're installed under /usr they'd still be considered system headers though.
Comment 11 Marc Glisse 2017-05-05 08:36:54 UTC
(In reply to Jonathan Wakely from comment #10)
> I'm starting to think we should just make libstdc++ headers 100%
> warning-free and stop marking them as system headers with the pragmas. When
> they're installed under /usr they'd still be considered system headers
> though.

Invent #pragma not_system_header ? But being a system header is sometimes also used to enable extensions, which may still be useful for libstdc++ headers.
Comment 12 Jonathan Wakely 2017-05-05 11:10:54 UTC
Indeed, it's what allows us to use variadic templates in C++98 mode, for example. And I don't think there's any way to use __attribute__((__extension__)) on template parameter packs to do that differently.
Comment 13 Jonathan Wakely 2018-02-06 21:21:17 UTC
(In reply to Jonathan Wakely from comment #10)
> I'm starting to think we should just make libstdc++ headers 100%
> warning-free

We're closer to that now, but not in a position to stop marking our headers as system headers.
Comment 14 Jonathan Wakely 2018-02-06 21:26:45 UTC
Created attachment 43350 [details]
Patch to use __builtin_object_size in std::string

So it isn't lost, here's a prototype I was working on last year (which only helps if you use -Wsystem-headers).
Comment 15 Martin Sebor 2021-12-15 17:58:25 UTC
GCC 11 and 12 finally diagnose this problem even without -Wsystem-headers, albeit inconsistently.  At -O1 GCC 11 issues -Wstringop-overread:

In file included from /build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/string:40,
                 from pr54924.C:2:
In static member function ‘static std::char_traits<char>::char_type* std::char_traits<char>::copy(std::char_traits<char>::char_type*, const char_type*, std::size_t)’,
    inlined from ‘static void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_S_copy(_CharT*, const _CharT*, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:359:21,
    inlined from ‘static void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_S_copy_chars(_CharT*, const _CharT*, const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:406:16,
    inlined from ‘void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_M_construct(_InIterator, _InIterator, std::forward_iterator_tag) [with _FwdIterator = const char*; _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.tcc:225:25,
    inlined from ‘void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_M_construct_aux(_InIterator, _InIterator, std::__false_type) [with _InIterator = const char*; _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:255:23,
    inlined from ‘void std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_M_construct(_InIterator, _InIterator) [with _InIterator = const char*; _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:274:20,
    inlined from ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ at /build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:521:21,
    inlined from ‘void __static_initialization_and_destruction_0(int, int)’ at pr54924.C:4:24,
    inlined from ‘(static initializers for pr54924.C)’ at pr54924.C:4:25:
/build/gcc-11-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:409:56: warning: ‘void* __builtin_memcpy(void*, const void*, long unsigned int)’ reading 5 bytes from a region of size 4 [-Wstringop-overread]
  409 |         return static_cast<char_type*>(__builtin_memcpy(__s1, __s2, __n));
      |                                        ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~

As a result of g:b8f2efaed02e8b03d215d74e42d3707761772f64 GCC 12 doesn't issue -Wstringop-overread at any level but at -O2 it does issue -Warray-bounds:

In file included from /build/gcc-master/x86_64-pc-linux-gnu/libstdc++-v3/include/string:53,
                 from pr54924.C:2:
In constructor ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’,
    inlined from ‘void __static_initialization_and_destruction_0(int, int)’ at pr54924.C:4:24,
    inlined from ‘(static initializers for pr54924.C)’ at pr54924.C:4:25:
/build/gcc-master/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:620:21: warning: array subscript 5 is outside array bounds of ‘const char [4]’ [-Warray-bounds]
  620 |         _M_construct(__s, __s + __n, std::forward_iterator_tag());
      |         ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I'm not happy with how this has turned out but I'm also not sure how to improve things, so I'll resolve this as fixed.
Comment 16 Jonathan Wakely 2021-12-15 21:30:07 UTC
(In reply to Jonathan Wakely from comment #14)
> Created attachment 43350 [details]
> Patch to use __builtin_object_size in std::string
> 
> So it isn't lost, here's a prototype I was working on last year (which only
> helps if you use -Wsystem-headers).

Huh, I forgot about this patch and then reinvented that wheel:
https://gcc.gnu.org/pipermail/gcc-patches/2021-October/581376.html