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 | |
| 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>
| -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); |
