OSDN Git Service

selftests/seccomp: Check for EPOLLHUP for user_notif
authorChristian Brauner <christian.brauner@ubuntu.com>
Sun, 31 May 2020 11:50:31 +0000 (13:50 +0200)
committerKees Cook <keescook@chromium.org>
Fri, 10 Jul 2020 23:01:51 +0000 (16:01 -0700)
This verifies we're correctly notified when a seccomp filter becomes
unused when a notifier is in use.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Link: https://lore.kernel.org/r/20200531115031.391515-4-christian.brauner@ubuntu.com
Signed-off-by: Kees Cook <keescook@chromium.org>
tools/testing/selftests/seccomp/seccomp_bpf.c

index c8f93ed..fc899cf 100644 (file)
@@ -51,6 +51,7 @@
 #include <poll.h>
 
 #include "../kselftest_harness.h"
+#include "../clone3/clone3_selftests.h"
 
 /* Attempt to de-conflict with the selftests tree. */
 #ifndef SKIP
@@ -3702,6 +3703,141 @@ skip:
        }
 }
 
+TEST(user_notification_filter_empty)
+{
+       pid_t pid;
+       long ret;
+       int status;
+       struct pollfd pollfd;
+       struct clone_args args = {
+               .flags = CLONE_FILES,
+               .exit_signal = SIGCHLD,
+       };
+
+       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+       ASSERT_EQ(0, ret) {
+               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 listener;
+
+               listener = user_trap_syscall(__NR_mknod, SECCOMP_FILTER_FLAG_NEW_LISTENER);
+               if (listener < 0)
+                       _exit(EXIT_FAILURE);
+
+               if (dup2(listener, 200) != 200)
+                       _exit(EXIT_FAILURE);
+
+               close(listener);
+
+               _exit(EXIT_SUCCESS);
+       }
+
+       EXPECT_EQ(waitpid(pid, &status, 0), pid);
+       EXPECT_EQ(true, WIFEXITED(status));
+       EXPECT_EQ(0, WEXITSTATUS(status));
+
+       /*
+        * The seccomp filter has become unused so we should be notified once
+        * the kernel gets around to cleaning up task struct.
+        */
+       pollfd.fd = 200;
+       pollfd.events = POLLHUP;
+
+       EXPECT_GT(poll(&pollfd, 1, 2000), 0);
+       EXPECT_GT((pollfd.revents & POLLHUP) ?: 0, 0);
+}
+
+static void *do_thread(void *data)
+{
+       return NULL;
+}
+
+TEST(user_notification_filter_empty_threaded)
+{
+       pid_t pid;
+       long ret;
+       int status;
+       struct pollfd pollfd;
+       struct clone_args args = {
+               .flags = CLONE_FILES,
+               .exit_signal = SIGCHLD,
+       };
+
+       ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+       ASSERT_EQ(0, ret) {
+               TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+       }
+
+       pid = sys_clone3(&args, sizeof(args));
+       ASSERT_GE(pid, 0);
+
+       if (pid == 0) {
+               pid_t pid1, pid2;
+               int listener, status;
+               pthread_t thread;
+
+               listener = user_trap_syscall(__NR_dup, SECCOMP_FILTER_FLAG_NEW_LISTENER);
+               if (listener < 0)
+                       _exit(EXIT_FAILURE);
+
+               if (dup2(listener, 200) != 200)
+                       _exit(EXIT_FAILURE);
+
+               close(listener);
+
+               pid1 = fork();
+               if (pid1 < 0)
+                       _exit(EXIT_FAILURE);
+
+               if (pid1 == 0)
+                       _exit(EXIT_SUCCESS);
+
+               pid2 = fork();
+               if (pid2 < 0)
+                       _exit(EXIT_FAILURE);
+
+               if (pid2 == 0)
+                       _exit(EXIT_SUCCESS);
+
+               if (pthread_create(&thread, NULL, do_thread, NULL) ||
+                   pthread_join(thread, NULL))
+                       _exit(EXIT_FAILURE);
+
+               if (pthread_create(&thread, NULL, do_thread, NULL) ||
+                   pthread_join(thread, NULL))
+                       _exit(EXIT_FAILURE);
+
+               if (waitpid(pid1, &status, 0) != pid1 || !WIFEXITED(status) ||
+                   WEXITSTATUS(status))
+                       _exit(EXIT_FAILURE);
+
+               if (waitpid(pid2, &status, 0) != pid2 || !WIFEXITED(status) ||
+                   WEXITSTATUS(status))
+                       _exit(EXIT_FAILURE);
+
+               exit(EXIT_SUCCESS);
+       }
+
+       EXPECT_EQ(waitpid(pid, &status, 0), pid);
+       EXPECT_EQ(true, WIFEXITED(status));
+       EXPECT_EQ(0, WEXITSTATUS(status));
+
+       /*
+        * The seccomp filter has become unused so we should be notified once
+        * the kernel gets around to cleaning up task struct.
+        */
+       pollfd.fd = 200;
+       pollfd.events = POLLHUP;
+
+       EXPECT_GT(poll(&pollfd, 1, 2000), 0);
+       EXPECT_GT((pollfd.revents & POLLHUP) ?: 0, 0);
+}
+
 /*
  * TODO:
  * - add microbenchmarks