[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