[PATCH v2 14/14] riscv/cfi: Support ucontext under CFI
Andrew Waterman
andrew@sifive.com
Wed Jul 23 01:01:44 GMT 2025
On Tue, Jul 22, 2025 at 3:37 PM Deepak Gupta <debug@rivosinc.com> wrote:
>
> 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?
Yeah, jumping into the middle of a function is the normal use case for
contexts established with getcontext/swapcontext, whereas makecontext
is normally used to establish a context with a function entry point.
>
> >
> > 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