Commit ad568218 authored by Christian Brauner's avatar Christian Brauner Committed by Kees Cook
Browse files

selftests/seccomp: Check for EPOLLHUP for user_notif



This verifies we're correctly notified when a seccomp filter becomes
unused when a notifier is in use.

Signed-off-by: default avatarChristian Brauner <christian.brauner@ubuntu.com>
Link: https://lore.kernel.org/r/20200531115031.391515-4-christian.brauner@ubuntu.com


Signed-off-by: default avatarKees Cook <keescook@chromium.org>
parent 99cdb8b9
Loading
Loading
Loading
Loading
+136 −0
Original line number Diff line number Diff line
@@ -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