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.