[PATCH 3/3] selftests/seccomp: Test NOTIF_RECV empty/dead errors

From: Jann Horn
Date: Mon Nov 02 2020 - 15:42:26 EST


Test that SECCOMP_IOCTL_NOTIF_RECV on a seccomp fd with zero users returns
-ENOTCONN, both in blocking and in non-blocking mode.
Also test that SECCOMP_IOCTL_NOTIF_RECV on a seccomp fd with no active
notifications returns -ENOENT in non-blocking mode.

Signed-off-by: Jann Horn <jannh@xxxxxxxxxx>
---
tools/testing/selftests/seccomp/seccomp_bpf.c | 127 ++++++++++++++++++
1 file changed, 127 insertions(+)

diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 5318f9cb1aec..8214c431ad4b 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -28,6 +28,7 @@
#include <linux/prctl.h>
#include <linux/ptrace.h>
#include <linux/seccomp.h>
+#include <linux/futex.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
@@ -315,6 +316,35 @@ static int __filecmp(pid_t pid1, pid_t pid2, int fd1, int fd2)
} \
_ret; })

+static void store_and_wake(int *ptr, int value)
+{
+ __atomic_store(ptr, &value, __ATOMIC_RELEASE);
+ if (syscall(__NR_futex, ptr, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) == -1) {
+ perror("FUTEX_WAKE failed unexpectedly");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int wait_and_load(int *ptr, int placeholder)
+{
+ int futex_ret;
+ int value;
+
+retry:
+ futex_ret = syscall(__NR_futex, ptr, FUTEX_WAIT, placeholder, NULL,
+ NULL, 0);
+ if (futex_ret == -1 && errno != EAGAIN) {
+ if (errno == EINTR)
+ goto retry;
+ perror("FUTEX_WAIT failed unexpectedly");
+ exit(EXIT_FAILURE);
+ }
+ value = __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
+ if (value == placeholder)
+ goto retry;
+ return value;
+}
+
TEST(kcmp)
{
int ret;
@@ -4160,6 +4190,103 @@ TEST(user_notification_addfd_rlimit)
close(memfd);
}

+TEST(user_notification_recv_dead)
+{
+ pid_t pid;
+ int status;
+ struct __clone_args args = {
+ .flags = CLONE_FILES,
+ .exit_signal = SIGCHLD,
+ };
+ struct seccomp_notif notif = {};
+ struct shared_data {
+ int notif_fd;
+ } *shared_data;
+
+ shared_data = mmap(NULL, sizeof(struct shared_data),
+ PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED,
+ -1, 0);
+ ASSERT_NE(NULL, shared_data);
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ pid = sys_clone3(&args, sizeof(args));
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ shared_data->notif_fd = user_notif_syscall(
+ __NR_mknodat, SECCOMP_FILTER_FLAG_NEW_LISTENER);
+ if (shared_data->notif_fd < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+
+ /* non-blocking recv */
+ EXPECT_EQ(-1, ioctl(shared_data->notif_fd, SECCOMP_IOCTL_NOTIF_RECV, &notif));
+ EXPECT_EQ(ENOTCONN, errno);
+
+ /* blocking recv */
+ set_blocking(shared_data->notif_fd);
+ EXPECT_EQ(-1, ioctl(shared_data->notif_fd, SECCOMP_IOCTL_NOTIF_RECV, &notif));
+ EXPECT_EQ(ENOTCONN, errno);
+}
+
+TEST(user_notification_recv_nonblock)
+{
+ pid_t pid;
+ struct __clone_args args = {
+ .flags = CLONE_FILES,
+ .exit_signal = SIGCHLD,
+ };
+ struct seccomp_notif notif = {};
+ struct shared_data {
+ int notif_fd;
+ } *shared_data;
+
+ shared_data = mmap(NULL, sizeof(struct shared_data),
+ PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED,
+ -1, 0);
+ ASSERT_NE(NULL, shared_data);
+ shared_data->notif_fd = -1;
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ pid = sys_clone3(&args, sizeof(args));
+ ASSERT_GE(pid, 0);
+
+ if (pid == 0) {
+ int fd;
+
+ fd = user_notif_syscall(__NR_mknodat,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER);
+ if (fd < 0) {
+ store_and_wake(&shared_data->notif_fd, -2);
+ _exit(EXIT_FAILURE);
+ }
+ store_and_wake(&shared_data->notif_fd, fd);
+
+ while (1)
+ pause();
+ }
+
+ wait_and_load(&shared_data->notif_fd, -1);
+
+ /* non-blocking recv */
+ EXPECT_EQ(-1, ioctl(shared_data->notif_fd, SECCOMP_IOCTL_NOTIF_RECV,
+ &notif));
+ EXPECT_EQ(ENOENT, errno);
+
+ kill(pid, SIGKILL);
+}
+
/*
* TODO:
* - expand NNP testing
--
2.29.1.341.ge80a0c044ae-goog