Skip to content

Commit 3b96a9c

Browse files
sargunkees
authored andcommitted
selftests/seccomp: Add test for wait killable notifier
This verifies that if a filter is set up with the wait killable feature that it obeys the semantics that non-fatal signals are ignored during a notification after the notification is received. Cases tested: * Non-fatal signal prior to receive * Non-fatal signal during receive * Fatal signal after receive The normal signal handling is tested in user_notification_signal. That behaviour remains unchanged. On an unsupported kernel, these tests will immediately bail as it relies on a new seccomp flag. Signed-off-by: Sargun Dhillon <sargun@sargun.me> Signed-off-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/r/20220503080958.20220-4-sargun@sargun.me
1 parent 922a1b5 commit 3b96a9c

1 file changed

Lines changed: 228 additions & 0 deletions

File tree

tools/testing/selftests/seccomp/seccomp_bpf.c

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
#define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)
6161
#endif
6262

63+
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
64+
6365
#ifndef PR_SET_PTRACER
6466
# define PR_SET_PTRACER 0x59616d61
6567
#endif
@@ -269,6 +271,10 @@ struct seccomp_notif_addfd_big {
269271
#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4)
270272
#endif
271273

274+
#ifndef SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV
275+
#define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5)
276+
#endif
277+
272278
#ifndef seccomp
273279
int seccomp(unsigned int op, unsigned int flags, void *args)
274280
{
@@ -4428,6 +4434,228 @@ TEST(user_notification_fifo)
44284434
}
44294435
}
44304436

4437+
/* get_proc_syscall - Get the syscall in progress for a given pid
4438+
*
4439+
* Returns the current syscall number for a given process
4440+
* Returns -1 if not in syscall (running or blocked)
4441+
*/
4442+
static long get_proc_syscall(struct __test_metadata *_metadata, int pid)
4443+
{
4444+
char proc_path[100] = {0};
4445+
long ret = -1;
4446+
ssize_t nread;
4447+
char *line;
4448+
4449+
snprintf(proc_path, sizeof(proc_path), "/proc/%d/syscall", pid);
4450+
nread = get_nth(_metadata, proc_path, 1, &line);
4451+
ASSERT_GT(nread, 0);
4452+
4453+
if (!strncmp("running", line, MIN(7, nread)))
4454+
ret = strtol(line, NULL, 16);
4455+
4456+
free(line);
4457+
return ret;
4458+
}
4459+
4460+
/* Ensure non-fatal signals prior to receive are unmodified */
4461+
TEST(user_notification_wait_killable_pre_notification)
4462+
{
4463+
struct sigaction new_action = {
4464+
.sa_handler = signal_handler,
4465+
};
4466+
int listener, status, sk_pair[2];
4467+
pid_t pid;
4468+
long ret;
4469+
char c;
4470+
/* 100 ms */
4471+
struct timespec delay = { .tv_nsec = 100000000 };
4472+
4473+
ASSERT_EQ(sigemptyset(&new_action.sa_mask), 0);
4474+
4475+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
4476+
ASSERT_EQ(0, ret)
4477+
{
4478+
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
4479+
}
4480+
4481+
ASSERT_EQ(socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair), 0);
4482+
4483+
listener = user_notif_syscall(
4484+
__NR_getppid, SECCOMP_FILTER_FLAG_NEW_LISTENER |
4485+
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV);
4486+
ASSERT_GE(listener, 0);
4487+
4488+
/*
4489+
* Check that we can kill the process with SIGUSR1 prior to receiving
4490+
* the notification. SIGUSR1 is wired up to a custom signal handler,
4491+
* and make sure it gets called.
4492+
*/
4493+
pid = fork();
4494+
ASSERT_GE(pid, 0);
4495+
4496+
if (pid == 0) {
4497+
close(sk_pair[0]);
4498+
handled = sk_pair[1];
4499+
4500+
/* Setup the non-fatal sigaction without SA_RESTART */
4501+
if (sigaction(SIGUSR1, &new_action, NULL)) {
4502+
perror("sigaction");
4503+
exit(1);
4504+
}
4505+
4506+
ret = syscall(__NR_getppid);
4507+
/* Make sure we got a return from a signal interruption */
4508+
exit(ret != -1 || errno != EINTR);
4509+
}
4510+
4511+
/*
4512+
* Make sure we've gotten to the seccomp user notification wait
4513+
* from getppid prior to sending any signals
4514+
*/
4515+
while (get_proc_syscall(_metadata, pid) != __NR_getppid &&
4516+
get_proc_stat(_metadata, pid) != 'S')
4517+
nanosleep(&delay, NULL);
4518+
4519+
/* Send non-fatal kill signal */
4520+
EXPECT_EQ(kill(pid, SIGUSR1), 0);
4521+
4522+
/* wait for process to exit (exit checks for EINTR) */
4523+
EXPECT_EQ(waitpid(pid, &status, 0), pid);
4524+
EXPECT_EQ(true, WIFEXITED(status));
4525+
EXPECT_EQ(0, WEXITSTATUS(status));
4526+
4527+
EXPECT_EQ(read(sk_pair[0], &c, 1), 1);
4528+
}
4529+
4530+
/* Ensure non-fatal signals after receive are blocked */
4531+
TEST(user_notification_wait_killable)
4532+
{
4533+
struct sigaction new_action = {
4534+
.sa_handler = signal_handler,
4535+
};
4536+
struct seccomp_notif_resp resp = {};
4537+
struct seccomp_notif req = {};
4538+
int listener, status, sk_pair[2];
4539+
pid_t pid;
4540+
long ret;
4541+
char c;
4542+
/* 100 ms */
4543+
struct timespec delay = { .tv_nsec = 100000000 };
4544+
4545+
ASSERT_EQ(sigemptyset(&new_action.sa_mask), 0);
4546+
4547+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
4548+
ASSERT_EQ(0, ret)
4549+
{
4550+
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
4551+
}
4552+
4553+
ASSERT_EQ(socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair), 0);
4554+
4555+
listener = user_notif_syscall(
4556+
__NR_getppid, SECCOMP_FILTER_FLAG_NEW_LISTENER |
4557+
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV);
4558+
ASSERT_GE(listener, 0);
4559+
4560+
pid = fork();
4561+
ASSERT_GE(pid, 0);
4562+
4563+
if (pid == 0) {
4564+
close(sk_pair[0]);
4565+
handled = sk_pair[1];
4566+
4567+
/* Setup the sigaction without SA_RESTART */
4568+
if (sigaction(SIGUSR1, &new_action, NULL)) {
4569+
perror("sigaction");
4570+
exit(1);
4571+
}
4572+
4573+
/* Make sure that the syscall is completed (no EINTR) */
4574+
ret = syscall(__NR_getppid);
4575+
exit(ret != USER_NOTIF_MAGIC);
4576+
}
4577+
4578+
/*
4579+
* Get the notification, to make move the notifying process into a
4580+
* non-preemptible (TASK_KILLABLE) state.
4581+
*/
4582+
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0);
4583+
/* Send non-fatal kill signal */
4584+
EXPECT_EQ(kill(pid, SIGUSR1), 0);
4585+
4586+
/*
4587+
* Make sure the task enters moves to TASK_KILLABLE by waiting for
4588+
* D (Disk Sleep) state after receiving non-fatal signal.
4589+
*/
4590+
while (get_proc_stat(_metadata, pid) != 'D')
4591+
nanosleep(&delay, NULL);
4592+
4593+
resp.id = req.id;
4594+
resp.val = USER_NOTIF_MAGIC;
4595+
/* Make sure the notification is found and able to be replied to */
4596+
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0);
4597+
4598+
/*
4599+
* Make sure that the signal handler does get called once we're back in
4600+
* userspace.
4601+
*/
4602+
EXPECT_EQ(read(sk_pair[0], &c, 1), 1);
4603+
/* wait for process to exit (exit checks for USER_NOTIF_MAGIC) */
4604+
EXPECT_EQ(waitpid(pid, &status, 0), pid);
4605+
EXPECT_EQ(true, WIFEXITED(status));
4606+
EXPECT_EQ(0, WEXITSTATUS(status));
4607+
}
4608+
4609+
/* Ensure fatal signals after receive are not blocked */
4610+
TEST(user_notification_wait_killable_fatal)
4611+
{
4612+
struct seccomp_notif req = {};
4613+
int listener, status;
4614+
pid_t pid;
4615+
long ret;
4616+
/* 100 ms */
4617+
struct timespec delay = { .tv_nsec = 100000000 };
4618+
4619+
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
4620+
ASSERT_EQ(0, ret)
4621+
{
4622+
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
4623+
}
4624+
4625+
listener = user_notif_syscall(
4626+
__NR_getppid, SECCOMP_FILTER_FLAG_NEW_LISTENER |
4627+
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV);
4628+
ASSERT_GE(listener, 0);
4629+
4630+
pid = fork();
4631+
ASSERT_GE(pid, 0);
4632+
4633+
if (pid == 0) {
4634+
/* This should never complete as it should get a SIGTERM */
4635+
syscall(__NR_getppid);
4636+
exit(1);
4637+
}
4638+
4639+
while (get_proc_stat(_metadata, pid) != 'S')
4640+
nanosleep(&delay, NULL);
4641+
4642+
/*
4643+
* Get the notification, to make move the notifying process into a
4644+
* non-preemptible (TASK_KILLABLE) state.
4645+
*/
4646+
EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0);
4647+
/* Kill the process with a fatal signal */
4648+
EXPECT_EQ(kill(pid, SIGTERM), 0);
4649+
4650+
/*
4651+
* Wait for the process to exit, and make sure the process terminated
4652+
* due to the SIGTERM signal.
4653+
*/
4654+
EXPECT_EQ(waitpid(pid, &status, 0), pid);
4655+
EXPECT_EQ(true, WIFSIGNALED(status));
4656+
EXPECT_EQ(SIGTERM, WTERMSIG(status));
4657+
}
4658+
44314659
/*
44324660
* TODO:
44334661
* - expand NNP testing

0 commit comments

Comments
 (0)