Skip to content

Commit 66c9c71

Browse files
deepak0414Paul Walmsley
authored andcommitted
riscv/signal: save and restore the shadow stack on a signal
Save the shadow stack pointer in the sigcontext structure when delivering a signal. Restore the shadow stack pointer from sigcontext on sigreturn. As part of the save operation, the kernel uses the 'ssamoswap' instruction to save a snapshot of the current shadow stack on the shadow stack itself (this can be called a "save token"). During restore on sigreturn, the kernel retrieves the save token from the top of the shadow stack and validates it. This ensures that user mode can't arbitrarily pivot to any shadow stack address without having a token and thus provides a strong security assurance during the window between signal delivery and sigreturn. Use an ABI-compatible way of saving/restoring the shadow stack pointer into the signal stack. This follows the vector extension, where extra registers are placed in a form of extension header + extension body in the stack. The extension header indicates the size of the extra architectural states plus the size of header itself, and a magic identifier for the extension. Then, the extension body contains the new architectural states in the form defined by uapi. Signed-off-by: Andy Chiu <andy.chiu@sifive.com> Signed-off-by: Deepak Gupta <debug@rivosinc.com> Tested-by: Andreas Korb <andreas.korb@aisec.fraunhofer.de> Tested-by: Valentin Haudiquet <valentin.haudiquet@canonical.com> Link: https://patch.msgid.link/20251112-v5_user_cfi_series-v23-17-b55691eacf4f@rivosinc.com [pjw@kernel.org: cleaned patch description, code comments; resolved checkpatch warning] Signed-off-by: Paul Walmsley <pjw@kernel.org>
1 parent 9d42fc2 commit 66c9c71

5 files changed

Lines changed: 158 additions & 0 deletions

File tree

arch/riscv/include/asm/usercfi.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#ifndef __ASSEMBLER__
99
#include <linux/types.h>
1010
#include <linux/prctl.h>
11+
#include <linux/errno.h>
1112

1213
struct task_struct;
1314
struct kernel_clone_args;
@@ -34,6 +35,9 @@ bool is_shstk_locked(struct task_struct *task);
3435
bool is_shstk_allocated(struct task_struct *task);
3536
void set_shstk_lock(struct task_struct *task);
3637
void set_shstk_status(struct task_struct *task, bool enable);
38+
unsigned long get_active_shstk(struct task_struct *task);
39+
int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr);
40+
int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr);
3741
bool is_indir_lp_enabled(struct task_struct *task);
3842
bool is_indir_lp_locked(struct task_struct *task);
3943
void set_indir_lp_status(struct task_struct *task, bool enable);
@@ -71,6 +75,12 @@ void set_indir_lp_lock(struct task_struct *task);
7175

7276
#define set_indir_lp_lock(task) do {} while (0)
7377

78+
#define restore_user_shstk(tsk, shstk_ptr) -EINVAL
79+
80+
#define save_user_shstk(tsk, saved_shstk_ptr) -EINVAL
81+
82+
#define get_active_shstk(task) 0UL
83+
7484
#endif /* CONFIG_RISCV_USER_CFI */
7585

7686
#endif /* __ASSEMBLER__ */

arch/riscv/include/uapi/asm/ptrace.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ struct __riscv_v_regset_state {
127127
*/
128128
#define RISCV_MAX_VLENB (8192)
129129

130+
struct __sc_riscv_cfi_state {
131+
unsigned long ss_ptr; /* shadow stack pointer */
132+
};
133+
130134
#endif /* __ASSEMBLER__ */
131135

132136
#endif /* _UAPI_ASM_RISCV_PTRACE_H */

arch/riscv/include/uapi/asm/sigcontext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
/* The Magic number for signal context frame header. */
1212
#define RISCV_V_MAGIC 0x53465457
13+
#define RISCV_ZICFISS_MAGIC 0x9487
1314
#define END_MAGIC 0x0
1415

1516
/* The size of END signal context header. */

arch/riscv/kernel/signal.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
#include <asm/vector.h>
2323
#include <asm/csr.h>
2424
#include <asm/cacheflush.h>
25+
#include <asm/usercfi.h>
2526

2627
unsigned long signal_minsigstksz __ro_after_init;
2728

2829
extern u32 __user_rt_sigreturn[2];
2930
static size_t riscv_v_sc_size __ro_after_init;
31+
static size_t riscv_zicfiss_sc_size __ro_after_init;
3032

3133
#define DEBUG_SIG 0
3234

@@ -140,6 +142,62 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec)
140142
return copy_from_user(current->thread.vstate.datap, datap, riscv_v_vsize);
141143
}
142144

145+
static long save_cfiss_state(struct pt_regs *regs, void __user *sc_cfi)
146+
{
147+
struct __sc_riscv_cfi_state __user *state = sc_cfi;
148+
unsigned long ss_ptr = 0;
149+
long err = 0;
150+
151+
if (!is_shstk_enabled(current))
152+
return 0;
153+
154+
/*
155+
* Save a pointer to the shadow stack itself on shadow stack as a form of token.
156+
* A token on the shadow stack gives the following properties:
157+
* - Safe save and restore for shadow stack switching. Any save of a shadow stack
158+
* must have saved a token on the shadow stack. Similarly any restore of shadow
159+
* stack must check the token before restore. Since writing to the shadow stack with
160+
* address of the shadow stack itself is not easily allowed, a restore without a save
161+
* is quite difficult for an attacker to perform.
162+
* - A natural break. A token in shadow stack provides a natural break in shadow stack
163+
* So a single linear range can be bucketed into different shadow stack segments. Any
164+
* sspopchk will detect the condition and fault to kernel as a sw check exception.
165+
*/
166+
err |= save_user_shstk(current, &ss_ptr);
167+
err |= __put_user(ss_ptr, &state->ss_ptr);
168+
if (unlikely(err))
169+
return -EFAULT;
170+
171+
return riscv_zicfiss_sc_size;
172+
}
173+
174+
static long __restore_cfiss_state(struct pt_regs *regs, void __user *sc_cfi)
175+
{
176+
struct __sc_riscv_cfi_state __user *state = sc_cfi;
177+
unsigned long ss_ptr = 0;
178+
long err;
179+
180+
/*
181+
* Restore shadow stack as a form of token stored on the shadow stack itself as a safe
182+
* way to restore.
183+
* A token on the shadow stack gives the following properties:
184+
* - Safe save and restore for shadow stack switching. Any save of shadow stack
185+
* must have saved a token on shadow stack. Similarly any restore of shadow
186+
* stack must check the token before restore. Since writing to a shadow stack with
187+
* the address of shadow stack itself is not easily allowed, a restore without a save
188+
* is quite difficult for an attacker to perform.
189+
* - A natural break. A token in the shadow stack provides a natural break in shadow stack
190+
* So a single linear range can be bucketed into different shadow stack segments.
191+
* sspopchk will detect the condition and fault to kernel as a sw check exception.
192+
*/
193+
err = __copy_from_user(&ss_ptr, &state->ss_ptr, sizeof(unsigned long));
194+
195+
if (unlikely(err))
196+
return err;
197+
198+
return restore_user_shstk(current, ss_ptr);
199+
}
200+
143201
struct arch_ext_priv {
144202
__u32 magic;
145203
long (*save)(struct pt_regs *regs, void __user *sc_vec);
@@ -150,6 +208,10 @@ static struct arch_ext_priv arch_ext_list[] = {
150208
.magic = RISCV_V_MAGIC,
151209
.save = &save_v_state,
152210
},
211+
{
212+
.magic = RISCV_ZICFISS_MAGIC,
213+
.save = &save_cfiss_state,
214+
},
153215
};
154216

155217
static const size_t nr_arch_exts = ARRAY_SIZE(arch_ext_list);
@@ -202,6 +264,12 @@ static long restore_sigcontext(struct pt_regs *regs,
202264

203265
err = __restore_v_state(regs, sc_ext_ptr);
204266
break;
267+
case RISCV_ZICFISS_MAGIC:
268+
if (!is_shstk_enabled(current) || size != riscv_zicfiss_sc_size)
269+
return -EINVAL;
270+
271+
err = __restore_cfiss_state(regs, sc_ext_ptr);
272+
break;
205273
default:
206274
return -EINVAL;
207275
}
@@ -223,6 +291,16 @@ static size_t get_rt_frame_size(bool cal_all)
223291
total_context_size += riscv_v_sc_size;
224292
}
225293

294+
if (is_shstk_enabled(current))
295+
total_context_size += riscv_zicfiss_sc_size;
296+
297+
/*
298+
* Preserved a __riscv_ctx_hdr for END signal context header if an
299+
* extension uses __riscv_extra_ext_header
300+
*/
301+
if (total_context_size)
302+
total_context_size += sizeof(struct __riscv_ctx_hdr);
303+
226304
frame_size += total_context_size;
227305

228306
frame_size = round_up(frame_size, 16);
@@ -359,6 +437,11 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
359437
#ifdef CONFIG_MMU
360438
regs->ra = (unsigned long)VDSO_SYMBOL(
361439
current->mm->context.vdso, rt_sigreturn);
440+
441+
/* if bcfi is enabled x1 (ra) and x5 (t0) must match. not sure if we need this? */
442+
if (is_shstk_enabled(current))
443+
regs->t0 = regs->ra;
444+
362445
#else
363446
/*
364447
* For the nommu case we don't have a VDSO. Instead we push two
@@ -487,6 +570,9 @@ void __init init_rt_signal_env(void)
487570
{
488571
riscv_v_sc_size = sizeof(struct __riscv_ctx_hdr) +
489572
sizeof(struct __sc_riscv_v_state) + riscv_v_vsize;
573+
574+
riscv_zicfiss_sc_size = sizeof(struct __riscv_ctx_hdr) +
575+
sizeof(struct __sc_riscv_cfi_state);
490576
/*
491577
* Determine the stack space required for guaranteed signal delivery.
492578
* The signal_minsigstksz will be populated into the AT_MINSIGSTKSZ entry

arch/riscv/kernel/usercfi.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ void set_active_shstk(struct task_struct *task, unsigned long shstk_addr)
5252
task->thread_info.user_cfi_state.user_shdw_stk = shstk_addr;
5353
}
5454

55+
unsigned long get_active_shstk(struct task_struct *task)
56+
{
57+
return task->thread_info.user_cfi_state.user_shdw_stk;
58+
}
59+
5560
void set_shstk_status(struct task_struct *task, bool enable)
5661
{
5762
if (!cpu_supports_shadow_stack())
@@ -168,6 +173,58 @@ static int create_rstor_token(unsigned long ssp, unsigned long *token_addr)
168173
return 0;
169174
}
170175

176+
/*
177+
* Save user shadow stack pointer on the shadow stack itself and return a pointer to saved location.
178+
* Returns -EFAULT if unsuccessful.
179+
*/
180+
int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr)
181+
{
182+
unsigned long ss_ptr = 0;
183+
unsigned long token_loc = 0;
184+
int ret = 0;
185+
186+
if (!saved_shstk_ptr)
187+
return -EINVAL;
188+
189+
ss_ptr = get_active_shstk(tsk);
190+
ret = create_rstor_token(ss_ptr, &token_loc);
191+
192+
if (!ret) {
193+
*saved_shstk_ptr = token_loc;
194+
set_active_shstk(tsk, token_loc);
195+
}
196+
197+
return ret;
198+
}
199+
200+
/*
201+
* Restores the user shadow stack pointer from the token on the shadow stack for task 'tsk'.
202+
* Returns -EFAULT if unsuccessful.
203+
*/
204+
int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr)
205+
{
206+
unsigned long token = 0;
207+
208+
token = amo_user_shstk((unsigned long __user *)shstk_ptr, 0);
209+
210+
if (token == -1)
211+
return -EFAULT;
212+
213+
/* invalid token, return EINVAL */
214+
if ((token - shstk_ptr) != SHSTK_ENTRY_SIZE) {
215+
pr_info_ratelimited("%s[%d]: bad restore token in %s: pc=%p sp=%p, token=%p, shstk_ptr=%p\n",
216+
tsk->comm, task_pid_nr(tsk), __func__,
217+
(void *)(task_pt_regs(tsk)->epc),
218+
(void *)(task_pt_regs(tsk)->sp),
219+
(void *)token, (void *)shstk_ptr);
220+
return -EINVAL;
221+
}
222+
223+
/* all checks passed, set active shstk and return success */
224+
set_active_shstk(tsk, token);
225+
return 0;
226+
}
227+
171228
static unsigned long allocate_shadow_stack(unsigned long addr, unsigned long size,
172229
unsigned long token_offset, bool set_tok)
173230
{

0 commit comments

Comments
 (0)