https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128285

            Bug ID: 128285
           Summary: cfgexpand: -fstack-protector-strong skips >16B
                    over-aligned buffers in Phase 1/2 and places them
                    below local pointers
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          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-*-*, riscv64-*-*, loongarch64-*-*

Summary
-------
Under `-fstack-protector-strong` / `-fstack-protector-all`, local character
arrays with alignment greater than `MAX_SUPPORTED_STACK_ALIGNMENT` (16 bytes on
AArch64, RISC-V, and LoongArch, e.g. `_Alignas(32) char buf[64]`) are skipped
during SSP Phase 1 and Phase 2 and allocated at the bottom of the stack frame—
at lower addresses than local function pointers and pointer arrays.

In `gcc/cfgexpand.cc:1027-1037` (`expand_stack_vars`), whenever the phase
predicate `pred` is non-NULL (`stack_protect_decl_phase_1` or
`stack_protect_decl_phase_2`), GCC skips any variable with
`alignb * BITS_PER_UNIT > MAX_SUPPORTED_STACK_ALIGNMENT`:

  if (pred && alignb * BITS_PER_UNIT > MAX_SUPPORTED_STACK_ALIGNMENT)
    continue;

The over-aligned buffer falls through to the unpredicated third pass and is
carved out via `large_alloc` at low stack offsets (`[sp+32..95]`), while local
function pointers `handlers[4]` sit above it at `[sp+96..127]` and the canary
sits at `[sp+136]`. A linear overflow of `buf` overwrites `handlers[]` and
hijacks `blr` before the epilogue canary check ever runs.

Reproducer
----------
$ cat trigger.c
typedef void (*fn_t)(const char *);
void fill(char *);

void dispatch_frame(fn_t f0, fn_t f1, int idx) {
    fn_t handlers[4] = {f0, f1, f0, f1};
    _Alignas(32) char buf[64];
    fill(buf);
    handlers[idx & 3](buf);
}

Compile flag: aarch64-linux-gnu-gcc -O2 -fstack-protector-strong -S trigger.c

$ cat trigger.s
dispatch_frame:
        sub     sp, sp, #176
        ...
        add     x19, sp, 47
        ldr     x2, [x0, #:lo12:__stack_chk_guard]
        str     x2, [sp, 136]           # stack canary at [sp+136]
        and     x0, x19, -32            # buf[64] placed at [sp+32..95] (LOW
address!)
        stp     q31, q31, [sp, 96]      # handlers[4] placed at [sp+96..127]
(ABOVE buf!)
        bl      fill
        ...
        add     x0, sp, 96
        ldr     x1, [x0, x2]            # reloads overwritten handlers[idx]
        blr     x1                      # indirect call hijacked before canary
check!

Suggested Fix
-------------
Allocate over-aligned buffers matched by `stack_protect_decl_phase_1` /
`stack_protect_decl_phase_2` above local pointers/scalars and immediately below
the stack canary slot.

Reply via email to