[v4,15/17] riscv/cfi: Support ucontext under CFI

Message ID 20260526061703.2188042-16-jesse.huang@sifive.com (mailing list archive)
State New
Headers
Series Support RISC-V Control Flow Integrifty (CFI) |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Jesse Huang May 26, 2026, 6:17 a.m. UTC
  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  | 19 +++++
 sysdeps/unix/sysv/linux/riscv/setcontext.S   | 67 ++++++++++++++++
 sysdeps/unix/sysv/linux/riscv/swapcontext.S  | 81 +++++++++++++++++++-
 sysdeps/unix/sysv/linux/riscv/ucontext_i.sym |  4 +-
 5 files changed, 189 insertions(+), 2 deletions(-)
  

Patch

diff --git a/sysdeps/unix/sysv/linux/riscv/getcontext.S b/sysdeps/unix/sysv/linux/riscv/getcontext.S
index fd55c3e7da..ff1512c2aa 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  */
+	REG_L	t1, TLS_SSP_BASE_OFFSET(tp)
+
+	bnez	t1, .Lbase_saved
+	/* if not found, save and use current ssp as the marker  */
+	mv	t1, t0
+	REG_S	t1, TLS_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 7f8ab46bd0..37c922c440 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,22 @@  __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->__saved.__ssp_base = shstk_size[0];
+  ucp->__saved.__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 9fd5f1f3cb..89942aec13 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, TLS_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, TLS_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
 
 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 4b3b0b3a14..72d48f7e26 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,26 @@  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, .Lsave_fin
+
+	/* Read ssp_base from TLS  */
+	REG_L	t2, TLS_SSP_BASE_OFFSET(tp)
+	bnez	t2, .Lbase_saved
+
+	/* if not found, use current ssp as the marker  */
+	mv	t2, ra
+	REG_S	t2, TLS_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)
+.Lsave_fin:
+#endif
+
 /* rt_sigprocmask (SIG_SETMASK, &ucp->uc_sigmask, &oucp->uc_sigmask, _NSIG8) */
 	li	a3, _NSIG8
 	add	a2, a0, UCONTEXT_SIGMASK
@@ -70,6 +92,55 @@  LEAF (__swapcontext)
 
 	bltz	a0, 99f
 
+#ifdef __riscv_shadow_stack
+	/* Skip if shadow stack is not enabled */
+	ssrdp	ra
+	beqz	ra, .Lfin
+	/* Load ss information from ucp  */
+	REG_L	a0, UCONTEXT_SSP_BASE(t0)
+	REG_L	a1, UCONTEXT_SSP(t0)
+	REG_L   a2, TLS_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, TLS_SSP_BASE_OFFSET(tp)
+.Lfin:
+#endif
+
 #ifndef __riscv_float_abi_soft
 	lw	t1, MCONTEXT_FSR(t0)
 
@@ -91,7 +162,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 +190,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
 
diff --git a/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym b/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym
index be55b26310..9a39f761ad 100644
--- a/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym
+++ b/sysdeps/unix/sysv/linux/riscv/ucontext_i.sym
@@ -19,7 +19,9 @@  UCONTEXT_FLAGS			ucontext (__uc_flags)
 UCONTEXT_LINK			ucontext (uc_link)
 UCONTEXT_STACK			ucontext (uc_stack)
 UCONTEXT_MCONTEXT		ucontext (uc_mcontext)
-UCONTEXT_SIGMASK		ucontext (uc_sigmask)
+UCONTEXT_SIGMASK		ucontext (__saved.__saved_mask)
+UCONTEXT_SSP			ucontext (__saved.__ssp)
+UCONTEXT_SSP_BASE		ucontext (__saved.__ssp_base)
 
 STACK_SP			stack (ss_sp)
 STACK_SIZE			stack (ss_size)