[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