https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128272
Bug ID: 128272
Summary: [RISC-V] Zicfiss: missing
STACK_SAVEAREA_MODE(SAVE_NONLOCAL) overflows 8-byte
nonlocal save slot
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: riscv64-*-*
Summary
-------
Under `-march=rv64gc_zicfiss -fcf-protection=return`, `save_stack_nonlocal`
writes 16 bytes (`ssp` and `sp`) into an 8-byte stack slot, overflowing into
the adjacent stack slot and corrupting hardware `sp` upon
`restore_stack_nonlocal`.
In `gcc/config/riscv/riscv.md:4280-4293`, when `TARGET_ZICFISS` is enabled,
`save_stack_nonlocal` stores two pointers into the save area: `ssp` at offset 0
and `sp` at offset `+UNITS_PER_WORD` (`+8` on RV64, 16 bytes total). However,
unlike x86 (`i386.h:1985`) and AArch64 (`aarch64.h:1416`),
`gcc/config/riscv/riscv.h`
does not define `STACK_SAVEAREA_MODE(LEVEL)`. Consequently,
`gcc/explow.cc:1211`
allocates only `Pmode` (8 bytes on RV64) for `SAVE_NONLOCAL`.
In `__builtin_apply`, the 8-byte nonlocal save slot is allocated at `-72(s0)`
immediately below the callee return-value buffer at `-64(s0)`.
`save_stack_nonlocal`
writes `sp` out-of-bounds to `-64(s0)`, `jalr` overwrites `-64(s0)` with the
callee's integer return value `a0` (`sd a0, -64(s0)`), and
`restore_stack_nonlocal`
(`ld sp, -64(s0)`) loads the callee's return value `a0` directly into the
hardware stack pointer `sp`!
Reproducer
----------
$ cat trigger.c
void *test_apply(void (*fn)(), void *args, unsigned long size) {
return __builtin_apply(fn, args, size);
}
Compile flag: riscv64-linux-gnu-gcc -O2 -march=rv64gc_zicfiss -mabi=lp64d
-fcf-protection=return -S trigger.c
$ cat trigger.s
test_apply:
sspush ra
addi sp,sp,-80
...
ssrdp a5
sd a5,-72(s0) # 8-byte save slot [-72(s0)..-65(s0)]
addi a5,a2,15
andi a5,a5,-16
sd sp,-64(s0) # 8B OOB write into return-value buffer
-64(s0)!
...
jalr s2
sd a0,-64(s0) # callee return value a0 overwrites
-64(s0)!
...
.L2:
ld sp,-64(s0) # loads callee return value a0 into
hardware sp!
Suggested Fix
-------------
Define `STACK_SAVEAREA_MODE(LEVEL)` in `gcc/config/riscv/riscv.h` so that
`SAVE_NONLOCAL` uses double-word mode (`TImode` on RV64 / `DImode` on RV32)
when `riscv_shadow_stack_enabled_p ()` is true (matching `aarch64.h:1416`).