[PATCH 10/12] riscv/cfi: Adjust setjmp/longjmp for shadow stack to work

Deepak Gupta debug@rivosinc.com
Wed Jun 18 19:18:50 GMT 2025


Hi Jesse,

Thanks for taking the lead.

Can we do save and restore a bit differently using a token mechanism.

On Wed, Jun 18, 2025 at 01:42:56AM -0700, Jesse Huang wrote:
>Since longjmp to a previous setjmp'ed state could change the stack
>frame and involves stack frame unwinding, shadow stacks is also required
>to be unwinded.
>
>The unwinding is implemented according to the zicfiss spec by increasing
>the ssp by a page size (4K) at most, to prevent from accidentally point
>to another legal shadow stack page after the adjustment.
>---
> sysdeps/riscv/__longjmp.S   | 30 ++++++++++++++++++++++++++++++
> sysdeps/riscv/bits/setjmp.h |  4 ++++
> sysdeps/riscv/setjmp.S      | 10 ++++++++++
> 3 files changed, 44 insertions(+)
>
>diff --git a/sysdeps/riscv/__longjmp.S b/sysdeps/riscv/__longjmp.S
>index 47ff3aa09e..3f1fc3148d 100644
>--- a/sysdeps/riscv/__longjmp.S
>+++ b/sysdeps/riscv/__longjmp.S
>@@ -51,6 +51,36 @@ ENTRY (__longjmp)
> 	FREG_L fs11,14*SZREG+11*SZFREG(a0)
> #endif
>
>+#ifdef __riscv_shadow_stack
>+        /* read ssp into t0  */
>+        ssrdp t0
>+        /* skip unwinding if ss is not enabled  */
>+        beqz  t0, .Lunwind_fin
>+# ifndef __riscv_float_abi_soft
>+	REG_L t1, 14*SZREG+12*SZFREG(a0)
>+# else
>+	REG_L t1, 14*SZREG(a0)
>+# endif
>+.Lunwind:
>+        /* should not be taken in normal condition  */
>+        bleu  t1, t0, .Lunwind_fin
>+        /* The unwinding algorithm came from the shadow_stack spec, increase ssp
>+           by at most a page size to ensure always run into a guard page
>+           before accidentally point to another legal shadow stack page  */
>+        /* t0 = (t1 - t0 >= 4096) ? t0 + 4096 : t1  */
>+        lui   a0, 1
>+        add   t0, t0, a0
>+        bleu  t0, t1, 1f
>+        mv    t0, t1
>+1:
>+        csrw  ssp, t0
>+        /* Test if the location pointed by ssp is legal  */
>+        sspush x5
>+        sspopchk x5

Above is useful in C++ exception unwinding where a lot of other stuff gets
unwinded too (registers, etc) as per static information in DWARF (I think its
in dwarf)

In case of setjmp/longjmp, we can use shadow stack token mechainsm.
We can do below instead to restore shadow stack pointer (see save token
in my comments in setjmp)

	ssrdp t0
	beqz t0, .Lskip_ss_token_rstor

	/*
	 * non-zero t0 means shadow stack is enabled. thus ssamoswap/csrw to
	 * ssp won't fault.
	 */

	REG_L t0, 14*SZREG(a0)	/* get the saved ss pointer */
	ssamoswap t1, x0, (t0)	/* Get the saved token, and store 0 */
	addi t0, t0, 8		/* increment ss pointer */
	bneq t0, t1, fatal	/* ss ptr and token must match */
	csrw ssp, t0		/* we did validation and restoring ssp correctly */

.Lskip_ss_token_rstor:


fatal:
	terminate or recovery action

>+        j .Lunwind
>+.Lunwind_fin:
>+#endif
>+
> 	seqz a0, a1
> 	add  a0, a0, a1   # a0 = (a1 == 0) ? 1 : a1
> 	ret
>diff --git a/sysdeps/riscv/bits/setjmp.h b/sysdeps/riscv/bits/setjmp.h
>index 5ebbec393a..5cbc12fa3d 100644
>--- a/sysdeps/riscv/bits/setjmp.h
>+++ b/sysdeps/riscv/bits/setjmp.h
>@@ -33,6 +33,10 @@ typedef struct __jmp_buf_internal_tag
>    double __fpregs[12];
> #elif !defined __riscv_float_abi_soft
> # error unsupported FLEN
>+#endif
>+#ifdef __riscv_shadow_stack
>+    /* Shadow stack pointer.  */
>+    long int __ssp;
> #endif
>   } __jmp_buf[1];
>
>diff --git a/sysdeps/riscv/setjmp.S b/sysdeps/riscv/setjmp.S
>index df048cb544..f547bc0238 100644
>--- a/sysdeps/riscv/setjmp.S
>+++ b/sysdeps/riscv/setjmp.S
>@@ -61,6 +61,16 @@ ENTRY (__sigsetjmp)
> 	FREG_S fs11,14*SZREG+11*SZFREG(a0)
> #endif
>
>+#ifdef __riscv_shadow_stack
>+        /* read ssp into t0  */
>+        ssrdp t0

It will be better if we do something like below to save shadow stack pointer
as token on shadow stack itself.

	ssrdp t0
	beqz t0, .Lskip_ss_token_prep

	mv t1, t0           	/* record current ss pointer in t1 */
	addi t0, t0, -8     	/* decrement current ss pointer by 8 */
	ssamoswap x0, t1, (t0)	/* save away current ss pointer on ss itself */

.Lskip_ss_token_prep:
>+# ifndef __riscv_float_abi_soft
>+	REG_S t0, 14*SZREG+12*SZFREG(a0)
>+# else
>+	REG_S t0, 14*SZREG(a0)
>+# endif
>+#endif
>+
> #if !IS_IN (libc) && IS_IN (rtld)
>   /* In ld.so we never save the signal mask.  */
>   li a0, 0
>-- 
>2.39.3
>


More information about the Libc-alpha mailing list