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

Reply via email to