Skip to content

Commit

Permalink
selftests/seccomp: Check for EPOLLHUP for user_notif
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Christian Brauner authored and Kees Cook committed Jul 10, 2020
1 parent 99cdb8b commit ad56821
Showing 1 changed file with 136 additions and 0 deletions.
136 changes: 136 additions & 0 deletions tools/testing/selftests/seccomp/seccomp_bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -3702,6 +3703,141 @@ TEST(user_notification_continue)
}
}

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
Expand Down

0 comments on commit ad56821

Please sign in to comment.