nospec: Allow getting/setting on non-current task

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>
This commit is contained in:
Kees Cook 2018-05-01 15:19:04 -07:00 committed by Thomas Gleixner
parent a73ec77ee1
commit 7bbf1373e2
3 changed files with 26 additions and 17 deletions

View File

@ -530,31 +530,35 @@ static void ssb_select_mitigation()
#undef pr_fmt #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) if (ssb_mode != SPEC_STORE_BYPASS_PRCTL)
return -ENXIO; return -ENXIO;
if (ctrl == PR_SPEC_ENABLE) if (ctrl == PR_SPEC_ENABLE)
clear_tsk_thread_flag(current, TIF_RDS); clear_tsk_thread_flag(task, TIF_RDS);
else 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(); speculative_store_bypass_update();
return 0; return 0;
} }
static int ssb_prctl_get(void) static int ssb_prctl_get(struct task_struct *task)
{ {
switch (ssb_mode) { switch (ssb_mode) {
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(current, TIF_RDS)) if (test_tsk_thread_flag(task, TIF_RDS))
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:
@ -564,24 +568,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) if (ctrl != PR_SPEC_ENABLE && ctrl != PR_SPEC_DISABLE)
return -ERANGE; return -ERANGE;
switch (which) { switch (which) {
case PR_SPEC_STORE_BYPASS: case PR_SPEC_STORE_BYPASS:
return ssb_prctl_set(ctrl); return ssb_prctl_set(task, ctrl);
default: default:
return -ENODEV; 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) { switch (which) {
case PR_SPEC_STORE_BYPASS: case PR_SPEC_STORE_BYPASS:
return ssb_prctl_get(); return ssb_prctl_get(task);
default: default:
return -ENODEV; return -ENODEV;
} }

View File

@ -7,6 +7,8 @@
#define _LINUX_NOSPEC_H #define _LINUX_NOSPEC_H
#include <asm/barrier.h> #include <asm/barrier.h>
struct task_struct;
/** /**
* array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
* @index: array element index * @index: array element index
@ -57,7 +59,8 @@ static inline unsigned long array_index_mask_nospec(unsigned long index,
}) })
/* Speculation control prctl */ /* Speculation control prctl */
int arch_prctl_spec_ctrl_get(unsigned long which); int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which);
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);
#endif /* _LINUX_NOSPEC_H */ #endif /* _LINUX_NOSPEC_H */

View File

@ -2244,12 +2244,13 @@ static int propagate_has_child_subreaper(struct task_struct *p, void *data)
return 1; return 1;
} }
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; 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; return -EINVAL;
} }
@ -2465,12 +2466,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
case PR_GET_SPECULATION_CTRL: case PR_GET_SPECULATION_CTRL:
if (arg3 || arg4 || arg5) if (arg3 || arg4 || arg5)
return -EINVAL; return -EINVAL;
error = arch_prctl_spec_ctrl_get(arg2); error = arch_prctl_spec_ctrl_get(me, arg2);
break; break;
case PR_SET_SPECULATION_CTRL: case PR_SET_SPECULATION_CTRL:
if (arg4 || arg5) if (arg4 || arg5)
return -EINVAL; return -EINVAL;
error = arch_prctl_spec_ctrl_set(arg2, arg3); error = arch_prctl_spec_ctrl_set(me, arg2, arg3);
break; break;
default: default:
error = -EINVAL; error = -EINVAL;