| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Thu, 3 May 2018 22:09:15 +0200 |
| Subject: prctl: Add force disable speculation |
| |
| commit 356e4bfff2c5489e016fdb925adbf12a1e3950ee 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> |
| [bwh: Backported to 3.16: adjust filename, context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| Documentation/spec_ctrl.rst | 34 +++++++++++++++++++++------------- |
| arch/x86/kernel/cpu/bugs.c | 35 +++++++++++++++++++++++++---------- |
| fs/proc/array.c | 3 +++ |
| include/linux/sched.h | 10 +++++++++- |
| include/uapi/linux/prctl.h | 1 + |
| 5 files changed, 59 insertions(+), 24 deletions(-) |
| |
| --- a/Documentation/spec_ctrl.rst |
| +++ b/Documentation/spec_ctrl.rst |
| @@ -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 |
| -==== ================ =================================================== |
| +==== ===================== =================================================== |
| +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); |
| --- a/arch/x86/kernel/cpu/bugs.c |
| +++ b/arch/x86/kernel/cpu/bugs.c |
| @@ -570,21 +570,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; |
| @@ -596,7 +612,9 @@ static int ssb_prctl_get(struct task_str |
| 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: |
| @@ -609,9 +627,6 @@ static int ssb_prctl_get(struct task_str |
| 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); |
| --- a/fs/proc/array.c |
| +++ b/fs/proc/array.c |
| @@ -335,6 +335,9 @@ static inline void task_seccomp(struct s |
| 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; |
| --- a/include/linux/sched.h |
| +++ b/include/linux/sched.h |
| @@ -1973,7 +1973,8 @@ static inline void memalloc_noio_restore |
| #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) \ |
| @@ -1996,6 +1997,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) |
| + |
| /* |
| * task->jobctl flags |
| */ |
| --- a/include/uapi/linux/prctl.h |
| +++ b/include/uapi/linux/prctl.h |
| @@ -162,5 +162,6 @@ |
| # 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 */ |