Commit e774d5f1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'riscv-for-linus-v7.0-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux

Pull RISC-V updates from Paul Walmsley:
 "Before v7.0 is released, fix a few issues with the CFI patchset,
  merged earlier in v7.0-rc, that primarily affect interfaces to
  non-kernel code:

   - Improve the prctl() interface for per-task indirect branch landing
     pad control to expand abbreviations and to resemble the speculation
     control prctl() interface

   - Expand the "LP" and "SS" abbreviations in the ptrace uapi header
     file to "branch landing pad" and "shadow stack", to improve
     readability

   - Fix a typo in a CFI-related macro name in the ptrace uapi header
     file

   - Ensure that the indirect branch tracking state and shadow stack
     state are unlocked immediately after an exec() on the new task so
     that libc subsequently can control it

   - While working in this area, clean up the kernel-internal,
     cross-architecture prctl() function names by expanding the
     abbreviations mentioned above"

* tag 'riscv-for-linus-v7.0-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux:
  prctl: cfi: change the branch landing pad prctl()s to be more descriptive
  riscv: ptrace: cfi: expand "SS" references to "shadow stack" in uapi headers
  prctl: rename branch landing pad implementation functions to be more explicit
  riscv: ptrace: expand "LP" references to "branch landing pads" in uapi headers
  riscv: cfi: clear CFI lock status in start_thread()
  riscv: ptrace: cfi: fix "PRACE" typo in uapi header
parents c43adb36 08ee1559
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
--------------------------------------------------
+4 −4
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ void set_active_shstk(struct task_struct *task, unsigned long shstk_addr);
bool is_shstk_enabled(struct task_struct *task);
bool is_shstk_locked(struct task_struct *task);
bool is_shstk_allocated(struct task_struct *task);
void set_shstk_lock(struct task_struct *task);
void set_shstk_lock(struct task_struct *task, bool lock);
void set_shstk_status(struct task_struct *task, bool enable);
unsigned long get_active_shstk(struct task_struct *task);
int restore_user_shstk(struct task_struct *tsk, unsigned long shstk_ptr);
@@ -47,7 +47,7 @@ int save_user_shstk(struct task_struct *tsk, unsigned long *saved_shstk_ptr);
bool is_indir_lp_enabled(struct task_struct *task);
bool is_indir_lp_locked(struct task_struct *task);
void set_indir_lp_status(struct task_struct *task, bool enable);
void set_indir_lp_lock(struct task_struct *task);
void set_indir_lp_lock(struct task_struct *task, bool lock);

#define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK (PR_SHADOW_STACK_ENABLE)

@@ -69,7 +69,7 @@ void set_indir_lp_lock(struct task_struct *task);

#define is_shstk_allocated(task) false

#define set_shstk_lock(task) do {} while (0)
#define set_shstk_lock(task, lock) do {} while (0)

#define set_shstk_status(task, enable) do {} while (0)

@@ -79,7 +79,7 @@ void set_indir_lp_lock(struct task_struct *task);

#define set_indir_lp_status(task, enable) do {} while (0)

#define set_indir_lp_lock(task) do {} while (0)
#define set_indir_lp_lock(task, lock) do {} while (0)

#define restore_user_shstk(tsk, shstk_ptr) -EINVAL

+22 −20
Original line number Diff line number Diff line
@@ -132,26 +132,28 @@ struct __sc_riscv_cfi_state {
	unsigned long ss_ptr;   /* shadow stack pointer */
};

#define PTRACE_CFI_LP_EN_BIT	0
#define PTRACE_CFI_LP_LOCK_BIT	1
#define PTRACE_CFI_ELP_BIT	2
#define PTRACE_CFI_SS_EN_BIT	3
#define PTRACE_CFI_SS_LOCK_BIT	4
#define PTRACE_CFI_SS_PTR_BIT	5

#define PTRACE_CFI_LP_EN_STATE		_BITUL(PTRACE_CFI_LP_EN_BIT)
#define PTRACE_CFI_LP_LOCK_STATE	_BITUL(PTRACE_CFI_LP_LOCK_BIT)
#define PTRACE_CFI_ELP_STATE		_BITUL(PTRACE_CFI_ELP_BIT)
#define PTRACE_CFI_SS_EN_STATE		_BITUL(PTRACE_CFI_SS_EN_BIT)
#define PTRACE_CFI_SS_LOCK_STATE	_BITUL(PTRACE_CFI_SS_LOCK_BIT)
#define PTRACE_CFI_SS_PTR_STATE		_BITUL(PTRACE_CFI_SS_PTR_BIT)

#define PRACE_CFI_STATE_INVALID_MASK	~(PTRACE_CFI_LP_EN_STATE | \
					  PTRACE_CFI_LP_LOCK_STATE | \
					  PTRACE_CFI_ELP_STATE | \
					  PTRACE_CFI_SS_EN_STATE | \
					  PTRACE_CFI_SS_LOCK_STATE | \
					  PTRACE_CFI_SS_PTR_STATE)
#define PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT		0
#define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT		1
#define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT	2
#define PTRACE_CFI_SHADOW_STACK_EN_BIT			3
#define PTRACE_CFI_SHADOW_STACK_LOCK_BIT		4
#define PTRACE_CFI_SHADOW_STACK_PTR_BIT			5

#define PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE		_BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_EN_BIT)
#define PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE	\
	_BITUL(PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_BIT)
#define PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE	\
	_BITUL(PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_BIT)
#define PTRACE_CFI_SHADOW_STACK_EN_STATE		_BITUL(PTRACE_CFI_SHADOW_STACK_EN_BIT)
#define PTRACE_CFI_SHADOW_STACK_LOCK_STATE		_BITUL(PTRACE_CFI_SHADOW_STACK_LOCK_BIT)
#define PTRACE_CFI_SHADOW_STACK_PTR_STATE		_BITUL(PTRACE_CFI_SHADOW_STACK_PTR_BIT)

#define PTRACE_CFI_STATE_INVALID_MASK	~(PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | \
					  PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE | \
					  PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE | \
					  PTRACE_CFI_SHADOW_STACK_EN_STATE | \
					  PTRACE_CFI_SHADOW_STACK_LOCK_STATE | \
					  PTRACE_CFI_SHADOW_STACK_PTR_STATE)

struct __cfi_status {
	__u64 cfi_state;
+2 −0
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc,
	 * clear shadow stack state on exec.
	 * libc will set it later via prctl.
	 */
	set_shstk_lock(current, false);
	set_shstk_status(current, false);
	set_shstk_base(current, 0, 0);
	set_active_shstk(current, 0);
@@ -167,6 +168,7 @@ void start_thread(struct pt_regs *regs, unsigned long pc,
	 * disable indirect branch tracking on exec.
	 * libc will enable it later via prctl.
	 */
	set_indir_lp_lock(current, false);
	set_indir_lp_status(current, false);

#ifdef CONFIG_64BIT
+11 −11
Original line number Diff line number Diff line
@@ -303,18 +303,18 @@ static int riscv_cfi_get(struct task_struct *target,
	regs = task_pt_regs(target);

	if (is_indir_lp_enabled(target)) {
		user_cfi.cfi_status.cfi_state |= PTRACE_CFI_LP_EN_STATE;
		user_cfi.cfi_status.cfi_state |= PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE;
		user_cfi.cfi_status.cfi_state |= is_indir_lp_locked(target) ?
						 PTRACE_CFI_LP_LOCK_STATE : 0;
						 PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE : 0;
		user_cfi.cfi_status.cfi_state |= (regs->status & SR_ELP) ?
						PTRACE_CFI_ELP_STATE : 0;
						PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE : 0;
	}

	if (is_shstk_enabled(target)) {
		user_cfi.cfi_status.cfi_state |= (PTRACE_CFI_SS_EN_STATE |
						  PTRACE_CFI_SS_PTR_STATE);
		user_cfi.cfi_status.cfi_state |= (PTRACE_CFI_SHADOW_STACK_EN_STATE |
						  PTRACE_CFI_SHADOW_STACK_PTR_STATE);
		user_cfi.cfi_status.cfi_state |= is_shstk_locked(target) ?
						 PTRACE_CFI_SS_LOCK_STATE : 0;
						 PTRACE_CFI_SHADOW_STACK_LOCK_STATE : 0;
		user_cfi.shstk_ptr = get_active_shstk(target);
	}

@@ -349,15 +349,15 @@ static int riscv_cfi_set(struct task_struct *target,
	 * rsvd field should be set to zero so that if those fields are needed in future
	 */
	if ((user_cfi.cfi_status.cfi_state &
	     (PTRACE_CFI_LP_EN_STATE | PTRACE_CFI_LP_LOCK_STATE |
	      PTRACE_CFI_SS_EN_STATE | PTRACE_CFI_SS_LOCK_STATE)) ||
	     (user_cfi.cfi_status.cfi_state & PRACE_CFI_STATE_INVALID_MASK))
	     (PTRACE_CFI_BRANCH_LANDING_PAD_EN_STATE | PTRACE_CFI_BRANCH_LANDING_PAD_LOCK_STATE |
	      PTRACE_CFI_SHADOW_STACK_EN_STATE | PTRACE_CFI_SHADOW_STACK_LOCK_STATE)) ||
	     (user_cfi.cfi_status.cfi_state & PTRACE_CFI_STATE_INVALID_MASK))
		return -EINVAL;

	/* If lpad is enabled on target and ptrace requests to set / clear elp, do that */
	if (is_indir_lp_enabled(target)) {
		if (user_cfi.cfi_status.cfi_state &
		    PTRACE_CFI_ELP_STATE) /* set elp state */
		    PTRACE_CFI_BRANCH_EXPECTED_LANDING_PAD_STATE) /* set elp state */
			regs->status |= SR_ELP;
		else
			regs->status &= ~SR_ELP; /* clear elp state */
@@ -365,7 +365,7 @@ static int riscv_cfi_set(struct task_struct *target,

	/* If shadow stack enabled on target, set new shadow stack pointer */
	if (is_shstk_enabled(target) &&
	    (user_cfi.cfi_status.cfi_state & PTRACE_CFI_SS_PTR_STATE))
	    (user_cfi.cfi_status.cfi_state & PTRACE_CFI_SHADOW_STACK_PTR_STATE))
		set_active_shstk(target, user_cfi.shstk_ptr);

	return 0;
Loading