diff --git a/sysdeps/riscv/Makefile b/sysdeps/riscv/Makefile
index b1f074a3eb..94e224615c 100644
--- a/sysdeps/riscv/Makefile
+++ b/sysdeps/riscv/Makefile
@@ -11,6 +11,10 @@ endif
 # of some assembler macros.
 ASFLAGS-.os += $(pic-ccflag)
 
+ifeq ($(subdir),setjmp)
+gen-as-const-headers += jmp_buf-ssp.sym
+endif
+
 ifeq (no,$(riscv-r-align))
 ASFLAGS-.os += -Wa,-mno-relax
 ASFLAGS-.o += -Wa,-mno-relax
diff --git a/sysdeps/riscv/__longjmp.S b/sysdeps/riscv/__longjmp.S
index bea854199c..8ee0662b81 100644
--- a/sysdeps/riscv/__longjmp.S
+++ b/sysdeps/riscv/__longjmp.S
@@ -18,9 +18,12 @@
 
 #include <sysdep.h>
 #include <sys/asm.h>
+#include <jmp_buf-ssp.h>
+#include <tcb-offsets.h>
 
 ENTRY (__longjmp)
 	REG_L ra,  0*SZREG(a0)
+	REG_L t1,  0*SZREG(a0)
 	REG_L s0,  1*SZREG(a0)
 	REG_L s1,  2*SZREG(a0)
 	REG_L s2,  3*SZREG(a0)
@@ -50,8 +53,59 @@ ENTRY (__longjmp)
 	FREG_L fs11,14*SZREG+11*SZFREG(a0)
 #endif
 
+#ifdef __riscv_shadow_stack
+	/* skip unwinding if ss is not enabled  */
+	ssrdp	ra
+	beqz	ra, .Lfin
+	REG_L	t0, SSP_OFFSET(a0)
+	REG_L	a0, SSP_BASE_OFFSET(a0)
+	REG_L	t2, TLS_SSP_BASE_OFFSET(tp)
+	bne	a0, t2, .Ldifferent_stack
+.Lunwind:
+	bleu  t0, ra, .Lfin
+	/* Increase ssp by at most one page size to ensure the adjustment
+	   always runs into a guard page before accidentally pointing to
+	   another legal shadow stack page  */
+	/* ra = (t0 - ra >= 4096) ? ra + 4096 : t0  */
+	lui   a0, 1
+	add   ra, ra, a0
+	bleu  ra, t0, 1f
+	mv    ra, t0
+1:
+	csrw  ssp, ra
+	/* Test if the location pointed by ssp is legal  */
+	sspush x5
+	sspopchk x5
+	j .Lunwind
+.Ldifferent_stack:
+	/* Create restore token  */
+	sspush  ra
+	mv	a4, t0
+
+.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, t0
+	REG_S   a0, TLS_SSP_BASE_OFFSET(tp)
+.Lfin:
+#endif
 	seqz a0, a1
 	add  a0, a0, a1   # a0 = (a1 == 0) ? 1 : a1
+#ifdef __riscv_landing_pad
+	/* Use indirect branch if CFI is enabled  */
+	jr   t1
+#else
+	mv   ra, t1
 	ret
+#endif
 
 END (__longjmp)
diff --git a/sysdeps/riscv/setjmp.S b/sysdeps/riscv/setjmp.S
index af1910b86d..0406b23956 100644
--- a/sysdeps/riscv/setjmp.S
+++ b/sysdeps/riscv/setjmp.S
@@ -18,6 +18,8 @@
 
 #include <sysdep.h>
 #include <sys/asm.h>
+#include <jmp_buf-ssp.h>
+#include <tcb-offsets.h>
 
 ENTRY (_setjmp)
   li	a1, 0
@@ -58,6 +60,26 @@ ENTRY (__sigsetjmp)
 	FREG_S fs11,14*SZREG+11*SZFREG(a0)
 #endif
 
+#ifdef __riscv_shadow_stack
+	/* Skip if shadow stack is not enabled */
+	ssrdp	t0
+	beqz	t0, .Lfin
+
+	/* Read ssp_base from TLS  */
+	REG_L	t2, TLS_SSP_BASE_OFFSET(tp)
+	bnez	t2, .Lbase_saved
+
+	/* if not found, use current ssp as the marker  */
+	mv	t2, t0
+	REG_S	t2, TLS_SSP_BASE_OFFSET(tp)
+
+.Lbase_saved:
+	/* Save caller's ssp and base marker to jmp_buf  */
+	REG_S	t0, SSP_OFFSET(a0)
+	REG_S	t2, SSP_BASE_OFFSET(a0)
+.Lfin:
+#endif
+
 #if !IS_IN (libc) && IS_IN (rtld)
   /* In ld.so we never save the signal mask.  */
   li a0, 0
diff --git a/sysdeps/unix/sysv/linux/riscv/jmp_buf-ssp.sym b/sysdeps/unix/sysv/linux/riscv/jmp_buf-ssp.sym
new file mode 100644
index 0000000000..bf944969f7
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/riscv/jmp_buf-ssp.sym
@@ -0,0 +1,7 @@
+#include <setjmpP.h>
+#include <stddef.h>
+#undef __saved_mask
+
+--
+SSP_OFFSET offsetof(struct __jmp_buf_tag, __saved_mask.__saved.__ssp)
+SSP_BASE_OFFSET offsetof(struct __jmp_buf_tag, __saved_mask.__saved.__ssp_base)
diff --git a/sysdeps/unix/sysv/linux/riscv/setjmpP.h b/sysdeps/unix/sysv/linux/riscv/setjmpP.h
new file mode 100644
index 0000000000..43cb28e2d1
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/riscv/setjmpP.h
@@ -0,0 +1,78 @@
+/* Internal header file for <setjmp.h>.  Linux/risc-v version.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef	_SETJMPP_H
+#define	_SETJMPP_H	1
+
+#include <bits/types/__sigset_t.h>
+#include <libc-pointer-arith.h>
+#include <sigsetops.h>
+
+/* Number of bits per long.  */
+#define _JUMP_BUF_SIGSET_BITS_PER_WORD (8 * sizeof (unsigned long int))
+/* This holds the number of signals, 512 should be sufficient for future.
+   expansion  */
+#define _JUMP_BUF_SIGSET_NSIG	512
+/* Number of longs to hold all signals.  */
+#define _JUMP_BUF_SIGSET_NWORDS \
+  (ALIGN_UP (_JUMP_BUF_SIGSET_NSIG, _JUMP_BUF_SIGSET_BITS_PER_WORD) \
+   / _JUMP_BUF_SIGSET_BITS_PER_WORD)
+
+typedef struct
+  {
+    unsigned long int __val[_JUMP_BUF_SIGSET_NWORDS];
+  } __jmp_buf_sigset_t;
+
+typedef union
+  {
+    __sigset_t __saved_mask_compat;
+    struct
+      {
+	__jmp_buf_sigset_t __saved_mask;
+	/* Used for shadow stack pointer.  NB: Shadow stack pointer
+	   must have the same alignment as __saved_mask.  Otherwise
+	   offset of __saved_mask will be changed.  */
+	unsigned long int __ssp;
+	unsigned long int __ssp_base;
+      } __saved;
+  } __jmpbuf_arch_t;
+
+/* <setjmp/setjmp.h> has
+
+   NB: We use setjmp in thread cancellation and this saves the shadow
+   stack register, but __libc_unwind_longjmp doesn't restore the shadow
+   stack register since cancellation never returns after longjmp.  */
+#undef __sigset_t
+#define __sigset_t __jmpbuf_arch_t
+#include <setjmp.h>
+#undef __saved_mask
+#define __saved_mask __saved_mask.__saved.__saved_mask
+
+#include <signal.h>
+
+typedef struct
+  {
+    unsigned long int __val[__NSIG_WORDS];
+  } __sigprocmask_sigset_t;
+
+extern jmp_buf ___buf;
+extern  __typeof (___buf[0].__saved_mask) ___saved_mask;
+_Static_assert (sizeof (___saved_mask) >= sizeof (__sigprocmask_sigset_t),
+		"size of ___saved_mask < size of __sigprocmask_sigset_t");
+
+#endif /* setjmpP.h  */
