[PATCH v5 10/16] riscv/cfi: Adjust setjmp/longjmp for shadow stack to work
Gabriel Ravier
gabravier@gmail.com
Sun Sep 13 23:06:33 GMT 2026
On 6/28/26 9:02 AM, Jesse Huang wrote:
> Since longjmp to a previous setjmp state can change the stack frame and
> involves stack frame unwinding, the shadow stack is also required to be
> unwound.
>
> The unwinding is implemented according to the Zicfiss specification by
> increasing the SSP by at most one page size (4K), to avoid accidentally
> pointing to another legal shadow stack page after the adjustment.
>
> The shadow stack pointer is stored in a wrapped sigset_t. By defining it
> inside a union, we can avoid changing the size of sigset_t and therefore
> jmp_buf.
> ---
> sysdeps/riscv/Makefile | 4 +
> sysdeps/riscv/__longjmp.S | 54 +++++++++++++
> sysdeps/riscv/setjmp.S | 22 ++++++
> sysdeps/unix/sysv/linux/riscv/jmp_buf-ssp.sym | 7 ++
> sysdeps/unix/sysv/linux/riscv/setjmpP.h | 78 +++++++++++++++++++
> 5 files changed, 165 insertions(+)
> create mode 100644 sysdeps/unix/sysv/linux/riscv/jmp_buf-ssp.sym
> create mode 100644 sysdeps/unix/sysv/linux/riscv/setjmpP.h
>
> [snip]
> diff --git a/sysdeps/riscv/__longjmp.S b/sysdeps/riscv/__longjmp.S
> index bea854199c..8ee0662b81 100644
> --- a/sysdeps/riscv/__longjmp.S
> +++ b/sysdeps/riscv/__longjmp.S
> @@ -18,9 +18,12 @@
>
> #include <sysdep.h>
> #include <sys/asm.h>
> +#include <jmp_buf-ssp.h>
> +#include <tcb-offsets.h>
>
> ENTRY (__longjmp)
> REG_L ra, 0*SZREG(a0)
> + REG_L t1, 0*SZREG(a0)
> REG_L s0, 1*SZREG(a0)
> REG_L s1, 2*SZREG(a0)
> REG_L s2, 3*SZREG(a0)
> @@ -50,8 +53,59 @@ ENTRY (__longjmp)
> FREG_L fs11,14*SZREG+11*SZFREG(a0)
> #endif
>
> +#ifdef __riscv_shadow_stack
> + /* skip unwinding if ss is not enabled */
> + ssrdp ra
> + beqz ra, .Lfin
> + REG_L t0, SSP_OFFSET(a0)
> + REG_L a0, SSP_BASE_OFFSET(a0)
> + REG_L t2, TLS_SSP_BASE_OFFSET(tp)
> + bne a0, t2, .Ldifferent_stack
> +.Lunwind:
> + bleu t0, ra, .Lfin
> + /* Increase ssp by at most one page size to ensure the adjustment
> + always runs into a guard page before accidentally pointing to
> + another legal shadow stack page */
> + /* ra = (t0 - ra >= 4096) ? ra + 4096 : t0 */
> + lui a0, 1
> + add ra, ra, a0
> + bleu ra, t0, 1f
> + mv ra, t0
> +1:
> + csrw ssp, ra
> + /* Test if the location pointed by ssp is legal */
> + sspush x5
> + sspopchk x5
> + j .Lunwind
> +.Ldifferent_stack:
> + /* Create restore token */
> + sspush ra
> + mv a4, t0
> +
> +.Lfind_rstor_token:
> + /* Probe and validate target restore token */
> + ssamoswap.d a3, x0, (a4)
> + addi a2, a4, 8
> + beq a3, a2, .Lswitch_stack
> + /* Restore the shadow stack and try the next slot */
> + ssamoswap.d x0, a3, (a4)
> + addi a4, a4, -8
> + j .Lfind_rstor_token
> +
> +.Lswitch_stack:
> + /* Switch stack: update ssp and base */
> + csrw ssp, t0
> + REG_S a0, TLS_SSP_BASE_OFFSET(tp)
> +.Lfin:
> +#endif
> seqz a0, a1
> add a0, a0, a1 # a0 = (a1 == 0) ? 1 : a1
> +#ifdef __riscv_landing_pad
> + /* Use indirect branch if CFI is enabled */
> + jr t1
Actually, t1 seems just flatly wrong - the RISC-V spec (4.17.1.1.
Landing Pad Enforcement) appears to specify solely x7 as the register
for a software-guarded branch, and t1 is x6 (x7 is t2). Shouldn't this
(and the above load) use t2 instead of t1?
> +#else
> + mv ra, t1
> ret
> +#endif
>
> END (__longjmp)
> [snip]
More information about the Libc-alpha
mailing list