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

prctl: add arch-agnostic prctl()s for indirect branch tracking

Three architectures (x86, aarch64, riscv) have support for indirect
branch tracking feature in a very similar fashion. On a very high
level, indirect branch tracking is a CPU feature where CPU tracks
branches which use a memory operand to transfer control. As part of
this tracking, during an indirect branch, the CPU expects a landing
pad instruction on the target PC, and if not found, the CPU raises
some fault (architecture-dependent).

x86 landing pad instr - 'ENDBRANCH'
arch64 landing pad instr - 'BTI'
riscv landing instr - 'lpad'

Given that three major architectures have support for indirect branch
tracking, this patch creates architecture-agnostic 'prctls' to allow
userspace to control this feature. They are:
- PR_GET_INDIR_BR_LP_STATUS: Get the current configured status for indirect
branch tracking.
- PR_SET_INDIR_BR_LP_STATUS: Set the configuration for indirect branch
tracking.
The following status options are allowed:
- PR_INDIR_BR_LP_ENABLE: Enables indirect branch tracking on user
thread.
- PR_INDIR_BR_LP_DISABLE: Disables indirect branch tracking on user
thread.
- PR_LOCK_INDIR_BR_LP_STATUS: Locks configured status for indirect branch
tracking for user thread.

Reviewed-by: Mark Brown <broonie@kernel.org>
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-13-b55691eacf4f@rivosinc.com
[pjw@kernel.org: cleaned up patch description, code comments]
Signed-off-by: Paul Walmsley <pjw@kernel.org>

authored by

Deepak Gupta and committed by
Paul Walmsley
5ca243f6 61a02002

+61
+4
include/linux/cpu.h
··· 229 229 #define smt_mitigations SMT_MITIGATIONS_OFF 230 230 #endif 231 231 232 + int arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status); 233 + int arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status); 234 + int arch_lock_indir_br_lp_status(struct task_struct *t, unsigned long status); 235 + 232 236 #endif /* _LINUX_CPU_H_ */
+27
include/uapi/linux/prctl.h
··· 386 386 # define PR_FUTEX_HASH_SET_SLOTS 1 387 387 # define PR_FUTEX_HASH_GET_SLOTS 2 388 388 389 + /* 390 + * Get the current indirect branch tracking configuration for the current 391 + * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. 392 + */ 393 + #define PR_GET_INDIR_BR_LP_STATUS 79 394 + 395 + /* 396 + * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will 397 + * enable cpu feature for user thread, to track all indirect branches and ensure 398 + * they land on arch defined landing pad instruction. 399 + * x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction. 400 + * arch64 - If enabled, an indirect branch must land on a BTI instruction. 401 + * riscv - If enabled, an indirect branch must land on an lpad instruction. 402 + * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect 403 + * branches will no more be tracked by cpu to land on arch defined landing pad 404 + * instruction. 405 + */ 406 + #define PR_SET_INDIR_BR_LP_STATUS 80 407 + # define PR_INDIR_BR_LP_ENABLE (1UL << 0) 408 + 409 + /* 410 + * Prevent further changes to the specified indirect branch tracking 411 + * configuration. All bits may be locked via this call, including 412 + * undefined bits. 413 + */ 414 + #define PR_LOCK_INDIR_BR_LP_STATUS 81 415 + 389 416 #endif /* _LINUX_PRCTL_H */
+30
kernel/sys.c
··· 2388 2388 return -EINVAL; 2389 2389 } 2390 2390 2391 + int __weak arch_get_indir_br_lp_status(struct task_struct *t, unsigned long __user *status) 2392 + { 2393 + return -EINVAL; 2394 + } 2395 + 2396 + int __weak arch_set_indir_br_lp_status(struct task_struct *t, unsigned long status) 2397 + { 2398 + return -EINVAL; 2399 + } 2400 + 2401 + int __weak arch_lock_indir_br_lp_status(struct task_struct *t, unsigned long status) 2402 + { 2403 + return -EINVAL; 2404 + } 2405 + 2391 2406 #define PR_IO_FLUSHER (PF_MEMALLOC_NOIO | PF_LOCAL_THROTTLE) 2392 2407 2393 2408 static int prctl_set_vma(unsigned long opt, unsigned long addr, ··· 2882 2867 break; 2883 2868 case PR_FUTEX_HASH: 2884 2869 error = futex_hash_prctl(arg2, arg3, arg4); 2870 + break; 2871 + case PR_GET_INDIR_BR_LP_STATUS: 2872 + if (arg3 || arg4 || arg5) 2873 + return -EINVAL; 2874 + error = arch_get_indir_br_lp_status(me, (unsigned long __user *)arg2); 2875 + break; 2876 + case PR_SET_INDIR_BR_LP_STATUS: 2877 + if (arg3 || arg4 || arg5) 2878 + return -EINVAL; 2879 + error = arch_set_indir_br_lp_status(me, arg2); 2880 + break; 2881 + case PR_LOCK_INDIR_BR_LP_STATUS: 2882 + if (arg3 || arg4 || arg5) 2883 + return -EINVAL; 2884 + error = arch_lock_indir_br_lp_status(me, arg2); 2885 2885 break; 2886 2886 default: 2887 2887 trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5);