Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit bfafe5e

Browse files
avaginkees
authored andcommitted
seccomp: release task filters when the task exits
Previously, seccomp filters were released in release_task(), which required the process to exit and its zombie to be collected. However, exited threads/processes can't trigger any seccomp events, making it more logical to release filters upon task exits. This adjustment simplifies scenarios where a parent is tracing its child process. The parent process can now handle all events from a seccomp listening descriptor and then call wait to collect a child zombie. seccomp_filter_release takes the siglock to avoid races with seccomp_sync_threads. There was an idea to bypass taking the lock by checking PF_EXITING, but it can be set without holding siglock if threads have SIGNAL_GROUP_EXIT. This means it can happen concurently with seccomp_filter_release. This change also fixes another minor problem. Suppose that a group leader installs the new filter without SECCOMP_FILTER_FLAG_TSYNC, exits, and becomes a zombie. Without this change, SECCOMP_FILTER_FLAG_TSYNC from any other thread can never succeed, seccomp_can_sync_threads() will check a zombie leader and is_ancestor() will fail. Reviewed-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Andrei Vagin <avagin@google.com> Link: https://lore.kernel.org/r/20240628021014.231976-3-avagin@google.com Reviewed-by: Tycho Andersen <tandersen@netflix.com> Signed-off-by: Kees Cook <kees@kernel.org>
1 parent 95036a7 commit bfafe5e

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

kernel/exit.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,6 @@ void release_task(struct task_struct *p)
277277
}
278278

279279
write_unlock_irq(&tasklist_lock);
280-
seccomp_filter_release(p);
281280
proc_flush_pid(thread_pid);
282281
put_pid(thread_pid);
283282
release_thread(p);
@@ -832,6 +831,8 @@ void __noreturn do_exit(long code)
832831
io_uring_files_cancel();
833832
exit_signals(tsk); /* sets PF_EXITING */
834833

834+
seccomp_filter_release(tsk);
835+
835836
acct_update_integrals(tsk);
836837
group_dead = atomic_dec_and_test(&tsk->signal->live);
837838
if (group_dead) {

kernel/seccomp.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,9 @@ static inline pid_t seccomp_can_sync_threads(void)
502502
/* Skip current, since it is initiating the sync. */
503503
if (thread == caller)
504504
continue;
505+
/* Skip exited threads. */
506+
if (thread->flags & PF_EXITING)
507+
continue;
505508

506509
if (thread->seccomp.mode == SECCOMP_MODE_DISABLED ||
507510
(thread->seccomp.mode == SECCOMP_MODE_FILTER &&
@@ -563,18 +566,21 @@ static void __seccomp_filter_release(struct seccomp_filter *orig)
563566
* @tsk: task the filter should be released from.
564567
*
565568
* This function should only be called when the task is exiting as
566-
* it detaches it from its filter tree. As such, READ_ONCE() and
567-
* barriers are not needed here, as would normally be needed.
569+
* it detaches it from its filter tree. PF_EXITING has to be set
570+
* for the task.
568571
*/
569572
void seccomp_filter_release(struct task_struct *tsk)
570573
{
571-
struct seccomp_filter *orig = tsk->seccomp.filter;
574+
struct seccomp_filter *orig;
572575

573-
/* We are effectively holding the siglock by not having any sighand. */
574-
WARN_ON(tsk->sighand != NULL);
576+
if (WARN_ON((tsk->flags & PF_EXITING) == 0))
577+
return;
575578

579+
spin_lock_irq(&tsk->sighand->siglock);
580+
orig = tsk->seccomp.filter;
576581
/* Detach task from its filter tree. */
577582
tsk->seccomp.filter = NULL;
583+
spin_unlock_irq(&tsk->sighand->siglock);
578584
__seccomp_filter_release(orig);
579585
}
580586

@@ -602,6 +608,13 @@ static inline void seccomp_sync_threads(unsigned long flags)
602608
if (thread == caller)
603609
continue;
604610

611+
/*
612+
* Skip exited threads. seccomp_filter_release could have
613+
* been already called for this task.
614+
*/
615+
if (thread->flags & PF_EXITING)
616+
continue;
617+
605618
/* Get a task reference for the new leaf node. */
606619
get_seccomp_filter(caller);
607620

0 commit comments

Comments
 (0)