Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

Kees Cook 3ba2530c dbd95212

+25 -11
+25 -11
kernel/seccomp.c
··· 173 173 */ 174 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 177 struct seccomp_data sd; 178 178 u32 ret = SECCOMP_RET_ALLOW; 179 179 180 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 182 return SECCOMP_RET_KILL; 183 + 184 + /* Make sure cross-thread synced filter points somewhere sane. */ 185 + smp_read_barrier_depends(); 183 186 184 187 populate_seccomp_data(&sd); 185 188 ··· 190 187 * All filters in the list are evaluated and the lowest BPF return 191 188 * value always takes priority (ignoring the DATA). 192 189 */ 193 - for (f = current->seccomp.filter; f; f = f->prev) { 190 + for (; f; f = f->prev) { 194 191 u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd); 195 192 196 193 if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) ··· 210 207 return true; 211 208 } 212 209 213 - static inline void seccomp_assign_mode(unsigned long seccomp_mode) 210 + static inline void seccomp_assign_mode(struct task_struct *task, 211 + unsigned long seccomp_mode) 214 212 { 215 - BUG_ON(!spin_is_locked(&current->sighand->siglock)); 213 + BUG_ON(!spin_is_locked(&task->sighand->siglock)); 216 214 217 - current->seccomp.mode = seccomp_mode; 218 - set_tsk_thread_flag(current, TIF_SECCOMP); 215 + task->seccomp.mode = seccomp_mode; 216 + /* 217 + * Make sure TIF_SECCOMP cannot be set before the mode (and 218 + * filter) is set. 219 + */ 220 + smp_mb__before_atomic(); 221 + set_tsk_thread_flag(task, TIF_SECCOMP); 219 222 } 220 223 221 224 #ifdef CONFIG_SECCOMP_FILTER ··· 444 435 445 436 int __secure_computing(int this_syscall) 446 437 { 447 - int mode = current->seccomp.mode; 448 438 int exit_sig = 0; 449 439 int *syscall; 450 440 u32 ret; 451 441 452 - switch (mode) { 442 + /* 443 + * Make sure that any changes to mode from another thread have 444 + * been seen after TIF_SECCOMP was seen. 445 + */ 446 + rmb(); 447 + 448 + switch (current->seccomp.mode) { 453 449 case SECCOMP_MODE_STRICT: 454 450 syscall = mode1_syscalls; 455 451 #ifdef CONFIG_COMPAT ··· 559 545 #ifdef TIF_NOTSC 560 546 disable_TSC(); 561 547 #endif 562 - seccomp_assign_mode(seccomp_mode); 548 + seccomp_assign_mode(current, seccomp_mode); 563 549 ret = 0; 564 550 565 551 out: ··· 609 595 /* Do not free the successfully attached filter. */ 610 596 prepared = NULL; 611 597 612 - seccomp_assign_mode(seccomp_mode); 598 + seccomp_assign_mode(current, seccomp_mode); 613 599 out: 614 600 spin_unlock_irq(&current->sighand->siglock); 615 601 seccomp_filter_free(prepared);