diff options
Diffstat (limited to 'kernel/seccomp.c')
-rw-r--r-- | kernel/seccomp.c | 135 |
1 files changed, 134 insertions, 1 deletions
diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 9065d2c79c56..74f460179171 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #ifdef CONFIG_SECCOMP_FILTER | 26 | #ifdef CONFIG_SECCOMP_FILTER |
27 | #include <asm/syscall.h> | 27 | #include <asm/syscall.h> |
28 | #include <linux/filter.h> | 28 | #include <linux/filter.h> |
29 | #include <linux/pid.h> | ||
29 | #include <linux/ptrace.h> | 30 | #include <linux/ptrace.h> |
30 | #include <linux/security.h> | 31 | #include <linux/security.h> |
31 | #include <linux/tracehook.h> | 32 | #include <linux/tracehook.h> |
@@ -225,6 +226,114 @@ static inline void seccomp_assign_mode(struct task_struct *task, | |||
225 | } | 226 | } |
226 | 227 | ||
227 | #ifdef CONFIG_SECCOMP_FILTER | 228 | #ifdef CONFIG_SECCOMP_FILTER |
229 | /* Returns 1 if the parent is an ancestor of the child. */ | ||
230 | static int is_ancestor(struct seccomp_filter *parent, | ||
231 | struct seccomp_filter *child) | ||
232 | { | ||
233 | /* NULL is the root ancestor. */ | ||
234 | if (parent == NULL) | ||
235 | return 1; | ||
236 | for (; child; child = child->prev) | ||
237 | if (child == parent) | ||
238 | return 1; | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * seccomp_can_sync_threads: checks if all threads can be synchronized | ||
244 | * | ||
245 | * Expects sighand and cred_guard_mutex locks to be held. | ||
246 | * | ||
247 | * Returns 0 on success, -ve on error, or the pid of a thread which was | ||
248 | * either not in the correct seccomp mode or it did not have an ancestral | ||
249 | * seccomp filter. | ||
250 | */ | ||
251 | static inline pid_t seccomp_can_sync_threads(void) | ||
252 | { | ||
253 | struct task_struct *thread, *caller; | ||
254 | |||
255 | BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); | ||
256 | BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||
257 | |||
258 | /* Validate all threads being eligible for synchronization. */ | ||
259 | caller = current; | ||
260 | for_each_thread(caller, thread) { | ||
261 | pid_t failed; | ||
262 | |||
263 | /* Skip current, since it is initiating the sync. */ | ||
264 | if (thread == caller) | ||
265 | continue; | ||
266 | |||
267 | if (thread->seccomp.mode == SECCOMP_MODE_DISABLED || | ||
268 | (thread->seccomp.mode == SECCOMP_MODE_FILTER && | ||
269 | is_ancestor(thread->seccomp.filter, | ||
270 | caller->seccomp.filter))) | ||
271 | continue; | ||
272 | |||
273 | /* Return the first thread that cannot be synchronized. */ | ||
274 | failed = task_pid_vnr(thread); | ||
275 | /* If the pid cannot be resolved, then return -ESRCH */ | ||
276 | if (unlikely(WARN_ON(failed == 0))) | ||
277 | failed = -ESRCH; | ||
278 | return failed; | ||
279 | } | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * seccomp_sync_threads: sets all threads to use current's filter | ||
286 | * | ||
287 | * Expects sighand and cred_guard_mutex locks to be held, and for | ||
288 | * seccomp_can_sync_threads() to have returned success already | ||
289 | * without dropping the locks. | ||
290 | * | ||
291 | */ | ||
292 | static inline void seccomp_sync_threads(void) | ||
293 | { | ||
294 | struct task_struct *thread, *caller; | ||
295 | |||
296 | BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); | ||
297 | BUG_ON(!spin_is_locked(¤t->sighand->siglock)); | ||
298 | |||
299 | /* Synchronize all threads. */ | ||
300 | caller = current; | ||
301 | for_each_thread(caller, thread) { | ||
302 | /* Skip current, since it needs no changes. */ | ||
303 | if (thread == caller) | ||
304 | continue; | ||
305 | |||
306 | /* Get a task reference for the new leaf node. */ | ||
307 | get_seccomp_filter(caller); | ||
308 | /* | ||
309 | * Drop the task reference to the shared ancestor since | ||
310 | * current's path will hold a reference. (This also | ||
311 | * allows a put before the assignment.) | ||
312 | */ | ||
313 | put_seccomp_filter(thread); | ||
314 | smp_store_release(&thread->seccomp.filter, | ||
315 | caller->seccomp.filter); | ||
316 | /* | ||
317 | * Opt the other thread into seccomp if needed. | ||
318 | * As threads are considered to be trust-realm | ||
319 | * equivalent (see ptrace_may_access), it is safe to | ||
320 | * allow one thread to transition the other. | ||
321 | */ | ||
322 | if (thread->seccomp.mode == SECCOMP_MODE_DISABLED) { | ||
323 | /* | ||
324 | * Don't let an unprivileged task work around | ||
325 | * the no_new_privs restriction by creating | ||
326 | * a thread that sets it up, enters seccomp, | ||
327 | * then dies. | ||
328 | */ | ||
329 | if (task_no_new_privs(caller)) | ||
330 | task_set_no_new_privs(thread); | ||
331 | |||
332 | seccomp_assign_mode(thread, SECCOMP_MODE_FILTER); | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | |||
228 | /** | 337 | /** |
229 | * seccomp_prepare_filter: Prepares a seccomp filter for use. | 338 | * seccomp_prepare_filter: Prepares a seccomp filter for use. |
230 | * @fprog: BPF program to install | 339 | * @fprog: BPF program to install |
@@ -364,6 +473,15 @@ static long seccomp_attach_filter(unsigned int flags, | |||
364 | if (total_insns > MAX_INSNS_PER_PATH) | 473 | if (total_insns > MAX_INSNS_PER_PATH) |
365 | return -ENOMEM; | 474 | return -ENOMEM; |
366 | 475 | ||
476 | /* If thread sync has been requested, check that it is possible. */ | ||
477 | if (flags & SECCOMP_FILTER_FLAG_TSYNC) { | ||
478 | int ret; | ||
479 | |||
480 | ret = seccomp_can_sync_threads(); | ||
481 | if (ret) | ||
482 | return ret; | ||
483 | } | ||
484 | |||
367 | /* | 485 | /* |
368 | * If there is an existing filter, make it the prev and don't drop its | 486 | * If there is an existing filter, make it the prev and don't drop its |
369 | * task reference. | 487 | * task reference. |
@@ -371,6 +489,10 @@ static long seccomp_attach_filter(unsigned int flags, | |||
371 | filter->prev = current->seccomp.filter; | 489 | filter->prev = current->seccomp.filter; |
372 | current->seccomp.filter = filter; | 490 | current->seccomp.filter = filter; |
373 | 491 | ||
492 | /* Now that the new filter is in place, synchronize to all threads. */ | ||
493 | if (flags & SECCOMP_FILTER_FLAG_TSYNC) | ||
494 | seccomp_sync_threads(); | ||
495 | |||
374 | return 0; | 496 | return 0; |
375 | } | 497 | } |
376 | 498 | ||
@@ -590,7 +712,7 @@ static long seccomp_set_mode_filter(unsigned int flags, | |||
590 | long ret = -EINVAL; | 712 | long ret = -EINVAL; |
591 | 713 | ||
592 | /* Validate flags. */ | 714 | /* Validate flags. */ |
593 | if (flags != 0) | 715 | if (flags & ~SECCOMP_FILTER_FLAG_MASK) |
594 | return -EINVAL; | 716 | return -EINVAL; |
595 | 717 | ||
596 | /* Prepare the new filter before holding any locks. */ | 718 | /* Prepare the new filter before holding any locks. */ |
@@ -598,6 +720,14 @@ static long seccomp_set_mode_filter(unsigned int flags, | |||
598 | if (IS_ERR(prepared)) | 720 | if (IS_ERR(prepared)) |
599 | return PTR_ERR(prepared); | 721 | return PTR_ERR(prepared); |
600 | 722 | ||
723 | /* | ||
724 | * Make sure we cannot change seccomp or nnp state via TSYNC | ||
725 | * while another thread is in the middle of calling exec. | ||
726 | */ | ||
727 | if (flags & SECCOMP_FILTER_FLAG_TSYNC && | ||
728 | mutex_lock_killable(¤t->signal->cred_guard_mutex)) | ||
729 | goto out_free; | ||
730 | |||
601 | spin_lock_irq(¤t->sighand->siglock); | 731 | spin_lock_irq(¤t->sighand->siglock); |
602 | 732 | ||
603 | if (!seccomp_may_assign_mode(seccomp_mode)) | 733 | if (!seccomp_may_assign_mode(seccomp_mode)) |
@@ -612,6 +742,9 @@ static long seccomp_set_mode_filter(unsigned int flags, | |||
612 | seccomp_assign_mode(current, seccomp_mode); | 742 | seccomp_assign_mode(current, seccomp_mode); |
613 | out: | 743 | out: |
614 | spin_unlock_irq(¤t->sighand->siglock); | 744 | spin_unlock_irq(¤t->sighand->siglock); |
745 | if (flags & SECCOMP_FILTER_FLAG_TSYNC) | ||
746 | mutex_unlock(¤t->signal->cred_guard_mutex); | ||
747 | out_free: | ||
615 | seccomp_filter_free(prepared); | 748 | seccomp_filter_free(prepared); |
616 | return ret; | 749 | return ret; |
617 | } | 750 | } |