https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115014
Bug ID: 115014 Summary: GCC generates incorrect instructions for addressing the data segment through EBP register Product: gcc Version: 13.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: target Assignee: unassigned at gcc dot gnu.org Reporter: mdoucha at suse dot cz Target Milestone: --- Created attachment 58149 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=58149&action=edit Preprocessed reproducer source file Target: x86 (32bit) While writing a new KVM test for the Linux Test Project, I ran into some mysterious virtual machine crashes due to stack segment fault exception. The test is essentially a tiny kernel that'll bootstrap a virtual CPU into protected mode, run a few operations to test something and return results in a predefined block of memory to the controller process running on the host machine. The cause of the crashes turns out to be a combination of custom stack segment limit and GCC generating unprefixed instructions for accessing data segment addresses through the EBP register. For example, GCC generates this instruction: movzbl 0x1(%ebp),%eax When it should generate a prefixed one instead: movzbl %ds:0x1(%ebp),%eax This would not be a problem on most systems because the stack and data segments are usually identical but due to the embedded nature of the KVM test, the stack segment has special settings and the address in EBP happens to be a pointer to string constant stored outside the stack segment limit. The fix should be simple: 1) If the address in EBP was calculated from stack pointer value, generate unprefixed instructions. 2) Otherwise add data segment prefix. This condition should be applied at least if the source file is compiled for 32bit x86 with the -ffreestanding argument. The attached source file contains a minimal reimplementation of standard C library functions for the embedded environment. The vsprintf() reimplementation is where the issue manifests. The source file was compiled with the following command line options: gcc -I../testcases/kernel/kvm/include -DCOMPILE_PAYLOAD -ffreestanding -O2 -Wall -fno-asynchronous-unwind-tables -fno-stack-protector -mno-mmx -mno-sse -save-temps -m32 -fno-pie -c -o lib_guest.o ../testcases/kernel/kvm/lib_guest.c Note that this is not the full KVM test which you could run to trigger the stack segment fault exception. The full test would require: - CPU bootstrap file in assembly[1] (see below) - the attached reproducer file - main test source - test controller source - custom linker script - build script to tie it all together Instead, you can just see the assembly output and look for the movzbl instruction mentioned above. The GCC package used for building the KVM test comes from SUSE, but I've already talked to the SUSE maintainer (Martin Jambor) and he asked me to open an upstream bug. gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib64/gcc/x86_64-suse-linux/13/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-suse-linux Configured with: ../configure CFLAGS=' -O2 -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -Werror=return-type -g' CXXFLAGS=' -O2 -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -Werror=return-type -g' XCFLAGS=' -O2 -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -Werror=return-type -g' TCFLAGS=' -O2 -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -Werror=return-type -g' GDCFLAGS=' -O2 -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g' --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64 --enable-languages=c,c++,objc,fortran,obj-c++,ada,go,d,jit,m2 --enable-offload-targets=nvptx-none,amdgcn-amdhsa, --enable-offload-defaulted --without-cuda-driver --enable-host-shared --enable-checking=release --disable-werror --with-gxx-include-dir=/usr/include/c++/13 --with-libstdcxx-zoneinfo=/usr/share/zoneinfo --enable-ssp --disable-libssp --disable-libvtv --enable-cet=auto --disable-libcc1 --enable-plugin --with-bugurl=https://bugs.opensuse.org/ --with-pkgversion='SUSE Linux' --with-slibdir=/lib64 --with-system-zlib --enable-libstdcxx-allocator=new --disable-libstdcxx-pch --enable-libphobos --enable-version-specific-runtime-libs --with-gcc-major-version-only --enable-linker-build-id --enable-linux-futex --enable-gnu-indirect-function --program-suffix=-13 --without-system-libunwind --enable-multilib --with-arch-32=x86-64 --with-tune=generic --with-build-config=bootstrap-lto-lean --enable-link-serialization --build=x86_64-suse-linux --host=x86_64-suse-linux Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 13.2.1 20240206 [revision 67ac78caf31f7cb3202177e6428a46d829b70f23] (SUSE Linux) The CPU bootstrap file for reference, with stack segment setup highlighted: [1] https://github.com/linux-test-project/ltp/blob/master/testcases/kernel/kvm/bootstrap_x86.S#L117