diff options
author | Kees Cook <keescook@chromium.org> | 2014-06-27 18:01:35 -0400 |
---|---|---|
committer | Kees Cook <keescook@chromium.org> | 2014-07-18 15:13:40 -0400 |
commit | 3ba2530cc06eb4aee4f1f754f43d781e8a12ee09 (patch) | |
tree | 29a898621ce07eabf881ef40123f2882d7dccc1a /kernel/seccomp.c | |
parent | dbd952127d11bb44a4ea30b08cc60531b6a23d71 (diff) |
seccomp: allow mode setting across threads
This changes the mode setting helper to allow threads to change the
seccomp mode from another thread. We must maintain barriers to keep
TIF_SECCOMP synchronized with the rest of the seccomp state.
Signed-off-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Andy Lutomirski <luto@amacapital.net>
Diffstat (limited to 'kernel/seccomp.c')
-rw-r--r-- | kernel/seccomp.c | 36 |
1 files changed, 25 insertions, 11 deletions
diff --git a/kernel/seccomp.c b/kernel/seccomp.c index d5543e787e4e..9065d2c79c56 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c | |||
@@ -173,21 +173,24 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) | |||
173 | */ | 173 | */ |
174 | static u32 seccomp_run_filters(int syscall) | 174 | static u32 seccomp_run_filters(int syscall) |
175 | { | 175 | { |
176 | struct seccomp_filter *f; | 176 | struct seccomp_filter *f = ACCESS_ONCE(current->seccomp.filter); |
177 | struct seccomp_data sd; | 177 | struct seccomp_data sd; |
178 | u32 ret = SECCOMP_RET_ALLOW; | 178 | u32 ret = SECCOMP_RET_ALLOW; |
179 | 179 | ||
180 | /* Ensure unexpected behavior doesn't result in failing open. */ | 180 | /* Ensure unexpected behavior doesn't result in failing open. */ |
181 | if (WARN_ON(current->seccomp.filter == NULL)) | 181 | if (unlikely(WARN_ON(f == NULL))) |
182 | return SECCOMP_RET_KILL; | 182 | return SECCOMP_RET_KILL; |
183 | 183 | ||
184 | /* Make sure cross-thread synced filter points somewhere sane. */ | ||
185 | smp_read_barrier_depends(); | ||
186 | |||
184 | populate_seccomp_data(&sd); | 187 | populate_seccomp_data(&sd); |
185 | 188 | ||
186 | /* | 189 | /* |
187 | * All filters in the list are evaluated and the lowest BPF return | 190 | * All filters in the list are evaluated and the lowest BPF return |
188 | * value always takes priority (ignoring the DATA). | 191 | * value always takes priority (ignoring the DATA). |
189 | */ | 192 | */ |
190 | for (f = current->seccomp.filter; f; f = f->prev) { | 193 | for (; f; f = f->prev) { |
191 | u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd); | 194 | u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd); |
192 | 195 | ||
193 | if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) | 196 | if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) |
@@ -207,12 +210,18 @@ static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) | |||
207 | return true; | 210 | return true; |
208 | } | 211 | } |
209 | 212 | ||
210 | static inline void seccomp_assign_mode(unsigned long seccomp_mode) | 213 | static inline void seccomp_assign_mode(struct task_struct *task, |
214 | unsigned long seccomp_mode) | ||
211 | { | 215 | { |
212 | BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | 216 | BUG_ON(!spin_is_locked(&task->sighand->siglock)); |
213 | 217 | ||
214 | current->seccomp.mode = seccomp_mode; | 218 | task->seccomp.mode = seccomp_mode; |
215 | set_tsk_thread_flag(current, TIF_SECCOMP); | 219 | /* |
220 | * Make sure TIF_SECCOMP cannot be set before the mode (and | ||
221 | * filter) is set. | ||
222 | */ | ||
223 | smp_mb__before_atomic(); | ||
224 | set_tsk_thread_flag(task, TIF_SECCOMP); | ||
216 | } | 225 | } |
217 | 226 | ||
218 | #ifdef CONFIG_SECCOMP_FILTER | 227 | #ifdef CONFIG_SECCOMP_FILTER |
@@ -435,12 +444,17 @@ static int mode1_syscalls_32[] = { | |||
435 | 444 | ||
436 | int __secure_computing(int this_syscall) | 445 | int __secure_computing(int this_syscall) |
437 | { | 446 | { |
438 | int mode = current->seccomp.mode; | ||
439 | int exit_sig = 0; | 447 | int exit_sig = 0; |
440 | int *syscall; | 448 | int *syscall; |
441 | u32 ret; | 449 | u32 ret; |
442 | 450 | ||
443 | switch (mode) { | 451 | /* |
452 | * Make sure that any changes to mode from another thread have | ||
453 | * been seen after TIF_SECCOMP was seen. | ||
454 | */ | ||
455 | rmb(); | ||
456 | |||
457 | switch (current->seccomp.mode) { | ||
444 | case SECCOMP_MODE_STRICT: | 458 | case SECCOMP_MODE_STRICT: |
445 | syscall = mode1_syscalls; | 459 | syscall = mode1_syscalls; |
446 | #ifdef CONFIG_COMPAT | 460 | #ifdef CONFIG_COMPAT |
@@ -545,7 +559,7 @@ static long seccomp_set_mode_strict(void) | |||
545 | #ifdef TIF_NOTSC | 559 | #ifdef TIF_NOTSC |
546 | disable_TSC(); | 560 | disable_TSC(); |
547 | #endif | 561 | #endif |
548 | seccomp_assign_mode(seccomp_mode); | 562 | seccomp_assign_mode(current, seccomp_mode); |
549 | ret = 0; | 563 | ret = 0; |
550 | 564 | ||
551 | out: | 565 | out: |
@@ -595,7 +609,7 @@ static long seccomp_set_mode_filter(unsigned int flags, | |||
595 | /* Do not free the successfully attached filter. */ | 609 | /* Do not free the successfully attached filter. */ |
596 | prepared = NULL; | 610 | prepared = NULL; |
597 | 611 | ||
598 | seccomp_assign_mode(seccomp_mode); | 612 | seccomp_assign_mode(current, seccomp_mode); |
599 | out: | 613 | out: |
600 | spin_unlock_irq(¤t->sighand->siglock); | 614 | spin_unlock_irq(¤t->sighand->siglock); |
601 | seccomp_filter_free(prepared); | 615 | seccomp_filter_free(prepared); |