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

            Bug ID: 116180
           Summary: False positive -Wstringop-overread when calling
                    std::strlen on pointer to static constexpr member
                    std::array containing contents of a non-null
                    terminated string followed by a null terminator
           Product: gcc
           Version: 14.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: tanelson at lcmail dot lcsc.edu
  Target Milestone: ---

Created attachment 58797
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=58797&action=edit
.ii output from g++ -v -save-temps -example.cpp -Wall

Some source code to trigger the warning is as follows:

#include<array>
#include<cstring>

using c_string_t = char const *;

template<c_string_t Str, int Len>
struct null_terminate_static_string {
        using backing_t = std::array<char, Len+1>;
        static constexpr backing_t backing = {
                [](){
                        backing_t init {};
                        using size_t = typename backing_t::size_type;
                        for(size_t i = 0; i < Len; ++i){
                                init[i] = Str[i];
                        }
                        return init;
                }()
        };
        static_assert(
                backing[Len] == '\0'
        );
        static constexpr c_string_t value = &(backing[0]);
};

int main(){
        static constexpr char non_null_terminated_char_array[] { 'a' };
        constexpr auto null_terminated_char_ptr =
null_terminate_static_string<non_null_terminated_char_array, 1>::value;
        static_assert(
                null_terminated_char_ptr[0] == 'a' &&
                null_terminated_char_ptr[1] == '\0'
        );
        return std::strlen(null_terminated_char_ptr);
}

https://godbolt.org/z/PojWKh6ax

When compiled with -Wall gives : 

example.cpp:27:116: warning: ‘strlen’ argument missing terminating nul
[-Wstringop-overread]
   27 |         constexpr auto null_terminated_char_ptr =
null_terminate_static_string<non_null_terminated_char_array, 1>::value;
      |                                                  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
example.cpp:9:36: note: referenced argument declared here
    9 |         static constexpr backing_t backing = {

___________________________

As you can see the string is null terminated as it passes asserts, additionally
you can see the intel assembly on godbolt that
null_terminate_static_string<...>::backing is null terminated with a zero
section. For the .s generated on my machine I have also:

movl   
$_ZN28null_terminate_static_stringIXadL_ZZ4mainE30non_null_terminated_char_arrayEELi1EE7backingE,
%edi
call    strlen
...
_ZN28null_terminate_static_stringIXadL_ZZ4mainE30non_null_terminated_char_arrayEELi1EE7backingE:
        .byte   97
        .zero   1
        .text
        .globl  main
        .type   main, @function

Where you can see 'a' (97) followed by a size 1 .zero section.

___________________________

-v output follows:

Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../configure --enable-languages=c++ --disable-multilib
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 14.1.0 (GCC)
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-shared-libgcc'
'-mtune=generic' '-march=x86-64' '-dumpdir' 'a-'
 /usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/cc1plus -E -quiet -v
-imultiarch x86_64-linux-gnu -D_GNU_SOURCE example.cpp -mtune=generic
-march=x86-64 -Wall -fpch-preprocess -o a-example.ii
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory
"/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../../../x86_64-pc-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../../../include/c++/14.1.0

/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../../../include/c++/14.1.0/x86_64-pc-linux-gnu

/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../../../include/c++/14.1.0/backward
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/include
 /usr/local/include
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/include-fixed/x86_64-linux-gnu
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-shared-libgcc'
'-mtune=generic' '-march=x86-64' '-dumpdir' 'a-'
 /usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/cc1plus -fpreprocessed
a-example.ii -quiet -dumpdir a- -dumpbase example.cpp -dumpbase-ext .cpp
-mtune=generic -march=x86-64 -Wall -version -o a-example.s
GNU C++17 (GCC) version 14.1.0 (x86_64-pc-linux-gnu)
        compiled by GNU C version 14.1.0, GMP version 6.2.1, MPFR version
4.1.0, MPC version 1.2.1, isl version none
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 9a7dcfae45e2d61f340191eea1397e8a
example.cpp: In function ‘int main()’:
example.cpp:27:116: warning: ‘strlen’ argument missing terminating nul
[-Wstringop-overread]
   27 |         constexpr auto null_terminated_char_ptr =
null_terminate_static_string<non_null_terminated_char_array, 1>::value;
      |                                                  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
example.cpp:9:36: note: referenced argument declared here
    9 |         static constexpr backing_t backing = {
      |                                    ^~~~~~~
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-shared-libgcc'
'-mtune=generic' '-march=x86-64' '-dumpdir' 'a-'
 as -v --64 -o a-example.o a-example.s
GNU assembler version 2.38 (x86_64-linux-gnu) using BFD version (GNU Binutils
for Ubuntu) 2.38
COMPILER_PATH=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/:/usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/:/usr/local/libexec/gcc/x86_64-pc-linux-gnu/:/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/:/usr/local/lib/gcc/x86_64-pc-linux-gnu/
LIBRARY_PATH=/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/:/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../../../lib64/:/lib/x86_64-linux-gnu/:/lib/../lib64/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib64/:/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-shared-libgcc'
'-mtune=generic' '-march=x86-64' '-dumpdir' 'a.'
 /usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/collect2 -plugin
/usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/liblto_plugin.so
-plugin-opt=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/14.1.0/lto-wrapper
-plugin-opt=-fresolution=a.res -plugin-opt=-pass-through=-lgcc_s
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc
-plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc
--eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/crt1.o /lib/x86_64-linux-gnu/crti.o
/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/crtbegin.o
-L/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0
-L/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../../../lib64
-L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu
-L/usr/lib/../lib64 -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/../../..
a-example.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc
/usr/local/lib/gcc/x86_64-pc-linux-gnu/14.1.0/crtend.o
/lib/x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Wall' '-shared-libgcc'
'-mtune=generic' '-march=x86-64' '-dumpdir' 'a.'

Reply via email to