https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128298
Bug ID: 128298
Summary: [RISC-V] Zicfiss: -msave-restore tail-calls
__riscv_restore_N without sspopchk after prologue
sspush ra
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 `-Os/-O2 -msave-restore -march=rv64gc_zicfiss -fcf-protection=return`,
non-leaf functions emit `sspush ra` in the prologue, but their epilogue
returns via `tail __riscv_restore_N` without executing `sspopchk`.
In `gcc/config/riscv/riscv.cc:10584-10616` (`riscv_expand_epilogue`), when
`use_restore_libcall` is true, GCC emits `emit_jump_insn
(gen_gpr_restore_return (...))`
and returns early (`return;`) at line 10615—before reaching the
`need_shadow_stack_push_pop_p ()`
block at line 10695 that emits `sspopchk`. Meanwhile,
`libgcc/config/riscv/save-restore.S`
contains no shadow-stack instructions.
As a result, the function returns using the unverified `ra` reloaded from the
ordinary stack while leaving its `sspush` entry on the hardware shadow stack.
When the caller subsequently executes `sspopchk`, it compares its own `ra`
against the callee's leaked shadow-stack entry and faults with a Zicfiss
Software-check exception.
Reproducer
----------
$ cat trigger.c
extern void sink (int *);
int f (int a, int b, int c) {
int arr[8];
sink (arr);
return arr[0] + a + b + c;
}
Compile flag: riscv64-linux-gnu-gcc -Os -msave-restore -march=rv64gc_zicfiss
-mabi=lp64d -fcf-protection=return -S trigger.c
$ cat trigger.s
f:
sspush ra # pushes ra onto shadow stack
call t0,__riscv_save_1
addi sp,sp,-32
...
addi sp,sp,32
tail __riscv_restore_1 # returns without sspopchk!
Suggested Fix
-------------
In `riscv_avoid_save_libcall` (`gcc/config/riscv/riscv.cc`), return `true` when
`need_shadow_stack_push_pop_p ()` is true (matching the existing Zcmp
`cm.popret`
exclusion under Zicfiss), or emit `sspopchk` before `gen_gpr_restore_return`.