https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120785
Bug ID: 120785 Summary: No compiler warning when wrapping a packed C-style array in a std::span, array memcpy instead Product: gcc Version: 13.3.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: anthony.hayw...@ast-science.com Target Milestone: --- Created attachment 61693 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=61693&action=edit File generated by `g++ -v -save-temps -std=c++20 packed_span_stack_overflow.cpp` We have some packed structs that contain arrays, which we use for mapping binary messages. When such an array contains some aligned type such as `int` rather than just being composed of 1-byte fields, we expect a compiler warning or error if we try to take the address of the array. A compiler warning or error does happen in many cases (e.g. if it's an `std::array` rather than a C-style array) but there is no warning when it is a C-style array and we construct a `std::span` to point at it. Instead, gcc generates a `memcpy` call that copies the entire array onto the stack. This is surprising behaviour and undesirable, given that `std::span` is supposed to be a lightweight pointer-style non-owning reference. Also, if the `std::span` is a local variable, the address sanitizer does not believe that it can be read safely (it does seem to be safe if the `span` is a function parameter). I have attached a short and self-contained example program `packed_span_stack_overflow.cpp` which demonstrates the stack copy. When run, it causes a segmentation fault (or `stack-overflow` if compiled with `-fsanitize=address`) due to copying the over-large array from the heap to the stack. Here is the console output from `g++ -v -save-temps -std=c++20 packed_span_stack_overflow.cpp`: Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.3.0-6ubuntu2~24.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2 Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04) COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' /usr/libexec/gcc/x86_64-linux-gnu/13/cc1plus -E -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE packed_span_stack_overflow.cpp -mtune=generic -march=x86-64 -std=c++20 -fpch-preprocess -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o a-packed_span_stack_overflow.ii ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/13" ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu" ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed" ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include" #include "..." search starts here: #include <...> search starts here: /usr/include/c++/13 /usr/include/x86_64-linux-gnu/c++/13 /usr/include/c++/13/backward /usr/lib/gcc/x86_64-linux-gnu/13/include /usr/local/include /usr/include/x86_64-linux-gnu /usr/include End of search list. COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' /usr/libexec/gcc/x86_64-linux-gnu/13/cc1plus -fpreprocessed a-packed_span_stack_overflow.ii -quiet -dumpdir a- -dumpbase packed_span_stack_overflow.cpp -dumpbase-ext .cpp -mtune=generic -march=x86-64 -std=c++20 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o a-packed_span_stack_overflow.s GNU C++20 (Ubuntu 13.3.0-6ubuntu2~24.04) version 13.3.0 (x86_64-linux-gnu) compiled by GNU C version 13.3.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: c81c05345ce537099dafd5580045814a COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a-' as -v --64 -o a-packed_span_stack_overflow.o a-packed_span_stack_overflow.s GNU assembler version 2.42 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.42 COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a.' /usr/libexec/gcc/x86_64-linux-gnu/13/collect2 -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/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 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. a-packed_span_stack_overflow.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o COLLECT_GCC_OPTIONS='-v' '-save-temps' '-std=c++20' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'a.' If you reduce the array size on source line 6 from 7,000,000 to 700,000 then the array fits on the stack, but compiling the modified program with `-fsanitize-address` and running it leads to a `stack-use-after-scope` error, showing that the unwanted copy of the array on the stack is not considered safe to access. In some environments, I've been able to read freed memory this way, suggesting that the sanitizer is right to be worried. I believe that gcc should raise a compiler error on this program, so that the surprise memcpy is avoided. This occurs on gcc 13.3 but I've also tested with 15.1 and the result is the same.