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

Commit 1ca995e

Browse files
committed
Merge tag 'seccomp-v6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull seccomp updates from Kees Cook: - interrupt SECCOMP_IOCTL_NOTIF_RECV when all users exit (Andrei Vagin) - Update selftests to check for expected NOTIF_RECV exits (Andrei Vagin) * tag 'seccomp-v6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: selftests/seccomp: check that a zombie leader doesn't affect others selftests/seccomp: add test for NOTIF_RECV and unused filters seccomp: release task filters when the task exits seccomp: interrupt SECCOMP_IOCTL_NOTIF_RECV when all users have exited
2 parents 72fda6c + f0c508f commit 1ca995e

File tree

3 files changed

+157
-7
lines changed

3 files changed

+157
-7
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);
@@ -834,6 +833,8 @@ void __noreturn do_exit(long code)
834833
io_uring_files_cancel();
835834
exit_signals(tsk); /* sets PF_EXITING */
836835

836+
seccomp_filter_release(tsk);
837+
837838
acct_update_integrals(tsk);
838839
group_dead = atomic_dec_and_test(&tsk->signal->live);
839840
if (group_dead) {

kernel/seccomp.c

Lines changed: 24 additions & 6 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

@@ -1466,7 +1479,7 @@ static int recv_wake_function(wait_queue_entry_t *wait, unsigned int mode, int s
14661479
void *key)
14671480
{
14681481
/* Avoid a wakeup if event not interesting for us. */
1469-
if (key && !(key_to_poll(key) & (EPOLLIN | EPOLLERR)))
1482+
if (key && !(key_to_poll(key) & (EPOLLIN | EPOLLERR | EPOLLHUP)))
14701483
return 0;
14711484
return autoremove_wake_function(wait, mode, sync, key);
14721485
}
@@ -1476,6 +1489,9 @@ static int recv_wait_event(struct seccomp_filter *filter)
14761489
DEFINE_WAIT_FUNC(wait, recv_wake_function);
14771490
int ret;
14781491

1492+
if (refcount_read(&filter->users) == 0)
1493+
return 0;
1494+
14791495
if (atomic_dec_if_positive(&filter->notif->requests) >= 0)
14801496
return 0;
14811497

@@ -1484,6 +1500,8 @@ static int recv_wait_event(struct seccomp_filter *filter)
14841500

14851501
if (atomic_dec_if_positive(&filter->notif->requests) >= 0)
14861502
break;
1503+
if (refcount_read(&filter->users) == 0)
1504+
break;
14871505

14881506
if (ret)
14891507
return ret;

tools/testing/selftests/seccomp/seccomp_bpf.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3954,6 +3954,60 @@ TEST(user_notification_filter_empty)
39543954
EXPECT_GT((pollfd.revents & POLLHUP) ?: 0, 0);
39553955
}
39563956

3957+
TEST(user_ioctl_notification_filter_empty)
3958+
{
3959+
pid_t pid;
3960+
long ret;
3961+
int status, p[2];
3962+
struct __clone_args args = {
3963+
.flags = CLONE_FILES,
3964+
.exit_signal = SIGCHLD,
3965+
};
3966+
struct seccomp_notif req = {};
3967+
3968+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
3969+
ASSERT_EQ(0, ret) {
3970+
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
3971+
}
3972+
3973+
if (__NR_clone3 < 0)
3974+
SKIP(return, "Test not built with clone3 support");
3975+
3976+
ASSERT_EQ(0, pipe(p));
3977+
3978+
pid = sys_clone3(&args, sizeof(args));
3979+
ASSERT_GE(pid, 0);
3980+
3981+
if (pid == 0) {
3982+
int listener;
3983+
3984+
listener = user_notif_syscall(__NR_mknodat, SECCOMP_FILTER_FLAG_NEW_LISTENER);
3985+
if (listener < 0)
3986+
_exit(EXIT_FAILURE);
3987+
3988+
if (dup2(listener, 200) != 200)
3989+
_exit(EXIT_FAILURE);
3990+
close(p[1]);
3991+
close(listener);
3992+
sleep(1);
3993+
3994+
_exit(EXIT_SUCCESS);
3995+
}
3996+
if (read(p[0], &status, 1) != 0)
3997+
_exit(EXIT_SUCCESS);
3998+
close(p[0]);
3999+
/*
4000+
* The seccomp filter has become unused so we should be notified once
4001+
* the kernel gets around to cleaning up task struct.
4002+
*/
4003+
EXPECT_EQ(ioctl(200, SECCOMP_IOCTL_NOTIF_RECV, &req), -1);
4004+
EXPECT_EQ(errno, ENOENT);
4005+
4006+
EXPECT_EQ(waitpid(pid, &status, 0), pid);
4007+
EXPECT_EQ(true, WIFEXITED(status));
4008+
EXPECT_EQ(0, WEXITSTATUS(status));
4009+
}
4010+
39574011
static void *do_thread(void *data)
39584012
{
39594013
return NULL;
@@ -4755,6 +4809,83 @@ TEST(user_notification_wait_killable_fatal)
47554809
EXPECT_EQ(SIGTERM, WTERMSIG(status));
47564810
}
47574811

4812+
struct tsync_vs_thread_leader_args {
4813+
pthread_t leader;
4814+
};
4815+
4816+
static void *tsync_vs_dead_thread_leader_sibling(void *_args)
4817+
{
4818+
struct sock_filter allow_filter[] = {
4819+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
4820+
};
4821+
struct sock_fprog allow_prog = {
4822+
.len = (unsigned short)ARRAY_SIZE(allow_filter),
4823+
.filter = allow_filter,
4824+
};
4825+
struct tsync_vs_thread_leader_args *args = _args;
4826+
void *retval;
4827+
long ret;
4828+
4829+
ret = pthread_join(args->leader, &retval);
4830+
if (ret)
4831+
exit(1);
4832+
if (retval != _args)
4833+
exit(2);
4834+
ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &allow_prog);
4835+
if (ret)
4836+
exit(3);
4837+
4838+
exit(0);
4839+
}
4840+
4841+
/*
4842+
* Ensure that a dead thread leader doesn't prevent installing new filters with
4843+
* SECCOMP_FILTER_FLAG_TSYNC from other threads.
4844+
*/
4845+
TEST(tsync_vs_dead_thread_leader)
4846+
{
4847+
int status;
4848+
pid_t pid;
4849+
long ret;
4850+
4851+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
4852+
ASSERT_EQ(0, ret) {
4853+
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
4854+
}
4855+
4856+
pid = fork();
4857+
ASSERT_GE(pid, 0);
4858+
4859+
if (pid == 0) {
4860+
struct sock_filter allow_filter[] = {
4861+
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
4862+
};
4863+
struct sock_fprog allow_prog = {
4864+
.len = (unsigned short)ARRAY_SIZE(allow_filter),
4865+
.filter = allow_filter,
4866+
};
4867+
struct tsync_vs_thread_leader_args *args;
4868+
pthread_t sibling;
4869+
4870+
args = malloc(sizeof(*args));
4871+
ASSERT_NE(NULL, args);
4872+
args->leader = pthread_self();
4873+
4874+
ret = pthread_create(&sibling, NULL,
4875+
tsync_vs_dead_thread_leader_sibling, args);
4876+
ASSERT_EQ(0, ret);
4877+
4878+
/* Install a new filter just to the leader thread. */
4879+
ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &allow_prog);
4880+
ASSERT_EQ(0, ret);
4881+
pthread_exit(args);
4882+
exit(1);
4883+
}
4884+
4885+
EXPECT_EQ(pid, waitpid(pid, &status, 0));
4886+
EXPECT_EQ(0, status);
4887+
}
4888+
47584889
/*
47594890
* TODO:
47604891
* - expand NNP testing

0 commit comments

Comments
 (0)