|
60 | 60 | #define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__) |
61 | 61 | #endif |
62 | 62 |
|
| 63 | +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) |
| 64 | + |
63 | 65 | #ifndef PR_SET_PTRACER |
64 | 66 | # define PR_SET_PTRACER 0x59616d61 |
65 | 67 | #endif |
@@ -269,6 +271,10 @@ struct seccomp_notif_addfd_big { |
269 | 271 | #define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4) |
270 | 272 | #endif |
271 | 273 |
|
| 274 | +#ifndef SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV |
| 275 | +#define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5) |
| 276 | +#endif |
| 277 | + |
272 | 278 | #ifndef seccomp |
273 | 279 | int seccomp(unsigned int op, unsigned int flags, void *args) |
274 | 280 | { |
@@ -4428,6 +4434,228 @@ TEST(user_notification_fifo) |
4428 | 4434 | } |
4429 | 4435 | } |
4430 | 4436 |
|
| 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 | + |
4431 | 4659 | /* |
4432 | 4660 | * TODO: |
4433 | 4661 | * - expand NNP testing |
|
0 commit comments