https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102926
Bug ID: 102926 Summary: TLS register value is spilled to the stack instead of reloaded from the system register Product: gcc Version: 12.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: target Assignee: unassigned at gcc dot gnu.org Reporter: ardb at kernel dot org Target Milestone: --- Target: ARM The code below uses the hardware TLS register on ARM, and instead of reloading its value directly from the system register, it spills its value to the stack. This is suboptimal, and given that the TLS register is being proposed as an alternative reference for the stack protector canary, it is also a security concern, as an attacker that controls the stack may be able to control both sides of the equation in the stack protector check occurring at the end of the function. Instead, I would expect any subsequent uses of the thread pointer to simply issue the MRC again, which doesn't touch memory. $ cat /tmp/spill.c int foo(int); int bar(void) { int *l = __builtin_thread_pointer(); return foo(l[0]) + l[1]; } $ arm-linux-gnueabihf-gcc -o - -S /tmp/spill.c -O3 -mtp=cp15 -ffixed-r4 -ffixed-r5 -ffixed-r6 -ffixed-r7 -ffixed-r8 -ffixed-r9 -ffixed-r10 -fno-omit-frame-pointer .cpu arm10tdmi .arch armv5t .fpu softvfp .eabi_attribute 20, 1 .eabi_attribute 21, 1 .eabi_attribute 23, 3 .eabi_attribute 24, 1 .eabi_attribute 25, 1 .eabi_attribute 26, 2 .eabi_attribute 30, 2 .eabi_attribute 34, 0 .eabi_attribute 18, 4 .file "spill.c" .text .align 2 .global bar .syntax unified .arm .type bar, %function bar: @ args = 0, pretend = 0, frame = 8 @ frame_needed = 1, uses_anonymous_args = 0 push {fp, lr} add fp, sp, #4 sub sp, sp, #8 mrc p15, 0, r3, c13, c0, 3 @ load_tp_hard ldr r0, [r3] str r3, [fp, #-8] bl foo ldr r3, [fp, #-8] ldr r3, [r3, #4] add r0, r0, r3 sub sp, fp, #4 @ sp needed pop {fp, pc} .size bar, .-bar .ident "GCC: (GNU) 12.0.0 20211024 (experimental)" .section .note.GNU-stack,"",%progbits