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

Reply via email to