https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128273
Bug ID: 128273
Summary: [AArch64] -fsanitize=shadow-call-stack clobbers SCS
register x18 via STATIC_CHAIN_REGNUM and nonlocal goto
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: 220245569 at seu dot edu.cn
CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
Target Milestone: ---
Target: aarch64-*-*
Summary
-------
Under `-O2 -fsanitize=shadow-call-stack -ffixed-x18` on AArch64, calling a
nested function overwrites the Shadow Call Stack register `x18` with a regular
stack frame address, causing the caller's epilogue to pop a regular stack slot
into `x30` (LR) and jump to it on `ret`:
1. `gcc/config/aarch64/aarch64.h:715` hardcodes `#define STATIC_CHAIN_REGNUM
R18_REGNUM`.
When a function calls a nested function that captures a parent variable, GCC
loads the parent frame address into `x18` (`add x18, sp, 16`), destroying
the SCS pointer. In the epilogue, `ldr x30, [x18, -8]!` loads `[sp + 8]`
from the ordinary data stack into `x30` and executes `ret`!
2. In `gcc/config/aarch64/aarch64.md:1500-1526` (`restore_stack_nonlocal`),
`__builtin_setjmp` / `__builtin_longjmp` and non-local `goto` do not save or
restore `x18`, leaving `x18` shifted after unwinding frames that pushed to
SCS.
Reproducer
----------
$ cat trigger.c
int ext_call(int);
int outer_nested_scs(int x) {
volatile int captured = x + 1;
__attribute__((noinline)) int inner_scs(int y) {
return ext_call(captured + y);
}
return inner_scs(2);
}
Compile flag: aarch64-linux-gnu-gcc -O2 -fsanitize=shadow-call-stack
-ffixed-x18 -S trigger.c
$ cat trigger.s
outer_nested_scs:
str x30, [x18], 8 # pushes return address to SCS (x18)
add w0, w0, 1
stp x29, x30, [sp, -32]!
add x1, sp, 32
add x18, sp, 16 # BUG: clobbers SCS pointer x18 with
data stack sp+16!
mov x29, sp
str w0, [sp, 16]
str x1, [sp, 24]
bl inner_scs.0.constprop.0
ldr x30, [x18, -8]! # BUG: loads [sp+8] (uninitialized data
stack) into x30!
ldr x29, [sp], 32
ret
Suggested Fix
-------------
Do not use `R18_REGNUM` (`x18`) as `STATIC_CHAIN_REGNUM` when `R18_REGNUM` is
fixed or `SANITIZE_SHADOW_CALL_STACK` is enabled (or diagnose the conflict),
and preserve `x18` across `save_stack_nonlocal` / `restore_stack_nonlocal`.