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: Implement arch-agnostic shadow stack prctls

Implement an architecture-agnostic prctl() interface for setting and
getting shadow stack status. The prctls implemented are
PR_GET_SHADOW_STACK_STATUS, PR_SET_SHADOW_STACK_STATUS and
PR_LOCK_SHADOW_STACK_STATUS.

As part of PR_SET_SHADOW_STACK_STATUS/PR_GET_SHADOW_STACK_STATUS, only
PR_SHADOW_STACK_ENABLE is implemented because RISCV allows each mode to
write to their own shadow stack using 'sspush' or 'ssamoswap'.

PR_LOCK_SHADOW_STACK_STATUS locks the current shadow stack enablement
configuration.

Reviewed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Tested-by: Andreas Korb <andreas.korb@aisec.fraunhofer.de> # QEMU, custom CVA6
Tested-by: Valentin Haudiquet <valentin.haudiquet@canonical.com>
Link: https://patch.msgid.link/20251112-v5_user_cfi_series-v23-12-b55691eacf4f@rivosinc.com
[pjw@kernel.org: cleaned up patch description]
Signed-off-by: Paul Walmsley <pjw@kernel.org>

authored by

Deepak Gupta and committed by
Paul Walmsley
61a02002 fd44a4a8

+134
+16
arch/riscv/include/asm/usercfi.h
··· 7 7 8 8 #ifndef __ASSEMBLER__ 9 9 #include <linux/types.h> 10 + #include <linux/prctl.h> 10 11 11 12 struct task_struct; 12 13 struct kernel_clone_args; ··· 15 14 #ifdef CONFIG_RISCV_USER_CFI 16 15 struct cfi_state { 17 16 unsigned long ubcfi_en : 1; /* Enable for backward cfi. */ 17 + unsigned long ubcfi_locked : 1; 18 18 unsigned long user_shdw_stk; /* Current user shadow stack pointer */ 19 19 unsigned long shdw_stk_base; /* Base address of shadow stack */ 20 20 unsigned long shdw_stk_size; /* size of shadow stack */ ··· 28 26 unsigned long get_shstk_base(struct task_struct *task, unsigned long *size); 29 27 void set_active_shstk(struct task_struct *task, unsigned long shstk_addr); 30 28 bool is_shstk_enabled(struct task_struct *task); 29 + bool is_shstk_locked(struct task_struct *task); 30 + bool is_shstk_allocated(struct task_struct *task); 31 + void set_shstk_lock(struct task_struct *task); 32 + void set_shstk_status(struct task_struct *task, bool enable); 33 + 34 + #define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE) 31 35 32 36 #else 33 37 ··· 48 40 #define set_active_shstk(task, shstk_addr) do {} while (0) 49 41 50 42 #define is_shstk_enabled(task) false 43 + 44 + #define is_shstk_locked(task) false 45 + 46 + #define is_shstk_allocated(task) false 47 + 48 + #define set_shstk_lock(task) do {} while (0) 49 + 50 + #define set_shstk_status(task, enable) do {} while (0) 51 51 52 52 #endif /* CONFIG_RISCV_USER_CFI */ 53 53
+8
arch/riscv/kernel/process.c
··· 156 156 regs->epc = pc; 157 157 regs->sp = sp; 158 158 159 + /* 160 + * clear shadow stack state on exec. 161 + * libc will set it later via prctl. 162 + */ 163 + set_shstk_status(current, false); 164 + set_shstk_base(current, 0, 0); 165 + set_active_shstk(current, 0); 166 + 159 167 #ifdef CONFIG_64BIT 160 168 regs->status &= ~SR_UXL; 161 169
+110
arch/riscv/kernel/usercfi.c
··· 24 24 return task->thread_info.user_cfi_state.ubcfi_en; 25 25 } 26 26 27 + bool is_shstk_allocated(struct task_struct *task) 28 + { 29 + return task->thread_info.user_cfi_state.shdw_stk_base; 30 + } 31 + 32 + bool is_shstk_locked(struct task_struct *task) 33 + { 34 + return task->thread_info.user_cfi_state.ubcfi_locked; 35 + } 36 + 27 37 void set_shstk_base(struct task_struct *task, unsigned long shstk_addr, unsigned long size) 28 38 { 29 39 task->thread_info.user_cfi_state.shdw_stk_base = shstk_addr; ··· 50 40 void set_active_shstk(struct task_struct *task, unsigned long shstk_addr) 51 41 { 52 42 task->thread_info.user_cfi_state.user_shdw_stk = shstk_addr; 43 + } 44 + 45 + void set_shstk_status(struct task_struct *task, bool enable) 46 + { 47 + if (!cpu_supports_shadow_stack()) 48 + return; 49 + 50 + task->thread_info.user_cfi_state.ubcfi_en = enable ? 1 : 0; 51 + 52 + if (enable) 53 + task->thread.envcfg |= ENVCFG_SSE; 54 + else 55 + task->thread.envcfg &= ~ENVCFG_SSE; 56 + 57 + csr_write(CSR_ENVCFG, task->thread.envcfg); 58 + } 59 + 60 + void set_shstk_lock(struct task_struct *task) 61 + { 62 + task->thread_info.user_cfi_state.ubcfi_locked = 1; 53 63 } 54 64 55 65 /* ··· 287 257 288 258 vm_munmap(base, size); 289 259 set_shstk_base(tsk, 0, 0); 260 + } 261 + 262 + int arch_get_shadow_stack_status(struct task_struct *t, unsigned long __user *status) 263 + { 264 + unsigned long bcfi_status = 0; 265 + 266 + if (!cpu_supports_shadow_stack()) 267 + return -EINVAL; 268 + 269 + /* this means shadow stack is enabled on the task */ 270 + bcfi_status |= (is_shstk_enabled(t) ? PR_SHADOW_STACK_ENABLE : 0); 271 + 272 + return copy_to_user(status, &bcfi_status, sizeof(bcfi_status)) ? -EFAULT : 0; 273 + } 274 + 275 + int arch_set_shadow_stack_status(struct task_struct *t, unsigned long status) 276 + { 277 + unsigned long size = 0, addr = 0; 278 + bool enable_shstk = false; 279 + 280 + if (!cpu_supports_shadow_stack()) 281 + return -EINVAL; 282 + 283 + /* Reject unknown flags */ 284 + if (status & ~PR_SHADOW_STACK_SUPPORTED_STATUS_MASK) 285 + return -EINVAL; 286 + 287 + /* bcfi status is locked and further can't be modified by user */ 288 + if (is_shstk_locked(t)) 289 + return -EINVAL; 290 + 291 + enable_shstk = status & PR_SHADOW_STACK_ENABLE; 292 + /* Request is to enable shadow stack and shadow stack is not enabled already */ 293 + if (enable_shstk && !is_shstk_enabled(t)) { 294 + /* shadow stack was allocated and enable request again 295 + * no need to support such usecase and return EINVAL. 296 + */ 297 + if (is_shstk_allocated(t)) 298 + return -EINVAL; 299 + 300 + size = calc_shstk_size(0); 301 + addr = allocate_shadow_stack(0, size, 0, false); 302 + if (IS_ERR_VALUE(addr)) 303 + return -ENOMEM; 304 + set_shstk_base(t, addr, size); 305 + set_active_shstk(t, addr + size); 306 + } 307 + 308 + /* 309 + * If a request to disable shadow stack happens, let's go ahead and release it 310 + * Although, if CLONE_VFORKed child did this, then in that case we will end up 311 + * not releasing the shadow stack (because it might be needed in parent). Although 312 + * we will disable it for VFORKed child. And if VFORKed child tries to enable again 313 + * then in that case, it'll get entirely new shadow stack because following condition 314 + * are true 315 + * - shadow stack was not enabled for vforked child 316 + * - shadow stack base was anyways pointing to 0 317 + * This shouldn't be a big issue because we want parent to have availability of shadow 318 + * stack whenever VFORKed child releases resources via exit or exec but at the same 319 + * time we want VFORKed child to break away and establish new shadow stack if it desires 320 + * 321 + */ 322 + if (!enable_shstk) 323 + shstk_release(t); 324 + 325 + set_shstk_status(t, enable_shstk); 326 + return 0; 327 + } 328 + 329 + int arch_lock_shadow_stack_status(struct task_struct *task, 330 + unsigned long arg) 331 + { 332 + /* If shtstk not supported or not enabled on task, nothing to lock here */ 333 + if (!cpu_supports_shadow_stack() || 334 + !is_shstk_enabled(task) || arg != 0) 335 + return -EINVAL; 336 + 337 + set_shstk_lock(task); 338 + 339 + return 0; 290 340 }