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 indirect branch tracking prctls

This patch adds a RISC-V implementation of the following prctls:
PR_SET_INDIR_BR_LP_STATUS, PR_GET_INDIR_BR_LP_STATUS and
PR_LOCK_INDIR_BR_LP_STATUS.

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>
Tested-by: Valentin Haudiquet <valentin.haudiquet@canonical.com>
Link: https://patch.msgid.link/20251112-v5_user_cfi_series-v23-14-b55691eacf4f@rivosinc.com
[pjw@kernel.org: clean up patch description]
Signed-off-by: Paul Walmsley <pjw@kernel.org>

authored by

Deepak Gupta and committed by
Paul Walmsley
8a9e22d2 5ca243f6

+102
+14
arch/riscv/include/asm/usercfi.h
··· 16 16 struct cfi_state { 17 17 unsigned long ubcfi_en : 1; /* Enable for backward cfi. */ 18 18 unsigned long ubcfi_locked : 1; 19 + unsigned long ufcfi_en : 1; /* Enable for forward cfi. Note that ELP goes in sstatus */ 20 + unsigned long ufcfi_locked : 1; 19 21 unsigned long user_shdw_stk; /* Current user shadow stack pointer */ 20 22 unsigned long shdw_stk_base; /* Base address of shadow stack */ 21 23 unsigned long shdw_stk_size; /* size of shadow stack */ ··· 34 32 bool is_shstk_allocated(struct task_struct *task); 35 33 void set_shstk_lock(struct task_struct *task); 36 34 void set_shstk_status(struct task_struct *task, bool enable); 35 + bool is_indir_lp_enabled(struct task_struct *task); 36 + bool is_indir_lp_locked(struct task_struct *task); 37 + void set_indir_lp_status(struct task_struct *task, bool enable); 38 + void set_indir_lp_lock(struct task_struct *task); 37 39 38 40 #define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE) 39 41 ··· 62 56 #define set_shstk_lock(task) do {} while (0) 63 57 64 58 #define set_shstk_status(task, enable) do {} while (0) 59 + 60 + #define is_indir_lp_enabled(task) false 61 + 62 + #define is_indir_lp_locked(task) false 63 + 64 + #define set_indir_lp_status(task, enable) do {} while (0) 65 + 66 + #define set_indir_lp_lock(task) do {} while (0) 65 67 66 68 #endif /* CONFIG_RISCV_USER_CFI */ 67 69
+4
arch/riscv/kernel/entry.S
··· 174 174 * or vector in kernel space. 175 175 */ 176 176 li t0, SR_SUM | SR_FS_VS 177 + #ifdef CONFIG_64BIT 178 + li t1, SR_ELP 179 + or t0, t0, t1 180 + #endif 177 181 178 182 REG_L s0, TASK_TI_USER_SP(tp) 179 183 csrrc s1, CSR_STATUS, t0
+5
arch/riscv/kernel/process.c
··· 163 163 set_shstk_status(current, false); 164 164 set_shstk_base(current, 0, 0); 165 165 set_active_shstk(current, 0); 166 + /* 167 + * disable indirect branch tracking on exec. 168 + * libc will enable it later via prctl. 169 + */ 170 + set_indir_lp_status(current, false); 166 171 167 172 #ifdef CONFIG_64BIT 168 173 regs->status &= ~SR_UXL;
+79
arch/riscv/kernel/usercfi.c
··· 72 72 task->thread_info.user_cfi_state.ubcfi_locked = 1; 73 73 } 74 74 75 + bool is_indir_lp_enabled(struct task_struct *task) 76 + { 77 + return task->thread_info.user_cfi_state.ufcfi_en; 78 + } 79 + 80 + bool is_indir_lp_locked(struct task_struct *task) 81 + { 82 + return task->thread_info.user_cfi_state.ufcfi_locked; 83 + } 84 + 85 + void set_indir_lp_status(struct task_struct *task, bool enable) 86 + { 87 + if (!cpu_supports_indirect_br_lp_instr()) 88 + return; 89 + 90 + task->thread_info.user_cfi_state.ufcfi_en = enable ? 1 : 0; 91 + 92 + if (enable) 93 + task->thread.envcfg |= ENVCFG_LPE; 94 + else 95 + task->thread.envcfg &= ~ENVCFG_LPE; 96 + 97 + csr_write(CSR_ENVCFG, task->thread.envcfg); 98 + } 99 + 100 + void set_indir_lp_lock(struct task_struct *task) 101 + { 102 + task->thread_info.user_cfi_state.ufcfi_locked = 1; 103 + } 75 104 /* 76 105 * If size is 0, then to be compatible with regular stack we want it to be as big as 77 106 * regular stack. Else PAGE_ALIGN it and return back ··· 394 365 return -EINVAL; 395 366 396 367 set_shstk_lock(task); 368 + 369 + return 0; 370 + } 371 + 372 + int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status) 373 + { 374 + unsigned long fcfi_status = 0; 375 + 376 + if (!cpu_supports_indirect_br_lp_instr()) 377 + return -EINVAL; 378 + 379 + /* indirect branch tracking is enabled on the task or not */ 380 + fcfi_status |= (is_indir_lp_enabled(t) ? PR_INDIR_BR_LP_ENABLE : 0); 381 + 382 + return copy_to_user(status, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0; 383 + } 384 + 385 + int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status) 386 + { 387 + bool enable_indir_lp = false; 388 + 389 + if (!cpu_supports_indirect_br_lp_instr()) 390 + return -EINVAL; 391 + 392 + /* indirect branch tracking is locked and further can't be modified by user */ 393 + if (is_indir_lp_locked(t)) 394 + return -EINVAL; 395 + 396 + /* Reject unknown flags */ 397 + if (status & ~PR_INDIR_BR_LP_ENABLE) 398 + return -EINVAL; 399 + 400 + enable_indir_lp = (status & PR_INDIR_BR_LP_ENABLE); 401 + set_indir_lp_status(t, enable_indir_lp); 402 + 403 + return 0; 404 + } 405 + 406 + int arch_lock_indir_br_lp_status(struct task_struct *task, 407 + unsigned long arg) 408 + { 409 + /* 410 + * If indirect branch tracking is not supported or not enabled on task, 411 + * nothing to lock here 412 + */ 413 + if (!cpu_supports_indirect_br_lp_instr() || 414 + !is_indir_lp_enabled(task) || arg != 0) 415 + return -EINVAL; 416 + 417 + set_indir_lp_lock(task); 397 418 398 419 return 0; 399 420 }