Commit 0bf18a0c authored by Kees Cook's avatar Kees Cook Committed by Shuah Khan
Browse files

selftests/seccomp: Refactor to use fixture variants



Now that the selftest harness has variants, use them to eliminate a
bunch of copy/paste duplication.

Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Will Drewry <wad@chromium.org>
Cc: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Acked-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarShuah Khan <skhan@linuxfoundation.org>
parent 3e4cd8ea
Loading
Loading
Loading
Loading
+42 −157
Original line number Diff line number Diff line
@@ -1470,6 +1470,7 @@ pid_t setup_trace_fixture(struct __test_metadata *_metadata,

	return tracer_pid;
}

void teardown_trace_fixture(struct __test_metadata *_metadata,
			    pid_t tracer)
{
@@ -1750,7 +1751,7 @@ void change_syscall(struct __test_metadata *_metadata,
	EXPECT_EQ(0, ret);
}

void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee,
void tracer_seccomp(struct __test_metadata *_metadata, pid_t tracee,
		    int status, void *args)
{
	int ret;
@@ -1827,6 +1828,24 @@ FIXTURE(TRACE_syscall) {
	pid_t tracer, mytid, mypid, parent;
};

FIXTURE_VARIANT(TRACE_syscall) {
	/*
	 * All of the SECCOMP_RET_TRACE behaviors can be tested with either
	 * SECCOMP_RET_TRACE+PTRACE_CONT or plain ptrace()+PTRACE_SYSCALL.
	 * This indicates if we should use SECCOMP_RET_TRACE (false), or
	 * ptrace (true).
	 */
	bool use_ptrace;
};

FIXTURE_VARIANT_ADD(TRACE_syscall, ptrace) {
	.use_ptrace = true,
};

FIXTURE_VARIANT_ADD(TRACE_syscall, seccomp) {
	.use_ptrace = false,
};

FIXTURE_SETUP(TRACE_syscall)
{
	struct sock_filter filter[] = {
@@ -1842,12 +1861,11 @@ FIXTURE_SETUP(TRACE_syscall)
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1005),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
	};

	memset(&self->prog, 0, sizeof(self->prog));
	self->prog.filter = malloc(sizeof(filter));
	ASSERT_NE(NULL, self->prog.filter);
	memcpy(self->prog.filter, filter, sizeof(filter));
	self->prog.len = (unsigned short)ARRAY_SIZE(filter);
	struct sock_fprog prog = {
		.len = (unsigned short)ARRAY_SIZE(filter),
		.filter = filter,
	};
	long ret;

	/* Prepare some testable syscall results. */
	self->mytid = syscall(__NR_gettid);
@@ -1865,60 +1883,28 @@ FIXTURE_SETUP(TRACE_syscall)
	ASSERT_NE(self->parent, self->mypid);

	/* Launch tracer. */
	self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL,
					   false);
}

FIXTURE_TEARDOWN(TRACE_syscall)
{
	teardown_trace_fixture(_metadata, self->tracer);
	if (self->prog.filter)
		free(self->prog.filter);
}
	self->tracer = setup_trace_fixture(_metadata,
					   variant->use_ptrace ? tracer_ptrace
							       : tracer_seccomp,
					   NULL, variant->use_ptrace);

TEST_F(TRACE_syscall, ptrace_syscall_redirected)
{
	/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
	teardown_trace_fixture(_metadata, self->tracer);
	self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
					   true);

	/* Tracer will redirect getpid to getppid. */
	EXPECT_NE(self->mypid, syscall(__NR_getpid));
}
	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

TEST_F(TRACE_syscall, ptrace_syscall_errno)
{
	/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
	teardown_trace_fixture(_metadata, self->tracer);
	self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
					   true);
	if (variant->use_ptrace)
		return;

	/* Tracer should skip the open syscall, resulting in ESRCH. */
	EXPECT_SYSCALL_RETURN(-ESRCH, syscall(__NR_openat));
	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
	ASSERT_EQ(0, ret);
}

TEST_F(TRACE_syscall, ptrace_syscall_faked)
FIXTURE_TEARDOWN(TRACE_syscall)
{
	/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
	teardown_trace_fixture(_metadata, self->tracer);
	self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
					   true);

	/* Tracer should skip the gettid syscall, resulting fake pid. */
	EXPECT_SYSCALL_RETURN(45000, syscall(__NR_gettid));
}

TEST_F(TRACE_syscall, syscall_allowed)
{
	long ret;

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* getppid works as expected (no changes). */
	EXPECT_EQ(self->parent, syscall(__NR_getppid));
	EXPECT_NE(self->mypid, syscall(__NR_getppid));
@@ -1926,14 +1912,6 @@ TEST_F(TRACE_syscall, syscall_allowed)

TEST_F(TRACE_syscall, syscall_redirected)
{
	long ret;

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* getpid has been redirected to getppid as expected. */
	EXPECT_EQ(self->parent, syscall(__NR_getpid));
	EXPECT_NE(self->mypid, syscall(__NR_getpid));
@@ -1941,33 +1919,17 @@ TEST_F(TRACE_syscall, syscall_redirected)

TEST_F(TRACE_syscall, syscall_errno)
{
	long ret;

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* openat has been skipped and an errno return. */
	/* Tracer should skip the open syscall, resulting in ESRCH. */
	EXPECT_SYSCALL_RETURN(-ESRCH, syscall(__NR_openat));
}

TEST_F(TRACE_syscall, syscall_faked)
{
	long ret;

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* gettid has been skipped and an altered return value stored. */
	/* Tracer skips the gettid syscall and store altered return value. */
	EXPECT_SYSCALL_RETURN(45000, syscall(__NR_gettid));
}

TEST_F(TRACE_syscall, skip_after_RET_TRACE)
TEST_F(TRACE_syscall, skip_after)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
@@ -1982,14 +1944,7 @@ TEST_F(TRACE_syscall, skip_after_RET_TRACE)
	};
	long ret;

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	/* Install fixture filter. */
	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* Install "errno on getppid" filter. */
	/* Install additional "errno on getppid" filter. */
	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
	ASSERT_EQ(0, ret);

@@ -1999,7 +1954,7 @@ TEST_F(TRACE_syscall, skip_after_RET_TRACE)
	EXPECT_EQ(EPERM, errno);
}

TEST_F_SIGNAL(TRACE_syscall, kill_after_RET_TRACE, SIGSYS)
TEST_F_SIGNAL(TRACE_syscall, kill_after, SIGSYS)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
@@ -2014,77 +1969,7 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_RET_TRACE, SIGSYS)
	};
	long ret;

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	/* Install fixture filter. */
	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* Install "death on getppid" filter. */
	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* Tracer will redirect getpid to getppid, and we should die. */
	EXPECT_NE(self->mypid, syscall(__NR_getpid));
}

TEST_F(TRACE_syscall, skip_after_ptrace)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
			offsetof(struct seccomp_data, nr)),
		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
	};
	struct sock_fprog prog = {
		.len = (unsigned short)ARRAY_SIZE(filter),
		.filter = filter,
	};
	long ret;

	/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
	teardown_trace_fixture(_metadata, self->tracer);
	self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
					   true);

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	/* Install "errno on getppid" filter. */
	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
	ASSERT_EQ(0, ret);

	/* Tracer will redirect getpid to getppid, and we should see EPERM. */
	EXPECT_EQ(-1, syscall(__NR_getpid));
	EXPECT_EQ(EPERM, errno);
}

TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
			offsetof(struct seccomp_data, nr)),
		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
	};
	struct sock_fprog prog = {
		.len = (unsigned short)ARRAY_SIZE(filter),
		.filter = filter,
	};
	long ret;

	/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
	teardown_trace_fixture(_metadata, self->tracer);
	self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
					   true);

	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
	ASSERT_EQ(0, ret);

	/* Install "death on getppid" filter. */
	/* Install additional "death on getppid" filter. */
	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
	ASSERT_EQ(0, ret);