Skip to content

Commit 08ee155

Browse files
author
Paul Walmsley
committed
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>
1 parent e5342fe commit 08ee155

6 files changed

Lines changed: 91 additions & 87 deletions

File tree

Documentation/arch/riscv/zicfilp.rst

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -76,34 +76,49 @@ the program.
7676
4. prctl() enabling
7777
--------------------
7878

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.
8385
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)
8587
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
89-
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-
95-
* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg)
88+
arg is a bitmask.
9689
97-
Returns the current status of indirect branch tracking. If enabled
98-
it'll return :c:macro:`PR_INDIR_BR_LP_ENABLE`
99-
100-
* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg)
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
93+
determined that all the objects loaded in the address space support
94+
indirect branch tracking.
95+
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.
103+
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.
110+
111+
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.
101121

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.
107122

108123
5. violations related to indirect branch tracking
109124
--------------------------------------------------

arch/riscv/kernel/usercfi.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -465,29 +465,28 @@ int arch_prctl_get_branch_landing_pad_state(struct task_struct *t,
465465
if (!is_user_lpad_enabled())
466466
return -EINVAL;
467467

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);
470470

471471
return copy_to_user(state, &fcfi_status, sizeof(fcfi_status)) ? -EFAULT : 0;
472472
}
473473

474474
int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state)
475475
{
476-
bool enable_indir_lp = false;
477-
478476
if (!is_user_lpad_enabled())
479477
return -EINVAL;
480478

481479
/* indirect branch tracking is locked and further can't be modified by user */
482480
if (is_indir_lp_locked(t))
483481
return -EINVAL;
484482

485-
/* Reject unknown flags */
486-
if (state & ~PR_INDIR_BR_LP_ENABLE)
483+
if (!(state & (PR_CFI_ENABLE | PR_CFI_DISABLE)))
484+
return -EINVAL;
485+
486+
if (state & PR_CFI_ENABLE && state & PR_CFI_DISABLE)
487487
return -EINVAL;
488488

489-
enable_indir_lp = (state & PR_INDIR_BR_LP_ENABLE);
490-
set_indir_lp_status(t, enable_indir_lp);
489+
set_indir_lp_status(t, !!(state & PR_CFI_ENABLE));
491490

492491
return 0;
493492
}

include/uapi/linux/prctl.h

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -397,30 +397,23 @@ struct prctl_mm_map {
397397
# define PR_RSEQ_SLICE_EXT_ENABLE 0x01
398398

399399
/*
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
402406
*/
403-
#define PR_GET_INDIR_BR_LP_STATUS 80
404-
407+
#define PR_GET_CFI 80
408+
#define PR_SET_CFI 81
405409
/*
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.
415-
*/
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.
410+
* Forward-edge CFI variants (excluding ARM64 BTI, which has its own
411+
* prctl()s).
423412
*/
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)
425418

426419
#endif /* _LINUX_PRCTL_H */

kernel/sys.c

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2889,20 +2889,23 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
28892889
return -EINVAL;
28902890
error = rseq_slice_extension_prctl(arg2, arg3);
28912891
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)
28942894
return -EINVAL;
2895-
error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg2);
2896-
break;
2897-
case PR_SET_INDIR_BR_LP_STATUS:
2898-
if (arg3 || arg4 || arg5)
2895+
if (arg4 || arg5)
28992896
return -EINVAL;
2900-
error = arch_prctl_set_branch_landing_pad_state(me, arg2);
2897+
error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg3);
29012898
break;
2902-
case PR_LOCK_INDIR_BR_LP_STATUS:
2903-
if (arg2 || arg3 || arg4 || arg5)
2899+
case PR_SET_CFI:
2900+
if (arg2 != PR_CFI_BRANCH_LANDING_PADS)
29042901
return -EINVAL;
2905-
error = arch_prctl_lock_branch_landing_pad_state(me);
2902+
if (arg4 || arg5)
2903+
return -EINVAL;
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);
29062909
break;
29072910
default:
29082911
trace_task_prctl_unknown(option, arg2, arg3, arg4, arg5);

tools/perf/trace/beauty/include/uapi/linux/prctl.h

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -397,30 +397,24 @@ struct prctl_mm_map {
397397
# define PR_RSEQ_SLICE_EXT_ENABLE 0x01
398398

399399
/*
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
402406
*/
403-
#define PR_GET_INDIR_BR_LP_STATUS 80
404-
407+
#define PR_GET_CFI 80
408+
#define PR_SET_CFI 81
405409
/*
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).
415412
*/
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)
418418

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
425419

426420
#endif /* _LINUX_PRCTL_H */

tools/testing/selftests/riscv/cfi/cfitests.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,11 @@ int main(int argc, char *argv[])
146146
* pads for user mode except lighting up a bit in senvcfg via a prctl.
147147
* Enable landing pad support throughout the execution of the test binary.
148148
*/
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);
150150
if (ret)
151151
ksft_exit_fail_msg("Get landing pad status failed with %d\n", ret);
152152

153-
if (!(lpad_status & PR_INDIR_BR_LP_ENABLE))
153+
if (!(lpad_status & PR_CFI_ENABLE))
154154
ksft_exit_fail_msg("Landing pad is not enabled, should be enabled via glibc\n");
155155

156156
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &ss_status, 0, 0, 0);

0 commit comments

Comments
 (0)