[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