| From foo@baz Mon May 21 22:23:32 CEST 2018 |
| From: Kees Cook <keescook@chromium.org> |
| Date: Tue, 1 May 2018 15:19:04 -0700 |
| Subject: nospec: Allow getting/setting on non-current task |
| |
| From: Kees Cook <keescook@chromium.org> |
| |
| commit 7bbf1373e228840bb0295a2ca26d548ef37f448e upstream |
| |
| Adjust arch_prctl_get/set_spec_ctrl() to operate on tasks other than |
| current. |
| |
| This is needed both for /proc/$pid/status queries and for seccomp (since |
| thread-syncing can trigger seccomp in non-current threads). |
| |
| Signed-off-by: Kees Cook <keescook@chromium.org> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/x86/kernel/cpu/bugs.c | 27 ++++++++++++++++----------- |
| include/linux/nospec.h | 7 +++++-- |
| kernel/sys.c | 9 +++++---- |
| 3 files changed, 26 insertions(+), 17 deletions(-) |
| |
| --- a/arch/x86/kernel/cpu/bugs.c |
| +++ b/arch/x86/kernel/cpu/bugs.c |
| @@ -529,31 +529,35 @@ static void ssb_select_mitigation() |
| |
| #undef pr_fmt |
| |
| -static int ssb_prctl_set(unsigned long ctrl) |
| +static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) |
| { |
| - bool rds = !!test_tsk_thread_flag(current, TIF_RDS); |
| + bool rds = !!test_tsk_thread_flag(task, TIF_RDS); |
| |
| if (ssb_mode != SPEC_STORE_BYPASS_PRCTL) |
| return -ENXIO; |
| |
| if (ctrl == PR_SPEC_ENABLE) |
| - clear_tsk_thread_flag(current, TIF_RDS); |
| + clear_tsk_thread_flag(task, TIF_RDS); |
| else |
| - set_tsk_thread_flag(current, TIF_RDS); |
| + set_tsk_thread_flag(task, TIF_RDS); |
| |
| - if (rds != !!test_tsk_thread_flag(current, TIF_RDS)) |
| + /* |
| + * 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)) |
| speculative_store_bypass_update(); |
| |
| return 0; |
| } |
| |
| -static int ssb_prctl_get(void) |
| +static int ssb_prctl_get(struct task_struct *task) |
| { |
| switch (ssb_mode) { |
| case SPEC_STORE_BYPASS_DISABLE: |
| return PR_SPEC_DISABLE; |
| case SPEC_STORE_BYPASS_PRCTL: |
| - if (test_tsk_thread_flag(current, TIF_RDS)) |
| + if (test_tsk_thread_flag(task, TIF_RDS)) |
| return PR_SPEC_PRCTL | PR_SPEC_DISABLE; |
| return PR_SPEC_PRCTL | PR_SPEC_ENABLE; |
| default: |
| @@ -563,24 +567,25 @@ static int ssb_prctl_get(void) |
| } |
| } |
| |
| -int arch_prctl_spec_ctrl_set(unsigned long which, unsigned long ctrl) |
| +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(ctrl); |
| + return ssb_prctl_set(task, ctrl); |
| default: |
| return -ENODEV; |
| } |
| } |
| |
| -int arch_prctl_spec_ctrl_get(unsigned long which) |
| +int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) |
| { |
| switch (which) { |
| case PR_SPEC_STORE_BYPASS: |
| - return ssb_prctl_get(); |
| + return ssb_prctl_get(task); |
| default: |
| return -ENODEV; |
| } |
| --- a/include/linux/nospec.h |
| +++ b/include/linux/nospec.h |
| @@ -7,6 +7,8 @@ |
| #define _LINUX_NOSPEC_H |
| #include <asm/barrier.h> |
| |
| +struct task_struct; |
| + |
| /** |
| * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise |
| * @index: array element index |
| @@ -57,7 +59,8 @@ static inline unsigned long array_index_ |
| }) |
| |
| /* Speculation control prctl */ |
| -int arch_prctl_spec_ctrl_get(unsigned long which); |
| -int arch_prctl_spec_ctrl_set(unsigned long which, unsigned long ctrl); |
| +int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which); |
| +int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, |
| + unsigned long ctrl); |
| |
| #endif /* _LINUX_NOSPEC_H */ |
| --- a/kernel/sys.c |
| +++ b/kernel/sys.c |
| @@ -2074,12 +2074,13 @@ static int prctl_get_tid_address(struct |
| } |
| #endif |
| |
| -int __weak arch_prctl_spec_ctrl_get(unsigned long which) |
| +int __weak arch_prctl_spec_ctrl_get(struct task_struct *t, unsigned long which) |
| { |
| return -EINVAL; |
| } |
| |
| -int __weak arch_prctl_spec_ctrl_set(unsigned long which, unsigned long ctrl) |
| +int __weak arch_prctl_spec_ctrl_set(struct task_struct *t, unsigned long which, |
| + unsigned long ctrl) |
| { |
| return -EINVAL; |
| } |
| @@ -2285,12 +2286,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsi |
| case PR_GET_SPECULATION_CTRL: |
| if (arg3 || arg4 || arg5) |
| return -EINVAL; |
| - error = arch_prctl_spec_ctrl_get(arg2); |
| + error = arch_prctl_spec_ctrl_get(me, arg2); |
| break; |
| case PR_SET_SPECULATION_CTRL: |
| if (arg4 || arg5) |
| return -EINVAL; |
| - error = arch_prctl_spec_ctrl_set(arg2, arg3); |
| + error = arch_prctl_spec_ctrl_set(me, arg2, arg3); |
| break; |
| default: |
| error = -EINVAL; |