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

prctl: cfi: change the branch landing pad prctl()s to be more descriptive

Per Linus' comments requesting the replacement of "INDIR_BR_LP" in the
indirect branch tracking prctl()s with something more readable, and
suggesting the use of the speculation control prctl()s as an exemplar,
reimplement the prctl()s and related constants that control per-task
forward-edge control flow integrity.

This primarily involves two changes. First, the prctls are
restructured to resemble the style of the speculative execution
workaround control prctls PR_{GET,SET}_SPECULATION_CTRL, to make them
easier to extend in the future. Second, the "indir_br_lp" abbrevation
is expanded to "branch_landing_pads" to be less telegraphic. The
kselftest and documentation is adjusted accordingly.

Link: https://lore.kernel.org/linux-riscv/CAHk-=whhSLGZAx3N5jJpb4GLFDqH_QvS07D+6BnkPWmCEzTAgw@mail.gmail.com/
Cc: Deepak Gupta <debug@rivosinc.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mark Brown <broonie@kernel.org>
Signed-off-by: Paul Walmsley <pjw@kernel.org>

+88 -84
+36 -21
Documentation/arch/riscv/zicfilp.rst
··· 76 76 4. prctl() enabling 77 77 -------------------- 78 78 79 - :c:macro:`PR_SET_INDIR_BR_LP_STATUS` / :c:macro:`PR_GET_INDIR_BR_LP_STATUS` / 80 - :c:macro:`PR_LOCK_INDIR_BR_LP_STATUS` are three prctls added to manage indirect 81 - branch tracking. These prctls are architecture-agnostic and return -EINVAL if 82 - the underlying functionality is not supported. 79 + Per-task indirect branch tracking state can be monitored and 80 + controlled via the :c:macro:`PR_GET_CFI` and :c:macro:`PR_SET_CFI` 81 + ``prctl()` arguments (respectively), by supplying 82 + :c:macro:`PR_CFI_BRANCH_LANDING_PADS` as the second argument. These 83 + are architecture-agnostic, and will return -EINVAL if the underlying 84 + functionality is not supported. 83 85 84 - * prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg) 86 + * prctl(:c:macro:`PR_SET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long arg) 85 87 86 - If arg1 is :c:macro:`PR_INDIR_BR_LP_ENABLE` and if CPU supports 87 - ``zicfilp`` then the kernel will enable indirect branch tracking for the 88 - task. The dynamic loader can issue this :c:macro:`prctl` once it has 88 + arg is a bitmask. 89 + 90 + If :c:macro:`PR_CFI_ENABLE` is set in arg, and the CPU supports 91 + ``zicfilp``, then the kernel will enable indirect branch tracking for 92 + the task. The dynamic loader can issue this ``prctl()`` once it has 89 93 determined that all the objects loaded in the address space support 90 - indirect branch tracking. Additionally, if there is a `dlopen` to an 91 - object which wasn't compiled with ``zicfilp``, the dynamic loader can 92 - issue this prctl with arg1 set to 0 (i.e. :c:macro:`PR_INDIR_BR_LP_ENABLE` 93 - cleared). 94 + indirect branch tracking. 94 95 95 - * prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg) 96 + Indirect branch tracking state can also be locked once enabled. This 97 + prevents the task from subsequently disabling it. This is done by 98 + setting the bit :c:macro:`PR_CFI_LOCK` in arg. Either indirect branch 99 + tracking must already be enabled for the task, or the bit 100 + :c:macro:`PR_CFI_ENABLE` must also be set in arg. This is intended 101 + for environments that wish to run with a strict security posture that 102 + do not wish to load objects without ``zicfilp`` support. 96 103 97 - Returns the current status of indirect branch tracking. If enabled 98 - it'll return :c:macro:`PR_INDIR_BR_LP_ENABLE` 104 + Indirect branch tracking can also be disabled for the task, assuming 105 + that it has not previously been enabled and locked. If there is a 106 + ``dlopen()`` to an object which wasn't compiled with ``zicfilp``, the 107 + dynamic loader can issue this ``prctl()`` with arg set to 108 + :c:macro:`PR_CFI_DISABLE`. Disabling indirect branch tracking for the 109 + task is not possible if it has previously been enabled and locked. 99 110 100 - * prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg) 101 111 102 - Locks the current status of indirect branch tracking on the task. User 103 - space may want to run with a strict security posture and wouldn't want 104 - loading of objects without ``zicfilp`` support in them, to disallow 105 - disabling of indirect branch tracking. In this case, user space can 106 - use this prctl to lock the current settings. 112 + * prctl(:c:macro:`PR_GET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long * arg) 113 + 114 + Returns the current status of indirect branch tracking into a bitmask 115 + stored into the memory location pointed to by arg. The bitmask will 116 + have the :c:macro:`PR_CFI_ENABLE` bit set if indirect branch tracking 117 + is currently enabled for the task, and if it is locked, will 118 + additionally have the :c:macro:`PR_CFI_LOCK` bit set. If indirect 119 + branch tracking is currently disabled for the task, the 120 + :c:macro:`PR_CFI_DISABLE` bit will be set. 121 + 107 122 108 123 5. violations related to indirect branch tracking 109 124 --------------------------------------------------
+7 -8
arch/riscv/kernel/usercfi.c
··· 465 465 if (!is_user_lpad_enabled()) 466 466 return -EINVAL; 467 467 468 - /* indirect branch tracking is enabled on the task or not */ 469 - fcfi_status |= (is_indir_lp_enabled(t) ? PR_INDIR_BR_LP_ENABLE : 0); 468 + fcfi_status = (is_indir_lp_enabled(t) ? PR_CFI_ENABLE : PR_CFI_DISABLE); 469 + fcfi_status |= (is_indir_lp_locked(t) ? PR_CFI_LOCK : 0); 470 470 471 471 return copy_to_user(state, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0; 472 472 } 473 473 474 474 int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state) 475 475 { 476 - bool enable_indir_lp = false; 477 - 478 476 if (!is_user_lpad_enabled()) 479 477 return -EINVAL; 480 478 ··· 480 482 if (is_indir_lp_locked(t)) 481 483 return -EINVAL; 482 484 483 - /* Reject unknown flags */ 484 - if (state & ~PR_INDIR_BR_LP_ENABLE) 485 + if (!(state & (PR_CFI_ENABLE | PR_CFI_DISABLE))) 485 486 return -EINVAL; 486 487 487 - enable_indir_lp = (state & PR_INDIR_BR_LP_ENABLE); 488 - set_indir_lp_status(t, enable_indir_lp); 488 + if (state & PR_CFI_ENABLE && state & PR_CFI_DISABLE) 489 + return -EINVAL; 490 + 491 + set_indir_lp_status(t, !!(state & PR_CFI_ENABLE)); 489 492 490 493 return 0; 491 494 }
+15 -22
include/uapi/linux/prctl.h
··· 397 397 # define PR_RSEQ_SLICE_EXT_ENABLE 0x01 398 398 399 399 /* 400 - * Get the current indirect branch tracking configuration for the current 401 - * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. 400 + * Get or set the control flow integrity (CFI) configuration for the 401 + * current thread. 402 + * 403 + * Some per-thread control flow integrity settings are not yet 404 + * controlled through this prctl(); see for example 405 + * PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS 402 406 */ 403 - #define PR_GET_INDIR_BR_LP_STATUS 80 404 - 407 + #define PR_GET_CFI 80 408 + #define PR_SET_CFI 81 405 409 /* 406 - * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will 407 - * enable cpu feature for user thread, to track all indirect branches and ensure 408 - * they land on arch defined landing pad instruction. 409 - * x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction. 410 - * arch64 - If enabled, an indirect branch must land on a BTI instruction. 411 - * riscv - If enabled, an indirect branch must land on an lpad instruction. 412 - * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect 413 - * branches will no more be tracked by cpu to land on arch defined landing pad 414 - * instruction. 410 + * Forward-edge CFI variants (excluding ARM64 BTI, which has its own 411 + * prctl()s). 415 412 */ 416 - #define PR_SET_INDIR_BR_LP_STATUS 81 417 - # define PR_INDIR_BR_LP_ENABLE (1UL << 0) 418 - 419 - /* 420 - * Prevent further changes to the specified indirect branch tracking 421 - * configuration. All bits may be locked via this call, including 422 - * undefined bits. 423 - */ 424 - #define PR_LOCK_INDIR_BR_LP_STATUS 82 413 + #define PR_CFI_BRANCH_LANDING_PADS 0 414 + /* Return and control values for PR_{GET,SET}_CFI */ 415 + # define PR_CFI_ENABLE _BITUL(0) 416 + # define PR_CFI_DISABLE _BITUL(1) 417 + # define PR_CFI_LOCK _BITUL(2) 425 418 426 419 #endif /* _LINUX_PRCTL_H */
+13 -10
kernel/sys.c
··· 2889 2889 return -EINVAL; 2890 2890 error = rseq_slice_extension_prctl(arg2, arg3); 2891 2891 break; 2892 - case PR_GET_INDIR_BR_LP_STATUS: 2893 - if (arg3 || arg4 || arg5) 2892 + case PR_GET_CFI: 2893 + if (arg2 != PR_CFI_BRANCH_LANDING_PADS) 2894 2894 return -EINVAL; 2895 - error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg2); 2895 + if (arg4 || arg5) 2896 + return -EINVAL; 2897 + error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg3); 2896 2898 break; 2897 - case PR_SET_INDIR_BR_LP_STATUS: 2898 - if (arg3 || arg4 || arg5) 2899 + case PR_SET_CFI: 2900 + if (arg2 != PR_CFI_BRANCH_LANDING_PADS) 2899 2901 return -EINVAL; 2900 - error = arch_prctl_set_branch_landing_pad_state(me, arg2); 2901 - break; 2902 - case PR_LOCK_INDIR_BR_LP_STATUS: 2903 - if (arg2 || arg3 || arg4 || arg5) 2902 + if (arg4 || arg5) 2904 2903 return -EINVAL; 2905 - error = arch_prctl_lock_branch_landing_pad_state(me); 2904 + error = arch_prctl_set_branch_landing_pad_state(me, arg3); 2905 + if (error) 2906 + break; 2907 + if (arg3 & PR_CFI_LOCK && !(arg3 & PR_CFI_DISABLE)) 2908 + error = arch_prctl_lock_branch_landing_pad_state(me); 2906 2909 break; 2907 2910 default: 2908 2911 trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5);
+15 -21
tools/perf/trace/beauty/include/uapi/linux/prctl.h
··· 397 397 # define PR_RSEQ_SLICE_EXT_ENABLE 0x01 398 398 399 399 /* 400 - * Get the current indirect branch tracking configuration for the current 401 - * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS. 400 + * Get or set the control flow integrity (CFI) configuration for the 401 + * current thread. 402 + * 403 + * Some per-thread control flow integrity settings are not yet 404 + * controlled through this prctl(); see for example 405 + * PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS 402 406 */ 403 - #define PR_GET_INDIR_BR_LP_STATUS 80 404 - 407 + #define PR_GET_CFI 80 408 + #define PR_SET_CFI 81 405 409 /* 406 - * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will 407 - * enable cpu feature for user thread, to track all indirect branches and ensure 408 - * they land on arch defined landing pad instruction. 409 - * x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction. 410 - * arch64 - If enabled, an indirect branch must land on a BTI instruction. 411 - * riscv - If enabled, an indirect branch must land on an lpad instruction. 412 - * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect 413 - * branches will no more be tracked by cpu to land on arch defined landing pad 414 - * instruction. 410 + * Forward-edge CFI variants (excluding ARM64 BTI, which has its own 411 + * prctl()s). 415 412 */ 416 - #define PR_SET_INDIR_BR_LP_STATUS 81 417 - # define PR_INDIR_BR_LP_ENABLE (1UL << 0) 413 + #define PR_CFI_BRANCH_LANDING_PADS 0 414 + /* Return and control values for PR_{GET,SET}_CFI */ 415 + # define PR_CFI_ENABLE _BITUL(0) 416 + # define PR_CFI_DISABLE _BITUL(1) 417 + # define PR_CFI_LOCK _BITUL(2) 418 418 419 - /* 420 - * Prevent further changes to the specified indirect branch tracking 421 - * configuration. All bits may be locked via this call, including 422 - * undefined bits. 423 - */ 424 - #define PR_LOCK_INDIR_BR_LP_STATUS 82 425 419 426 420 #endif /* _LINUX_PRCTL_H */
+2 -2
tools/testing/selftests/riscv/cfi/cfitests.c
··· 146 146 * pads for user mode except lighting up a bit in senvcfg via a prctl. 147 147 * Enable landing pad support throughout the execution of the test binary. 148 148 */ 149 - ret = my_syscall5(__NR_prctl, PR_GET_INDIR_BR_LP_STATUS, &lpad_status, 0, 0, 0); 149 + ret = my_syscall5(__NR_prctl, PR_GET_CFI, PR_CFI_BRANCH_LANDING_PADS, &lpad_status, 0, 0); 150 150 if (ret) 151 151 ksft_exit_fail_msg("Get landing pad status failed with %d\n", ret); 152 152 153 - if (!(lpad_status & PR_INDIR_BR_LP_ENABLE)) 153 + if (!(lpad_status & PR_CFI_ENABLE)) 154 154 ksft_exit_fail_msg("Landing pad is not enabled, should be enabled via glibc\n"); 155 155 156 156 ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &ss_status, 0, 0, 0);