Skip to content

Commit 3a15fb6

Browse files
Christian Braunerkees
authored andcommitted
seccomp: release filter after task is fully dead
The seccomp filter used to be released in free_task() which is called asynchronously via call_rcu() and assorted mechanisms. Since we need to inform tasks waiting on the seccomp notifier when a filter goes empty we will notify them as soon as a task has been marked fully dead in release_task(). To not split seccomp cleanup into two parts, move filter release out of free_task() and into release_task() after we've unhashed struct task from struct pid, exited signals, and unlinked it from the threadgroups' thread list. We'll put the empty filter notification infrastructure into it in a follow up patch. This also renames put_seccomp_filter() to seccomp_filter_release() which is a more descriptive name of what we're doing here especially once we've added the empty filter notification mechanism in there. We're also NULL-ing the task's filter tree entrypoint which seems cleaner than leaving a dangling pointer in there. Note that this shouldn't need any memory barriers since we're calling this when the task is in release_task() which means it's EXIT_DEAD. So it can't modify its seccomp filters anymore. You can also see this from the point where we're calling seccomp_filter_release(). It's after __exit_signal() and at this point, tsk->sighand will already have been NULLed which is required for thread-sync and filter installation alike. Cc: Tycho Andersen <[email protected]> Cc: Kees Cook <[email protected]> Cc: Matt Denton <[email protected]> Cc: Sargun Dhillon <[email protected]> Cc: Jann Horn <[email protected]> Cc: Chris Palmer <[email protected]> Cc: Aleksa Sarai <[email protected]> Cc: Robert Sesek <[email protected]> Cc: Jeffrey Vander Stoep <[email protected]> Cc: Linux Containers <[email protected]> Signed-off-by: Christian Brauner <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Kees Cook <[email protected]>
1 parent b707dde commit 3a15fb6

File tree

4 files changed

+40
-28
lines changed

4 files changed

+40
-28
lines changed

include/linux/seccomp.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ static inline int seccomp_mode(struct seccomp *s)
8484
#endif /* CONFIG_SECCOMP */
8585

8686
#ifdef CONFIG_SECCOMP_FILTER
87-
extern void put_seccomp_filter(struct task_struct *tsk);
87+
extern void seccomp_filter_release(struct task_struct *tsk);
8888
extern void get_seccomp_filter(struct task_struct *tsk);
8989
#else /* CONFIG_SECCOMP_FILTER */
90-
static inline void put_seccomp_filter(struct task_struct *tsk)
90+
static inline void seccomp_filter_release(struct task_struct *tsk)
9191
{
9292
return;
9393
}

kernel/exit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ void release_task(struct task_struct *p)
217217
}
218218

219219
write_unlock_irq(&tasklist_lock);
220+
seccomp_filter_release(p);
220221
proc_flush_pid(thread_pid);
221222
put_pid(thread_pid);
222223
release_thread(p);

kernel/fork.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,6 @@ void free_task(struct task_struct *tsk)
473473
#endif
474474
rt_mutex_debug_task_free(tsk);
475475
ftrace_graph_exit_task(tsk);
476-
put_seccomp_filter(tsk);
477476
arch_release_task_struct(tsk);
478477
if (tsk->flags & PF_KTHREAD)
479478
free_kthread_struct(tsk);

kernel/seccomp.c

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,42 @@ static inline pid_t seccomp_can_sync_threads(void)
368368
return 0;
369369
}
370370

371+
static inline void seccomp_filter_free(struct seccomp_filter *filter)
372+
{
373+
if (filter) {
374+
bpf_prog_destroy(filter->prog);
375+
kfree(filter);
376+
}
377+
}
378+
379+
static void __put_seccomp_filter(struct seccomp_filter *orig)
380+
{
381+
/* Clean up single-reference branches iteratively. */
382+
while (orig && refcount_dec_and_test(&orig->refs)) {
383+
struct seccomp_filter *freeme = orig;
384+
orig = orig->prev;
385+
seccomp_filter_free(freeme);
386+
}
387+
}
388+
389+
/**
390+
* seccomp_filter_release - Detach the task from its filter tree
391+
* and drop its reference count during
392+
* exit.
393+
*
394+
* This function should only be called when the task is exiting as
395+
* it detaches it from its filter tree. As such, READ_ONCE() and
396+
* barriers are not needed here, as would normally be needed.
397+
*/
398+
void seccomp_filter_release(struct task_struct *tsk)
399+
{
400+
struct seccomp_filter *orig = tsk->seccomp.filter;
401+
402+
/* Detach task from its filter tree. */
403+
tsk->seccomp.filter = NULL;
404+
__put_seccomp_filter(orig);
405+
}
406+
371407
/**
372408
* seccomp_sync_threads: sets all threads to use current's filter
373409
*
@@ -397,7 +433,7 @@ static inline void seccomp_sync_threads(unsigned long flags)
397433
* current's path will hold a reference. (This also
398434
* allows a put before the assignment.)
399435
*/
400-
put_seccomp_filter(thread);
436+
__put_seccomp_filter(thread->seccomp.filter);
401437
smp_store_release(&thread->seccomp.filter,
402438
caller->seccomp.filter);
403439
atomic_set(&thread->seccomp.filter_count,
@@ -571,30 +607,6 @@ void get_seccomp_filter(struct task_struct *tsk)
571607
__get_seccomp_filter(orig);
572608
}
573609

574-
static inline void seccomp_filter_free(struct seccomp_filter *filter)
575-
{
576-
if (filter) {
577-
bpf_prog_destroy(filter->prog);
578-
kfree(filter);
579-
}
580-
}
581-
582-
static void __put_seccomp_filter(struct seccomp_filter *orig)
583-
{
584-
/* Clean up single-reference branches iteratively. */
585-
while (orig && refcount_dec_and_test(&orig->refs)) {
586-
struct seccomp_filter *freeme = orig;
587-
orig = orig->prev;
588-
seccomp_filter_free(freeme);
589-
}
590-
}
591-
592-
/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
593-
void put_seccomp_filter(struct task_struct *tsk)
594-
{
595-
__put_seccomp_filter(tsk->seccomp.filter);
596-
}
597-
598610
static void seccomp_init_siginfo(kernel_siginfo_t *info, int syscall, int reason)
599611
{
600612
clear_siginfo(info);

0 commit comments

Comments
 (0)