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`.

Reply via email to