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

Jesse Huang jesse.huang@sifive.com
Tue Jul 29 05:24:39 GMT 2025


I think your way is having problems because it's introducing asymmetry on
the shadow stack.
It sounds like swapcontext-saved context will only be consumed by another
swapcontext.
However, we cannot expect we are switching into a context saved by a
specific function,
setcontext into a swapcontext-saved context, swapcontext into a
getcontext-saved context,
these are all legal usage of the library that we need to support, if only
swapcontext adds an
extra slot on the shadow stack the restorer must be able to distinguish by
what calls the target
context is saved to know how to deal with that extra slot.

On Tue, Jul 29, 2025 at 12:29 AM Deepak Gupta <debug@rivosinc.com> wrote:

> On Mon, Jul 28, 2025 at 09:52:49PM +0800, Jesse Huang wrote:
> >getcontext()/setcontext() can be used as setjmp()/longjmp(), which we
> >expect to jump to anywhere in the
>
> Not anywhere. Only to the PC subsequent to call `getcontext/setjmp`.
>
> >middle of a function where the checkpoint is established. We can't use
> >normal jump unless we also insert
> >landing pad in any possible jump target(right after the call to
> >getcontext()/swapcontext()), the label will
> >also be another problem.
>
> yes we can't insert lpad at such places.
>
> >
> >swapcontext() is simply getcontext() followed by a setcontext(), although
> >the scenario you described could
> >be a possibility, we cannot rely on any specific usage of the call,
> >otherwise we break the compatibility.
> >
> >It is possible that swapcontext() restore to an earlier context that was
> >set in an outer function, where the
> >target return address in the shadow stack can be apart from the token for
> >several slots, so the technique
> >you described does not work well in this case.
>
> swapcontext is unique because, going away thread context is frozen inside
> swapcontext and incoming thread becomes live as soon as swapcontext
> returns.
> This allows swapcontext to save return address of going away thread on
> shadow
> stack of going away thread (without worrying about its clobbering). Thus
> using
> software guarded jump is safer with swapcontext because it can always load
> return address from shadow stack.
>
> This becomes tricky with getcontext because getcontext will return and thus
> saving return address shadow stack will not possible.
>
> In this case, perhaps best is what we have done with setjmp/longjmp.
>
> >
> >
> >On Wed, Jul 23, 2025 at 9:32 AM Deepak Gupta <debug@rivosinc.com> wrote:
> >
> >> On Tue, Jul 22, 2025 at 06:01:44PM -0700, Andrew Waterman wrote:
> >> >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
> >>
> >>
> >> I think this can be done like this.
> >>
> >> #ifdef __riscv_landing_pad
> >>          mv t2, t1
> >>          jr t2
> >> #else
> >>          jr t1
> >> #endif
> >>
> >> Although there is probably a better way (see below)
> >>
> >> >>
> >> >>
> >> >> 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.
> >>
> >> In case of swapcontext, I expect consumer of swapcontext is something
> >> like a userlevel thread scheduler where a higher level api like
> >> "yield_coroutine" is eventually calling into `swapcontext`. In such a
> >> case `swapcontext` can simply push return address of the caller of
> >> `swapcontext` on outgoing shadow stack and then save the token.
> >>
> >> Then `swapcontext` can validate token on top of incoming shadow stack,
> >> if token check is success then consume address (next to token) from
> >> incoming shadow stack and then do `jr t2`. This ensures that we are
> >> going back yield point of incoming thread and no where else (shadow
> >> stack ensures provides that RO guarantee).
> >>
> >> I don't know how getcontext/setcontext are used together to user level
> >> thread scheduling.
> >>
> >> But certainly incase of `swapcontext` I think above code can leverage
> >> shadow stack to ensure that sw guarded jump is relying RO property of
> >> shadow stack.
> >>
> >> >
> >> >>
> >> >> >
> >> >> > 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
> >> >> >
> >>
>

On Tue, Jul 29, 2025 at 12:29 AM Deepak Gupta <debug@rivosinc.com> wrote:

> On Mon, Jul 28, 2025 at 09:52:49PM +0800, Jesse Huang wrote:
> >getcontext()/setcontext() can be used as setjmp()/longjmp(), which we
> >expect to jump to anywhere in the
>
> Not anywhere. Only to the PC subsequent to call `getcontext/setjmp`.
>
> >middle of a function where the checkpoint is established. We can't use
> >normal jump unless we also insert
> >landing pad in any possible jump target(right after the call to
> >getcontext()/swapcontext()), the label will
> >also be another problem.
>
> yes we can't insert lpad at such places.
>
> >
> >swapcontext() is simply getcontext() followed by a setcontext(), although
> >the scenario you described could
> >be a possibility, we cannot rely on any specific usage of the call,
> >otherwise we break the compatibility.
> >
> >It is possible that swapcontext() restore to an earlier context that was
> >set in an outer function, where the
> >target return address in the shadow stack can be apart from the token for
> >several slots, so the technique
> >you described does not work well in this case.
>
> swapcontext is unique because, going away thread context is frozen inside
> swapcontext and incoming thread becomes live as soon as swapcontext
> returns.
> This allows swapcontext to save return address of going away thread on
> shadow
> stack of going away thread (without worrying about its clobbering). Thus
> using
> software guarded jump is safer with swapcontext because it can always load
> return address from shadow stack.
>
> This becomes tricky with getcontext because getcontext will return and thus
> saving return address shadow stack will not possible.
>
> In this case, perhaps best is what we have done with setjmp/longjmp.
>
> >
> >
> >On Wed, Jul 23, 2025 at 9:32 AM Deepak Gupta <debug@rivosinc.com> wrote:
> >
> >> On Tue, Jul 22, 2025 at 06:01:44PM -0700, Andrew Waterman wrote:
> >> >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
> >>
> >>
> >> I think this can be done like this.
> >>
> >> #ifdef __riscv_landing_pad
> >>          mv t2, t1
> >>          jr t2
> >> #else
> >>          jr t1
> >> #endif
> >>
> >> Although there is probably a better way (see below)
> >>
> >> >>
> >> >>
> >> >> 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.
> >>
> >> In case of swapcontext, I expect consumer of swapcontext is something
> >> like a userlevel thread scheduler where a higher level api like
> >> "yield_coroutine" is eventually calling into `swapcontext`. In such a
> >> case `swapcontext` can simply push return address of the caller of
> >> `swapcontext` on outgoing shadow stack and then save the token.
> >>
> >> Then `swapcontext` can validate token on top of incoming shadow stack,
> >> if token check is success then consume address (next to token) from
> >> incoming shadow stack and then do `jr t2`. This ensures that we are
> >> going back yield point of incoming thread and no where else (shadow
> >> stack ensures provides that RO guarantee).
> >>
> >> I don't know how getcontext/setcontext are used together to user level
> >> thread scheduling.
> >>
> >> But certainly incase of `swapcontext` I think above code can leverage
> >> shadow stack to ensure that sw guarded jump is relying RO property of
> >> shadow stack.
> >>
> >> >
> >> >>
> >> >> >
> >> >> > 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
> >> >> >
> >>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20250729/9655519b/attachment-0001.htm>


More information about the Libc-alpha mailing list