Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Deepak Gupta and committed by
Paul Walmsley
66c9c713 9d42fc28

+158
+10
arch/riscv/include/asm/usercfi.h
··· 8 8 #ifndef __ASSEMBLER__ 9 9 #include <linux/types.h> 10 10 #include <linux/prctl.h> 11 + #include <linux/errno.h> 11 12 12 13 struct task_struct; 13 14 struct kernel_clone_args; ··· 35 34 bool is_shstk_allocated(struct task_struct *task); 36 35 void set_shstk_lock(struct task_struct *task); 37 36 void set_shstk_status(struct task_struct *task, bool enable); 37 + unsigned long get_active_shstk(struct task_struct *task); 38 + int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr); 39 + int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr); 38 40 bool is_indir_lp_enabled(struct task_struct *task); 39 41 bool is_indir_lp_locked(struct task_struct *task); 40 42 void set_indir_lp_status(struct task_struct *task, bool enable); ··· 74 70 #define set_indir_lp_status(task, enable) do {} while (0) 75 71 76 72 #define set_indir_lp_lock(task) do {} while (0) 73 + 74 + #define restore_user_shstk(tsk, shstk_ptr) -EINVAL 75 + 76 + #define save_user_shstk(tsk, saved_shstk_ptr) -EINVAL 77 + 78 + #define get_active_shstk(task) 0UL 77 79 78 80 #endif /* CONFIG_RISCV_USER_CFI */ 79 81
+4
arch/riscv/include/uapi/asm/ptrace.h
··· 127 127 */ 128 128 #define RISCV_MAX_VLENB (8192) 129 129 130 + struct __sc_riscv_cfi_state { 131 + unsigned long ss_ptr; /* shadow stack pointer */ 132 + }; 133 + 130 134 #endif /* __ASSEMBLER__ */ 131 135 132 136 #endif /* _UAPI_ASM_RISCV_PTRACE_H */
+1
arch/riscv/include/uapi/asm/sigcontext.h
··· 10 10 11 11 /* The Magic number for signal context frame header. */ 12 12 #define RISCV_V_MAGIC 0x53465457 13 + #define RISCV_ZICFISS_MAGIC 0x9487 13 14 #define END_MAGIC 0x0 14 15 15 16 /* The size of END signal context header. */
+86
arch/riscv/kernel/signal.c
··· 22 22 #include <asm/vector.h> 23 23 #include <asm/csr.h> 24 24 #include <asm/cacheflush.h> 25 + #include <asm/usercfi.h> 25 26 26 27 unsigned long signal_minsigstksz __ro_after_init; 27 28 28 29 extern u32 __user_rt_sigreturn[2]; 29 30 static size_t riscv_v_sc_size __ro_after_init; 31 + static size_t riscv_zicfiss_sc_size __ro_after_init; 30 32 31 33 #define DEBUG_SIG 0 32 34 ··· 142 140 return copy_from_user(current->thread.vstate.datap, datap, riscv_v_vsize); 143 141 } 144 142 143 + static long save_cfiss_state(struct pt_regs *regs, void __user *sc_cfi) 144 + { 145 + struct __sc_riscv_cfi_state __user *state = sc_cfi; 146 + unsigned long ss_ptr = 0; 147 + long err = 0; 148 + 149 + if (!is_shstk_enabled(current)) 150 + return 0; 151 + 152 + /* 153 + * Save a pointer to the shadow stack itself on shadow stack as a form of token. 154 + * A token on the shadow stack gives the following properties: 155 + * - Safe save and restore for shadow stack switching. Any save of a shadow stack 156 + * must have saved a token on the shadow stack. Similarly any restore of shadow 157 + * stack must check the token before restore. Since writing to the shadow stack with 158 + * address of the shadow stack itself is not easily allowed, a restore without a save 159 + * is quite difficult for an attacker to perform. 160 + * - A natural break. A token in shadow stack provides a natural break in shadow stack 161 + * So a single linear range can be bucketed into different shadow stack segments. Any 162 + * sspopchk will detect the condition and fault to kernel as a sw check exception. 163 + */ 164 + err |= save_user_shstk(current, &ss_ptr); 165 + err |= __put_user(ss_ptr, &state->ss_ptr); 166 + if (unlikely(err)) 167 + return -EFAULT; 168 + 169 + return riscv_zicfiss_sc_size; 170 + } 171 + 172 + static long __restore_cfiss_state(struct pt_regs *regs, void __user *sc_cfi) 173 + { 174 + struct __sc_riscv_cfi_state __user *state = sc_cfi; 175 + unsigned long ss_ptr = 0; 176 + long err; 177 + 178 + /* 179 + * Restore shadow stack as a form of token stored on the shadow stack itself as a safe 180 + * way to restore. 181 + * A token on the shadow stack gives the following properties: 182 + * - Safe save and restore for shadow stack switching. Any save of shadow stack 183 + * must have saved a token on shadow stack. Similarly any restore of shadow 184 + * stack must check the token before restore. Since writing to a shadow stack with 185 + * the address of shadow stack itself is not easily allowed, a restore without a save 186 + * is quite difficult for an attacker to perform. 187 + * - A natural break. A token in the shadow stack provides a natural break in shadow stack 188 + * So a single linear range can be bucketed into different shadow stack segments. 189 + * sspopchk will detect the condition and fault to kernel as a sw check exception. 190 + */ 191 + err = __copy_from_user(&ss_ptr, &state->ss_ptr, sizeof(unsigned long)); 192 + 193 + if (unlikely(err)) 194 + return err; 195 + 196 + return restore_user_shstk(current, ss_ptr); 197 + } 198 + 145 199 struct arch_ext_priv { 146 200 __u32 magic; 147 201 long (*save)(struct pt_regs *regs, void __user *sc_vec); ··· 207 149 { 208 150 .magic = RISCV_V_MAGIC, 209 151 .save = &save_v_state, 152 + }, 153 + { 154 + .magic = RISCV_ZICFISS_MAGIC, 155 + .save = &save_cfiss_state, 210 156 }, 211 157 }; 212 158 ··· 264 202 265 203 err = __restore_v_state(regs, sc_ext_ptr); 266 204 break; 205 + case RISCV_ZICFISS_MAGIC: 206 + if (!is_shstk_enabled(current) || size != riscv_zicfiss_sc_size) 207 + return -EINVAL; 208 + 209 + err = __restore_cfiss_state(regs, sc_ext_ptr); 210 + break; 267 211 default: 268 212 return -EINVAL; 269 213 } ··· 290 222 if (cal_all || riscv_v_vstate_query(task_pt_regs(current))) 291 223 total_context_size += riscv_v_sc_size; 292 224 } 225 + 226 + if (is_shstk_enabled(current)) 227 + total_context_size += riscv_zicfiss_sc_size; 228 + 229 + /* 230 + * Preserved a __riscv_ctx_hdr for END signal context header if an 231 + * extension uses __riscv_extra_ext_header 232 + */ 233 + if (total_context_size) 234 + total_context_size += sizeof(struct __riscv_ctx_hdr); 293 235 294 236 frame_size += total_context_size; 295 237 ··· 437 359 #ifdef CONFIG_MMU 438 360 regs->ra = (unsigned long)VDSO_SYMBOL( 439 361 current->mm->context.vdso, rt_sigreturn); 362 + 363 + /* if bcfi is enabled x1 (ra) and x5 (t0) must match. not sure if we need this? */ 364 + if (is_shstk_enabled(current)) 365 + regs->t0 = regs->ra; 366 + 440 367 #else 441 368 /* 442 369 * For the nommu case we don't have a VDSO. Instead we push two ··· 570 487 { 571 488 riscv_v_sc_size = sizeof(struct __riscv_ctx_hdr) + 572 489 sizeof(struct __sc_riscv_v_state) + riscv_v_vsize; 490 + 491 + riscv_zicfiss_sc_size = sizeof(struct __riscv_ctx_hdr) + 492 + sizeof(struct __sc_riscv_cfi_state); 573 493 /* 574 494 * Determine the stack space required for guaranteed signal delivery. 575 495 * The signal_minsigstksz will be populated into the AT_MINSIGSTKSZ entry
+57
arch/riscv/kernel/usercfi.c
··· 52 52 task->thread_info.user_cfi_state.user_shdw_stk = shstk_addr; 53 53 } 54 54 55 + unsigned long get_active_shstk(struct task_struct *task) 56 + { 57 + return task->thread_info.user_cfi_state.user_shdw_stk; 58 + } 59 + 55 60 void set_shstk_status(struct task_struct *task, bool enable) 56 61 { 57 62 if (!cpu_supports_shadow_stack()) ··· 170 165 if (token_addr) 171 166 *token_addr = addr; 172 167 168 + return 0; 169 + } 170 + 171 + /* 172 + * Save user shadow stack pointer on the shadow stack itself and return a pointer to saved location. 173 + * Returns -EFAULT if unsuccessful. 174 + */ 175 + int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr) 176 + { 177 + unsigned long ss_ptr = 0; 178 + unsigned long token_loc = 0; 179 + int ret = 0; 180 + 181 + if (!saved_shstk_ptr) 182 + return -EINVAL; 183 + 184 + ss_ptr = get_active_shstk(tsk); 185 + ret = create_rstor_token(ss_ptr, &token_loc); 186 + 187 + if (!ret) { 188 + *saved_shstk_ptr = token_loc; 189 + set_active_shstk(tsk, token_loc); 190 + } 191 + 192 + return ret; 193 + } 194 + 195 + /* 196 + * Restores the user shadow stack pointer from the token on the shadow stack for task 'tsk'. 197 + * Returns -EFAULT if unsuccessful. 198 + */ 199 + int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr) 200 + { 201 + unsigned long token = 0; 202 + 203 + token = amo_user_shstk((unsigned long __user *)shstk_ptr, 0); 204 + 205 + if (token == -1) 206 + return -EFAULT; 207 + 208 + /* invalid token, return EINVAL */ 209 + if ((token - shstk_ptr) != SHSTK_ENTRY_SIZE) { 210 + pr_info_ratelimited("%s[%d]: bad restore token in %s: pc=%p sp=%p, token=%p, shstk_ptr=%p\n", 211 + tsk->comm, task_pid_nr(tsk), __func__, 212 + (void *)(task_pt_regs(tsk)->epc), 213 + (void *)(task_pt_regs(tsk)->sp), 214 + (void *)token, (void *)shstk_ptr); 215 + return -EINVAL; 216 + } 217 + 218 + /* all checks passed, set active shstk and return success */ 219 + set_active_shstk(tsk, token); 173 220 return 0; 174 221 } 175 222