Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 356e4bff authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

prctl: Add force disable speculation



For certain use cases it is desired to enforce mitigations so they cannot
be undone afterwards. That's important for loader stubs which want to
prevent a child from disabling the mitigation again. Will also be used for
seccomp(). The extra state preserving of the prctl state for SSB is a
preparatory step for EBPF dymanic speculation control.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent f9544b2b
Loading
Loading
Loading
Loading
+21 −13
Original line number Diff line number Diff line
@@ -25,19 +25,21 @@ PR_GET_SPECULATION_CTRL
-----------------------

PR_GET_SPECULATION_CTRL returns the state of the speculation misfeature
which is selected with arg2 of prctl(2). The return value uses bits 0-2 with
which is selected with arg2 of prctl(2). The return value uses bits 0-3 with
the following meaning:

==== ================ ===================================================
==== ===================== ===================================================
Bit  Define                Description
==== ================ ===================================================
==== ===================== ===================================================
0    PR_SPEC_PRCTL         Mitigation can be controlled per task by
                           PR_SET_SPECULATION_CTRL
1    PR_SPEC_ENABLE        The speculation feature is enabled, mitigation is
                           disabled
2    PR_SPEC_DISABLE       The speculation feature is disabled, mitigation is
                           enabled
==== ================ ===================================================
3    PR_SPEC_FORCE_DISABLE Same as PR_SPEC_DISABLE, but cannot be undone. A
                           subsequent prctl(..., PR_SPEC_ENABLE) will fail.
==== ===================== ===================================================

If all bits are 0 the CPU is not affected by the speculation misfeature.

@@ -47,9 +49,11 @@ misfeature will fail.

PR_SET_SPECULATION_CTRL
-----------------------

PR_SET_SPECULATION_CTRL allows to control the speculation misfeature, which
is selected by arg2 of :manpage:`prctl(2)` per task. arg3 is used to hand
in the control value, i.e. either PR_SPEC_ENABLE or PR_SPEC_DISABLE.
in the control value, i.e. either PR_SPEC_ENABLE or PR_SPEC_DISABLE or
PR_SPEC_FORCE_DISABLE.

Common error codes
------------------
@@ -70,10 +74,13 @@ Value Meaning
0       Success

ERANGE  arg3 is incorrect, i.e. it's neither PR_SPEC_ENABLE nor
        PR_SPEC_DISABLE
        PR_SPEC_DISABLE nor PR_SPEC_FORCE_DISABLE

ENXIO   Control of the selected speculation misfeature is not possible.
        See PR_GET_SPECULATION_CTRL.

EPERM   Speculation was disabled with PR_SPEC_FORCE_DISABLE and caller
        tried to enable it again.
======= =================================================================

Speculation misfeature controls
@@ -84,3 +91,4 @@ Speculation misfeature controls
   * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, 0, 0, 0);
   * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0);
   * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0);
   * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0);
+25 −10
Original line number Diff line number Diff line
@@ -533,21 +533,37 @@ static void ssb_select_mitigation()

static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl)
{
	bool rds = !!test_tsk_thread_flag(task, TIF_RDS);
	bool update;

	if (ssb_mode != SPEC_STORE_BYPASS_PRCTL)
		return -ENXIO;

	if (ctrl == PR_SPEC_ENABLE)
		clear_tsk_thread_flag(task, TIF_RDS);
	else
		set_tsk_thread_flag(task, TIF_RDS);
	switch (ctrl) {
	case PR_SPEC_ENABLE:
		/* If speculation is force disabled, enable is not allowed */
		if (task_spec_ssb_force_disable(task))
			return -EPERM;
		task_clear_spec_ssb_disable(task);
		update = test_and_clear_tsk_thread_flag(task, TIF_RDS);
		break;
	case PR_SPEC_DISABLE:
		task_set_spec_ssb_disable(task);
		update = !test_and_set_tsk_thread_flag(task, TIF_RDS);
		break;
	case PR_SPEC_FORCE_DISABLE:
		task_set_spec_ssb_disable(task);
		task_set_spec_ssb_force_disable(task);
		update = !test_and_set_tsk_thread_flag(task, TIF_RDS);
		break;
	default:
		return -ERANGE;
	}

	/*
	 * If being set on non-current task, delay setting the CPU
	 * mitigation until it is next scheduled.
	 */
	if (task == current && rds != !!test_tsk_thread_flag(task, TIF_RDS))
	if (task == current && update)
		speculative_store_bypass_update();

	return 0;
@@ -559,7 +575,9 @@ static int ssb_prctl_get(struct task_struct *task)
	case SPEC_STORE_BYPASS_DISABLE:
		return PR_SPEC_DISABLE;
	case SPEC_STORE_BYPASS_PRCTL:
		if (test_tsk_thread_flag(task, TIF_RDS))
		if (task_spec_ssb_force_disable(task))
			return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
		if (task_spec_ssb_disable(task))
			return PR_SPEC_PRCTL | PR_SPEC_DISABLE;
		return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
	default:
@@ -572,9 +590,6 @@ static int ssb_prctl_get(struct task_struct *task)
int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
			     unsigned long ctrl)
{
	if (ctrl != PR_SPEC_ENABLE && ctrl != PR_SPEC_DISABLE)
		return -ERANGE;

	switch (which) {
	case PR_SPEC_STORE_BYPASS:
		return ssb_prctl_set(task, ctrl);
+3 −0
Original line number Diff line number Diff line
@@ -344,6 +344,9 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p)
	case PR_SPEC_NOT_AFFECTED:
		seq_printf(m, "not vulnerable");
		break;
	case PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE:
		seq_printf(m, "thread force mitigated");
		break;
	case PR_SPEC_PRCTL | PR_SPEC_DISABLE:
		seq_printf(m, "thread mitigated");
		break;
+9 −1
Original line number Diff line number Diff line
@@ -1393,7 +1393,8 @@ static inline bool is_percpu_thread(void)
#define PFA_NO_NEW_PRIVS		0	/* May not gain new privileges. */
#define PFA_SPREAD_PAGE			1	/* Spread page cache over cpuset */
#define PFA_SPREAD_SLAB			2	/* Spread some slab caches over cpuset */

#define PFA_SPEC_SSB_DISABLE		3	/* Speculative Store Bypass disabled */
#define PFA_SPEC_SSB_FORCE_DISABLE	4	/* Speculative Store Bypass force disabled*/

#define TASK_PFA_TEST(name, func)					\
	static inline bool task_##func(struct task_struct *p)		\
@@ -1418,6 +1419,13 @@ TASK_PFA_TEST(SPREAD_SLAB, spread_slab)
TASK_PFA_SET(SPREAD_SLAB, spread_slab)
TASK_PFA_CLEAR(SPREAD_SLAB, spread_slab)

TASK_PFA_TEST(SPEC_SSB_DISABLE, spec_ssb_disable)
TASK_PFA_SET(SPEC_SSB_DISABLE, spec_ssb_disable)
TASK_PFA_CLEAR(SPEC_SSB_DISABLE, spec_ssb_disable)

TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)
TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)

static inline void
current_restore_flags(unsigned long orig_flags, unsigned long flags)
{
+1 −0
Original line number Diff line number Diff line
@@ -217,5 +217,6 @@ struct prctl_mm_map {
# define PR_SPEC_PRCTL			(1UL << 0)
# define PR_SPEC_ENABLE			(1UL << 1)
# define PR_SPEC_DISABLE		(1UL << 2)
# define PR_SPEC_FORCE_DISABLE		(1UL << 3)

#endif /* _LINUX_PRCTL_H */