prctl: Add force disable speculation

commit 356e4bfff2 upstream

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: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Thomas Gleixner 2018-05-03 22:09:15 +02:00 коммит произвёл Greg Kroah-Hartman
Родитель c024722ffe
Коммит 20d036a2e2
5 изменённых файлов: 59 добавлений и 24 удалений

Просмотреть файл

@ -25,19 +25,21 @@ PR_GET_SPECULATION_CTRL
----------------------- -----------------------
PR_GET_SPECULATION_CTRL returns the state of the speculation misfeature 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: the following meaning:
==== ================ =================================================== ==== ===================== ===================================================
Bit Define Description Bit Define Description
==== ================ =================================================== ==== ===================== ===================================================
0 PR_SPEC_PRCTL Mitigation can be controlled per task by 0 PR_SPEC_PRCTL Mitigation can be controlled per task by
PR_SET_SPECULATION_CTRL PR_SET_SPECULATION_CTRL
1 PR_SPEC_ENABLE The speculation feature is enabled, mitigation is 1 PR_SPEC_ENABLE The speculation feature is enabled, mitigation is
disabled disabled
2 PR_SPEC_DISABLE The speculation feature is disabled, mitigation is 2 PR_SPEC_DISABLE The speculation feature is disabled, mitigation is
enabled 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. 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
----------------------- -----------------------
PR_SET_SPECULATION_CTRL allows to control the speculation misfeature, which 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 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 Common error codes
------------------ ------------------
@ -70,10 +74,13 @@ Value Meaning
0 Success 0 Success
ERANGE arg3 is incorrect, i.e. it's neither PR_SPEC_ENABLE nor 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. ENXIO Control of the selected speculation misfeature is not possible.
See PR_GET_SPECULATION_CTRL. See PR_GET_SPECULATION_CTRL.
EPERM Speculation was disabled with PR_SPEC_FORCE_DISABLE and caller
tried to enable it again.
======= ================================================================= ======= =================================================================
Speculation misfeature controls Speculation misfeature controls
@ -84,3 +91,4 @@ Speculation misfeature controls
* prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, 0, 0, 0); * 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_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_DISABLE, 0, 0);
* prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0);

Просмотреть файл

@ -533,21 +533,37 @@ static void ssb_select_mitigation()
static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) 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) if (ssb_mode != SPEC_STORE_BYPASS_PRCTL)
return -ENXIO; return -ENXIO;
if (ctrl == PR_SPEC_ENABLE) switch (ctrl) {
clear_tsk_thread_flag(task, TIF_RDS); case PR_SPEC_ENABLE:
else /* If speculation is force disabled, enable is not allowed */
set_tsk_thread_flag(task, TIF_RDS); 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 * If being set on non-current task, delay setting the CPU
* mitigation until it is next scheduled. * 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(); speculative_store_bypass_update();
return 0; return 0;
@ -559,7 +575,9 @@ static int ssb_prctl_get(struct task_struct *task)
case SPEC_STORE_BYPASS_DISABLE: case SPEC_STORE_BYPASS_DISABLE:
return PR_SPEC_DISABLE; return PR_SPEC_DISABLE;
case SPEC_STORE_BYPASS_PRCTL: 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_DISABLE;
return PR_SPEC_PRCTL | PR_SPEC_ENABLE; return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
default: 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, int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
unsigned long ctrl) unsigned long ctrl)
{ {
if (ctrl != PR_SPEC_ENABLE && ctrl != PR_SPEC_DISABLE)
return -ERANGE;
switch (which) { switch (which) {
case PR_SPEC_STORE_BYPASS: case PR_SPEC_STORE_BYPASS:
return ssb_prctl_set(task, ctrl); return ssb_prctl_set(task, ctrl);

Просмотреть файл

@ -356,6 +356,9 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p)
case PR_SPEC_NOT_AFFECTED: case PR_SPEC_NOT_AFFECTED:
seq_printf(m, "not vulnerable"); seq_printf(m, "not vulnerable");
break; break;
case PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE:
seq_printf(m, "thread force mitigated");
break;
case PR_SPEC_PRCTL | PR_SPEC_DISABLE: case PR_SPEC_PRCTL | PR_SPEC_DISABLE:
seq_printf(m, "thread mitigated"); seq_printf(m, "thread mitigated");
break; break;

Просмотреть файл

@ -1363,7 +1363,8 @@ static inline bool is_percpu_thread(void)
#define PFA_NO_NEW_PRIVS 0 /* May not gain new privileges. */ #define PFA_NO_NEW_PRIVS 0 /* May not gain new privileges. */
#define PFA_SPREAD_PAGE 1 /* Spread page cache over cpuset */ #define PFA_SPREAD_PAGE 1 /* Spread page cache over cpuset */
#define PFA_SPREAD_SLAB 2 /* Spread some slab caches 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) \ #define TASK_PFA_TEST(name, func) \
static inline bool task_##func(struct task_struct *p) \ static inline bool task_##func(struct task_struct *p) \
@ -1388,6 +1389,13 @@ TASK_PFA_TEST(SPREAD_SLAB, spread_slab)
TASK_PFA_SET(SPREAD_SLAB, spread_slab) TASK_PFA_SET(SPREAD_SLAB, spread_slab)
TASK_PFA_CLEAR(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 static inline void
current_restore_flags(unsigned long orig_flags, unsigned long flags) current_restore_flags(unsigned long orig_flags, unsigned long flags)
{ {

Просмотреть файл

@ -208,5 +208,6 @@ struct prctl_mm_map {
# define PR_SPEC_PRCTL (1UL << 0) # define PR_SPEC_PRCTL (1UL << 0)
# define PR_SPEC_ENABLE (1UL << 1) # define PR_SPEC_ENABLE (1UL << 1)
# define PR_SPEC_DISABLE (1UL << 2) # define PR_SPEC_DISABLE (1UL << 2)
# define PR_SPEC_FORCE_DISABLE (1UL << 3)
#endif /* _LINUX_PRCTL_H */ #endif /* _LINUX_PRCTL_H */