Commit 08ee1559 authored by Paul Walmsley's avatar Paul Walmsley
Browse files

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: default avatarPaul Walmsley <pjw@kernel.org>
parent e5342fe2
Loading
Loading
Loading
Loading
+39 −24
Original line number Diff line number Diff line
@@ -76,34 +76,49 @@ the program.
4. prctl() enabling
--------------------

:c:macro:`PR_SET_INDIR_BR_LP_STATUS` / :c:macro:`PR_GET_INDIR_BR_LP_STATUS` /
:c:macro:`PR_LOCK_INDIR_BR_LP_STATUS` are three prctls added to manage indirect
branch tracking.  These prctls are architecture-agnostic and return -EINVAL if
the underlying functionality is not supported.
Per-task indirect branch tracking state can be monitored and
controlled via the :c:macro:`PR_GET_CFI` and :c:macro:`PR_SET_CFI`
``prctl()` arguments (respectively), by supplying
:c:macro:`PR_CFI_BRANCH_LANDING_PADS` as the second argument.  These
are architecture-agnostic, and will return -EINVAL if the underlying
functionality is not supported.

* prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg)
* prctl(:c:macro:`PR_SET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long arg)

If arg1 is :c:macro:`PR_INDIR_BR_LP_ENABLE` and if CPU supports
``zicfilp`` then the kernel will enable indirect branch tracking for the
task.  The dynamic loader can issue this :c:macro:`prctl` once it has
determined that all the objects loaded in the address space support
indirect branch tracking.  Additionally, if there is a `dlopen` to an
object which wasn't compiled with ``zicfilp``, the dynamic loader can
issue this prctl with arg1 set to 0 (i.e. :c:macro:`PR_INDIR_BR_LP_ENABLE`
cleared).

* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg)
arg is a bitmask.

Returns the current status of indirect branch tracking. If enabled
it'll return :c:macro:`PR_INDIR_BR_LP_ENABLE`

* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg)
If :c:macro:`PR_CFI_ENABLE` is set in arg, and the CPU supports
``zicfilp``, then the kernel will enable indirect branch tracking for
the task.  The dynamic loader can issue this ``prctl()`` once it has
determined that all the objects loaded in the address space support
indirect branch tracking.

Indirect branch tracking state can also be locked once enabled.  This
prevents the task from subsequently disabling it.  This is done by
setting the bit :c:macro:`PR_CFI_LOCK` in arg.  Either indirect branch
tracking must already be enabled for the task, or the bit
:c:macro:`PR_CFI_ENABLE` must also be set in arg.  This is intended
for environments that wish to run with a strict security posture that
do not wish to load objects without ``zicfilp`` support.

Indirect branch tracking can also be disabled for the task, assuming
that it has not previously been enabled and locked.  If there is a
``dlopen()`` to an object which wasn't compiled with ``zicfilp``, the
dynamic loader can issue this ``prctl()`` with arg set to
:c:macro:`PR_CFI_DISABLE`.  Disabling indirect branch tracking for the
task is not possible if it has previously been enabled and locked.


* prctl(:c:macro:`PR_GET_CFI`, :c:macro:`PR_CFI_BRANCH_LANDING_PADS`, unsigned long * arg)

Returns the current status of indirect branch tracking into a bitmask
stored into the memory location pointed to by arg.  The bitmask will
have the :c:macro:`PR_CFI_ENABLE` bit set if indirect branch tracking
is currently enabled for the task, and if it is locked, will
additionally have the :c:macro:`PR_CFI_LOCK` bit set.  If indirect
branch tracking is currently disabled for the task, the
:c:macro:`PR_CFI_DISABLE` bit will be set.

Locks the current status of indirect branch tracking on the task. User
space may want to run with a strict security posture and wouldn't want
loading of objects without ``zicfilp`` support in them, to disallow
disabling of indirect branch tracking. In this case, user space can
use this prctl to lock the current settings.

5. violations related to indirect branch tracking
--------------------------------------------------
+7 −8
Original line number Diff line number Diff line
@@ -465,16 +465,14 @@ int arch_prctl_get_branch_landing_pad_state(struct task_struct *t,
	if (!is_user_lpad_enabled())
		return -EINVAL;

	/* indirect branch tracking is enabled on the task or not */
	fcfi_status |= (is_indir_lp_enabled(t) ? PR_INDIR_BR_LP_ENABLE : 0);
	fcfi_status = (is_indir_lp_enabled(t) ? PR_CFI_ENABLE : PR_CFI_DISABLE);
	fcfi_status |= (is_indir_lp_locked(t) ? PR_CFI_LOCK : 0);

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

int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state)
{
	bool enable_indir_lp = false;

	if (!is_user_lpad_enabled())
		return -EINVAL;

@@ -482,12 +480,13 @@ int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long
	if (is_indir_lp_locked(t))
		return -EINVAL;

	/* Reject unknown flags */
	if (state & ~PR_INDIR_BR_LP_ENABLE)
	if (!(state & (PR_CFI_ENABLE | PR_CFI_DISABLE)))
		return -EINVAL;

	if (state & PR_CFI_ENABLE && state & PR_CFI_DISABLE)
		return -EINVAL;

	enable_indir_lp = (state & PR_INDIR_BR_LP_ENABLE);
	set_indir_lp_status(t, enable_indir_lp);
	set_indir_lp_status(t, !!(state & PR_CFI_ENABLE));

	return 0;
}
+15 −22
Original line number Diff line number Diff line
@@ -397,30 +397,23 @@ struct prctl_mm_map {
# define PR_RSEQ_SLICE_EXT_ENABLE		0x01

/*
 * Get the current indirect branch tracking configuration for the current
 * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS.
 * Get or set the control flow integrity (CFI) configuration for the
 * current thread.
 *
 * Some per-thread control flow integrity settings are not yet
 * controlled through this prctl(); see for example
 * PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS
 */
#define PR_GET_INDIR_BR_LP_STATUS      80

#define PR_GET_CFI	80
#define PR_SET_CFI	81
/*
 * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will
 * enable cpu feature for user thread, to track all indirect branches and ensure
 * they land on arch defined landing pad instruction.
 * x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction.
 * arch64 - If enabled, an indirect branch must land on a BTI instruction.
 * riscv - If enabled, an indirect branch must land on an lpad instruction.
 * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect
 * branches will no more be tracked by cpu to land on arch defined landing pad
 * instruction.
 */
#define PR_SET_INDIR_BR_LP_STATUS      81
# define PR_INDIR_BR_LP_ENABLE		   (1UL << 0)

/*
 * Prevent further changes to the specified indirect branch tracking
 * configuration.  All bits may be locked via this call, including
 * undefined bits.
 * Forward-edge CFI variants (excluding ARM64 BTI, which has its own
 * prctl()s).
 */
#define PR_LOCK_INDIR_BR_LP_STATUS      82
#define PR_CFI_BRANCH_LANDING_PADS	0
/* Return and control values for PR_{GET,SET}_CFI */
# define PR_CFI_ENABLE		_BITUL(0)
# define PR_CFI_DISABLE		_BITUL(1)
# define PR_CFI_LOCK		_BITUL(2)

#endif /* _LINUX_PRCTL_H */
+13 −10
Original line number Diff line number Diff line
@@ -2889,19 +2889,22 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
			return -EINVAL;
		error = rseq_slice_extension_prctl(arg2, arg3);
		break;
	case PR_GET_INDIR_BR_LP_STATUS:
		if (arg3 || arg4 || arg5)
	case PR_GET_CFI:
		if (arg2 != PR_CFI_BRANCH_LANDING_PADS)
			return -EINVAL;
		error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg2);
		break;
	case PR_SET_INDIR_BR_LP_STATUS:
		if (arg3 || arg4 || arg5)
		if (arg4 || arg5)
			return -EINVAL;
		error = arch_prctl_set_branch_landing_pad_state(me, arg2);
		error = arch_prctl_get_branch_landing_pad_state(me, (unsigned long __user *)arg3);
		break;
	case PR_LOCK_INDIR_BR_LP_STATUS:
		if (arg2 || arg3 || arg4 || arg5)
	case PR_SET_CFI:
		if (arg2 != PR_CFI_BRANCH_LANDING_PADS)
			return -EINVAL;
		if (arg4 || arg5)
			return -EINVAL;
		error = arch_prctl_set_branch_landing_pad_state(me, arg3);
		if (error)
			break;
		if (arg3 & PR_CFI_LOCK && !(arg3 & PR_CFI_DISABLE))
			error = arch_prctl_lock_branch_landing_pad_state(me);
		break;
	default:
+15 −21
Original line number Diff line number Diff line
@@ -397,30 +397,24 @@ struct prctl_mm_map {
# define PR_RSEQ_SLICE_EXT_ENABLE		0x01

/*
 * Get the current indirect branch tracking configuration for the current
 * thread, this will be the value configured via PR_SET_INDIR_BR_LP_STATUS.
 * Get or set the control flow integrity (CFI) configuration for the
 * current thread.
 *
 * Some per-thread control flow integrity settings are not yet
 * controlled through this prctl(); see for example
 * PR_{GET,SET,LOCK}_SHADOW_STACK_STATUS
 */
#define PR_GET_INDIR_BR_LP_STATUS      80

#define PR_GET_CFI	80
#define PR_SET_CFI	81
/*
 * Set the indirect branch tracking configuration. PR_INDIR_BR_LP_ENABLE will
 * enable cpu feature for user thread, to track all indirect branches and ensure
 * they land on arch defined landing pad instruction.
 * x86 - If enabled, an indirect branch must land on an ENDBRANCH instruction.
 * arch64 - If enabled, an indirect branch must land on a BTI instruction.
 * riscv - If enabled, an indirect branch must land on an lpad instruction.
 * PR_INDIR_BR_LP_DISABLE will disable feature for user thread and indirect
 * branches will no more be tracked by cpu to land on arch defined landing pad
 * instruction.
 * Forward-edge CFI variants (excluding ARM64 BTI, which has its own
 * prctl()s).
 */
#define PR_SET_INDIR_BR_LP_STATUS      81
# define PR_INDIR_BR_LP_ENABLE		   (1UL << 0)
#define PR_CFI_BRANCH_LANDING_PADS	0
/* Return and control values for PR_{GET,SET}_CFI */
# define PR_CFI_ENABLE		_BITUL(0)
# define PR_CFI_DISABLE		_BITUL(1)
# define PR_CFI_LOCK		_BITUL(2)

/*
 * Prevent further changes to the specified indirect branch tracking
 * configuration.  All bits may be locked via this call, including
 * undefined bits.
 */
#define PR_LOCK_INDIR_BR_LP_STATUS      82

#endif /* _LINUX_PRCTL_H */
Loading