https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103009
Bug ID: 103009 Summary: Weakness in stack-protector with no-pie active on ARM. Product: gcc Version: 9.4.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: sylw.bar at gmail dot com Target Milestone: --- Created attachment 51710 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=51710&action=edit Test program During development for very space restricted Cortex-A5 based environment I found potential weakness in stack-protector functionality in GCC 9.4.0. Fortunatelly, I was able to reproduce problem using 32-bit Ubuntu Server 21.10 for Raspberry Pi (I used RPi4). https://ubuntu.com/download/raspberry-pi The problem: Not appropriate content of canary value put on the stack when stack-protector is enabled and -fno-pie active. In theory, for security reasons, the canary value should be randomly generated. During my development I realized that in my system the canary is constant and holds address of __stack_chk_guard hidden variable. The __stack_chk_guard variable is internal stack-protector variable that holds random value that should be copied to tested function stack. I prepared simple program that extracts real value of canary copied on test() function stack. The canary is hidden but it is located right after 12-byte buffer. I'm using out of bound index in main() to extract canary which type is uintptr_t. Important fact is that problem occurs when -fno-pie is set. Here is test.c program: #include <stdio.h> #include <stdint.h> #define BUF_LEN 12 extern uintptr_t __stack_chk_guard; uintptr_t test(int idx) { unsigned char buffer[BUF_LEN]; return *((uintptr_t*)&buffer[idx]); } int main() { printf("__stack_chk_guard: &%x = %x\n", (int)&__stack_chk_guard, __stack_chk_guard); uintptr_t canary = test(BUF_LEN); printf("canary: %x\n", canary); return 0; } System: 32-bit Ubuntu Server 21.10 for Raspberry Pi on RPI4 with additional packets: gcc-9 gcc-10 gcc-11: GCC 10&11 show correct result: ubuntu@ubuntu:~/work$ gcc-11 -Os -fno-pie -fstack-protector-all test.c -o test ubuntu@ubuntu:~/work$ ./test __stack_chk_guard: &b6f72298 = 90148100 canary: 90148100 ubuntu@ubuntu:~/work$ gcc-10 -Os -fno-pie -fstack-protector-all test.c -o test ubuntu@ubuntu:~/work$ ./test __stack_chk_guard: &b6fa5298 = 1642cf00 canary: 1642cf00 GCC 9.4 reveals that address of __stack_chk_guard was copied to canary: ubuntu@ubuntu:~/work$ gcc-9 -Os -fno-pie -fstack-protector-all test.c -o test ubuntu@ubuntu:~/work$ ./test __stack_chk_guard: &b6f85298 = 69229c00 canary: b6f85298 ubuntu@ubuntu:~/work$ gcc-9 -v Using built-in specs. COLLECT_GCC=gcc-9 COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/9/lto-wrapper Target: arm-linux-gnueabihf Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.4.0-3ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=arm-linux-gnueabihf- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-sjlj-exceptions --with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard --with-mode=thumb --disable-werror --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf Thread model: posix gcc version 9.4.0 (Ubuntu 9.4.0-3ubuntu1) Summary. 1. Problem (potential) is reproducible on 9.3.0 and 9.4.0 with -fno-pie. 2. Problem not visible on: 10.3.0, 11.2.0 3. stack-protector functionality is working fine, but lack of randomness of canary is security risk. Attaching function test() assemblies compiled on 9.4 and 10.3. There are visible differences between addressing __stack_chk_guard using r3 register. 10.3: test: @ args = 0, pretend = 0, frame = 16 @ frame_needed = 0, uses_anonymous_args = 0 push {r0, r1, r2, r3, r4, lr} ldr r3, .L3 ldr r3, [r3] str r3, [sp, #12] ... .L3: .word __stack_chk_guard 9.4: test: @ args = 0, pretend = 0, frame = 16 @ frame_needed = 0, uses_anonymous_args = 0 push {r0, r1, r2, r3, r4, lr} ldr r3, .L3 ldr r3, [r3] str r3, [sp, #12] ... .L3: .word .LC0 .LC0: .word __stack_chk_guard