[PATCH v2 14/14] riscv/cfi: Support ucontext under CFI

Deepak Gupta debug@rivosinc.com
Tue Jul 22 22:37:51 GMT 2025


On Fri, Jul 11, 2025 at 06:52:55AM -0700, Jesse Huang wrote:
>This patch adds support for shadow stack and landing pad to the
>ucontext library, shadow stack switches are protected by a shadow stack
>restore token which will be validated during the switch.
>
>Co-authored-by: Nia Su <nia.su@sifive.com>
>---
> sysdeps/unix/sysv/linux/riscv/getcontext.S  | 20 ++++++
> sysdeps/unix/sysv/linux/riscv/makecontext.c | 18 +++++
> sysdeps/unix/sysv/linux/riscv/setcontext.S  | 67 ++++++++++++++++++
> sysdeps/unix/sysv/linux/riscv/swapcontext.S | 77 ++++++++++++++++++++-
> 4 files changed, 181 insertions(+), 1 deletion(-)

>
>diff --git a/sysdeps/unix/sysv/linux/riscv/getcontext.S b/sysdeps/unix/sysv/linux/riscv/getcontext.S
>index 86e7a8ff91..e529ad98ef 100644
>--- a/sysdeps/unix/sysv/linux/riscv/getcontext.S
>+++ b/sysdeps/unix/sysv/linux/riscv/getcontext.S
>@@ -17,11 +17,13 @@
>    <https://www.gnu.org/licenses/>.  */
>
> #include "ucontext-macros.h"
>+#include "tcb-offsets.h"
>
> /* int getcontext (ucontext_t *ucp) */
>
> 	.text
> LEAF (__getcontext)
>+	LPAD
> 	SAVE_INT_REG (ra,   0, a0)
> 	SAVE_INT_REG (ra,   1, a0)
> 	SAVE_INT_REG (sp,   2, a0)
>@@ -58,6 +60,24 @@ LEAF (__getcontext)
> 	sw	a1, MCONTEXT_FSR(a0)
> #endif /* __riscv_float_abi_soft */
>
>+#ifdef __riscv_shadow_stack
>+	ssrdp	t0
>+	beqz	t0, .Lskip_ss
>+	/* Read ssp_base from TLS  */
>+	ld	t1, SSP_BASE_OFFSET(tp)
>+
>+	bnez	t1, .Lbase_saved
>+	/* if not found, use current ssp as the marker  */
>+	mv	t1, t0
>+	sd	t1, SSP_BASE_OFFSET(tp)
>+
>+.Lbase_saved:
>+	/* Save caller's ssp and base marker to ucontext  */
>+	REG_S	t1, UCONTEXT_SSP_BASE(a0)
>+	REG_S	t0, UCONTEXT_SSP(a0)
>+.Lskip_ss:
>+#endif
>+
> /* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
> 	li	a3, _NSIG8
> 	add     a2, a0, UCONTEXT_SIGMASK
>diff --git a/sysdeps/unix/sysv/linux/riscv/makecontext.c b/sysdeps/unix/sysv/linux/riscv/makecontext.c
>index 3da27dd5df..1f9bef6887 100644
>--- a/sysdeps/unix/sysv/linux/riscv/makecontext.c
>+++ b/sysdeps/unix/sysv/linux/riscv/makecontext.c
>@@ -21,6 +21,9 @@
> #include <sys/ucontext.h>
> #include <stdarg.h>
> #include <assert.h>
>+#ifdef __riscv_shadow_stack
>+#include <allocate-shadow-stack.h>
>+#endif
>
> void
> __makecontext (ucontext_t *ucp, void (*func) (void), int argc,
>@@ -73,6 +76,21 @@ __makecontext (ucontext_t *ucp, void (*func) (void), int argc,
>
>       va_end (vl);
>     }
>+#ifdef __riscv_shadow_stack
>+  /* Allocate shadow stack for the new context  */
>+
>+  /* shstk_size[0]: shadow stack base
>+     shstk_size[1]: shadow stack size  */
>+  shadow_stack_size_t shstk_size[2];
>+  int ret = __allocate_shadow_stack(ucp->uc_stack.ss_size, shstk_size);
>+  if (ret != 0)
>+    {
>+      abort();
>+    }
>+
>+  ucp->uc_ssp_base = shstk_size[0];
>+  ucp->uc_ssp = shstk_size[0] + shstk_size[1] - sizeof (shstk_size[0]);
>+#endif
> }
>
> weak_alias (__makecontext, makecontext)
>diff --git a/sysdeps/unix/sysv/linux/riscv/setcontext.S b/sysdeps/unix/sysv/linux/riscv/setcontext.S
>index a2de57b537..eb7ffc5f3a 100644
>--- a/sysdeps/unix/sysv/linux/riscv/setcontext.S
>+++ b/sysdeps/unix/sysv/linux/riscv/setcontext.S
>@@ -17,6 +17,7 @@
>    <https://www.gnu.org/licenses/>.  */
>
> #include "ucontext-macros.h"
>+#include "tcb-offsets.h"
>
> /*  int __setcontext (const ucontext_t *ucp)
>
>@@ -29,6 +30,7 @@
>
> 	.text
> LEAF (__setcontext)
>+	LPAD
>
> 	mv	t0, a0	/* Save ucp into t0.  */
>
>@@ -45,6 +47,55 @@ LEAF (__setcontext)
>
> 	cfi_def_cfa (t0, 0)
>
>+#ifdef __riscv_shadow_stack
>+	/* Skip if shadow stack is not enabled  */
>+	ssrdp	ra
>+	beqz	ra, .Lfin
>+	/* We are safe to adjust shadow stack after the sanity check  */
>+	REG_L	t1, UCONTEXT_SSP_BASE(t0)
>+	REG_L	a1, UCONTEXT_SSP(t0)
>+	REG_L   a2, SSP_BASE_OFFSET(tp)
>+	bne	t1, a2, .Ldifferent_stack
>+
>+.Lunwind:
>+	bleu    a1, ra, .Lfin
>+	/* 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  */
>+	/* ra = (a1 - ra >= 4096) ? ra + 4096 : a1  */
>+	lui     t2, 1
>+	add     ra, ra, t2
>+	bleu    ra, a1, 1f
>+	mv      ra, a1
>+1:
>+	csrw    ssp, ra
>+	/* Test if the location pointed by ssp is legal  */
>+	sspush  ra
>+	sspopchk ra
>+	j .Lunwind
>+
>+.Ldifferent_stack:
>+	/* Create restore token  */
>+	sspush  ra
>+	mv	a4, a1
>+
>+.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, a1
>+	REG_S   t1, SSP_BASE_OFFSET(tp)
>+.Lfin:
>+#endif
>+
> #ifndef __riscv_float_abi_soft
> 	lw	t1, MCONTEXT_FSR(t0)
>
>@@ -66,7 +117,11 @@ LEAF (__setcontext)
>
> 	/* Note the contents of argument registers will be random
> 	   unless makecontext() has been called.  */
>+#ifdef __riscv_landing_pad
>+	RESTORE_INT_REG     (t2,   0, t0)
>+#else
> 	RESTORE_INT_REG     (t1,   0, t0)
>+#endif
> 	RESTORE_INT_REG_CFI (ra,   1, t0)
> 	RESTORE_INT_REG     (sp,   2, t0)
> 	RESTORE_INT_REG_CFI (s0,   8, t0)
>@@ -90,7 +145,12 @@ LEAF (__setcontext)
> 	RESTORE_INT_REG_CFI (s10, 26, t0)
> 	RESTORE_INT_REG_CFI (s11, 27, t0)
>
>+#ifdef __riscv_landing_pad
>+	/* We need to use software-guared jump */
>+	jr	t2
>+#else
> 	jr	t1
>+#endif


Why not use SET_LPAD here and avoid sw guarded jump?

setcontext/swapcontext targets should be regular functions.
Aren't they? Users of setcontext/swapcontext expect to jump
in middle of function?

>
> 99:	tail	__syscall_error
>
>@@ -99,12 +159,19 @@ libc_hidden_def (__setcontext)
> weak_alias (__setcontext, setcontext)
>
> LEAF (__start_context)
>+	LPAD
>
> 	/* Terminate call stack by noting ra == 0.  Happily, s0 == 0 here.  */
> 	cfi_register (ra, s0)
>
> 	/* Call the function passed to makecontext.  */
>+#ifdef __riscv_landing_pad
>+	/* We need to use software-guared jump */
>+	mv	t2, s1
>+	jalr	t2
>+#else
> 	jalr	s1
>+#endif
>
> 	/* Invoke subsequent context if present, else exit(0).  */
> 	mv	a0, s2
>diff --git a/sysdeps/unix/sysv/linux/riscv/swapcontext.S b/sysdeps/unix/sysv/linux/riscv/swapcontext.S
>index bf5754c8b5..cb76eca9b1 100644
>--- a/sysdeps/unix/sysv/linux/riscv/swapcontext.S
>+++ b/sysdeps/unix/sysv/linux/riscv/swapcontext.S
>@@ -17,10 +17,12 @@
>    <https://www.gnu.org/licenses/>.  */
>
> #include "ucontext-macros.h"
>+#include "tcb-offsets.h"
>
> /* int swapcontext (ucontext_t *oucp, const ucontext_t *ucp) */
>
> LEAF (__swapcontext)
>+	LPAD
> 	mv	t0, a1			/* Save ucp into t0.  */
>
> 	SAVE_INT_REG (ra,   0, a0)
>@@ -59,6 +61,25 @@ LEAF (__swapcontext)
> 	sw	a1, MCONTEXT_FSR(a0)
> #endif /* __riscv_float_abi_soft */
>
>+#ifdef __riscv_shadow_stack
>+	/* Skip if shadow stack is not enabled */
>+	ssrdp	ra
>+	beqz	ra, .Lfin
>+
>+	/* Read ssp_base from TLS  */
>+	ld	t2, SSP_BASE_OFFSET(tp)
>+	bnez	t2, .Lbase_saved
>+
>+	/* if not found, use current ssp as the marker  */
>+	mv	t2, ra
>+	sd	t2, SSP_BASE_OFFSET(tp)
>+
>+.Lbase_saved:
>+	/* Save caller's ssp and base marker to oucp  */
>+	REG_S	t2, UCONTEXT_SSP_BASE(a0)
>+	REG_S	ra, UCONTEXT_SSP(a0)
>+#endif
>+
> /* rt_sigprocmask (SIG_SETMASK, &ucp->uc_sigmask, &oucp->uc_sigmask, _NSIG8) */
> 	li	a3, _NSIG8
> 	add	a2, a0, UCONTEXT_SIGMASK
>@@ -70,6 +91,52 @@ LEAF (__swapcontext)
>
> 	bltz	a0, 99f
>
>+#ifdef __riscv_shadow_stack
>+	/* Load ss information from ucp  */
>+	REG_L	a0, UCONTEXT_SSP_BASE(t0)
>+	REG_L	a1, UCONTEXT_SSP(t0)
>+	REG_L   a2, SSP_BASE_OFFSET(tp)
>+	bne	a0, a2, .Ldifferent_stack
>+
>+.Lunwind:
>+	bleu    a1, ra, .Lfin
>+	/* 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  */
>+	/* ra = (a1 - ra >= 4096) ? ra + 4096 : a1  */
>+	lui     t2, 1
>+	add     ra, ra, t2
>+	bleu    ra, a1, 1f
>+	mv      ra, a1
>+1:
>+	csrw    ssp, ra
>+	/* Test if the location pointed by ssp is legal  */
>+	sspush  ra
>+	sspopchk ra
>+	j .Lunwind
>+
>+.Ldifferent_stack:
>+	/* Create restore token  */
>+	sspush  ra
>+	mv	a4, a1
>+
>+.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, a1
>+	REG_S   a0, SSP_BASE_OFFSET(tp)
>+.Lfin:
>+#endif
>+
> #ifndef __riscv_float_abi_soft
> 	lw	t1, MCONTEXT_FSR(t0)
>
>@@ -91,7 +158,11 @@ LEAF (__swapcontext)
>
> 	/* Note the contents of argument registers will be random
> 	   unless makecontext() has been called.  */
>+#ifdef __riscv_landing_pad
>+	RESTORE_INT_REG (t2,   0, t0)
>+#else
> 	RESTORE_INT_REG (t1,   0, t0)
>+#endif
> 	RESTORE_INT_REG (ra,   1, t0)
> 	RESTORE_INT_REG (sp,   2, t0)
> 	RESTORE_INT_REG (s0,   8, t0)
>@@ -115,8 +186,12 @@ LEAF (__swapcontext)
> 	RESTORE_INT_REG (s10, 26, t0)
> 	RESTORE_INT_REG (s11, 27, t0)
>
>+#ifdef __riscv_landing_pad
>+	/* We need to use software-guared jump */
>+	jr	t2
>+#else
> 	jr	t1
>-
>+#endif
>
> 99:	tail	__syscall_error
>
>-- 
>2.39.3
>


More information about the Libc-alpha mailing list