diff options
Diffstat (limited to 'tools/testing/selftests/seccomp/seccomp_bpf.c')
| -rw-r--r-- | tools/testing/selftests/seccomp/seccomp_bpf.c | 628 | 
1 files changed, 527 insertions, 101 deletions
| diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 4d6f92a9df6b..24dbf634e2dd 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -6,10 +6,18 @@   */  #include <sys/types.h> -#include <asm/siginfo.h> -#define __have_siginfo_t 1 -#define __have_sigval_t 1 -#define __have_sigevent_t 1 + +/* + * glibc 2.26 and later have SIGSYS in siginfo_t. Before that, + * we need to use the kernel's siginfo.h file and trick glibc + * into accepting it. + */ +#if !__GLIBC_PREREQ(2, 26) +# include <asm/siginfo.h> +# define __have_siginfo_t 1 +# define __have_sigval_t 1 +# define __have_sigevent_t 1 +#endif  #include <errno.h>  #include <linux/filter.h> @@ -68,17 +76,7 @@  #define SECCOMP_MODE_FILTER 2  #endif -#ifndef SECCOMP_RET_KILL -#define SECCOMP_RET_KILL        0x00000000U /* kill the task immediately */ -#define SECCOMP_RET_TRAP        0x00030000U /* disallow and force a SIGSYS */ -#define SECCOMP_RET_ERRNO       0x00050000U /* returns an errno */ -#define SECCOMP_RET_TRACE       0x7ff00000U /* pass to a tracer or disallow */ -#define SECCOMP_RET_ALLOW       0x7fff0000U /* allow */ - -/* Masks for the return value sections. */ -#define SECCOMP_RET_ACTION      0x7fff0000U -#define SECCOMP_RET_DATA        0x0000ffffU - +#ifndef SECCOMP_RET_ALLOW  struct seccomp_data {  	int nr;  	__u32 arch; @@ -87,6 +85,70 @@ struct seccomp_data {  };  #endif +#ifndef SECCOMP_RET_KILL_PROCESS +#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */ +#define SECCOMP_RET_KILL_THREAD	 0x00000000U /* kill the thread */ +#endif +#ifndef SECCOMP_RET_KILL +#define SECCOMP_RET_KILL	 SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_TRAP	 0x00030000U /* disallow and force a SIGSYS */ +#define SECCOMP_RET_ERRNO	 0x00050000U /* returns an errno */ +#define SECCOMP_RET_TRACE	 0x7ff00000U /* pass to a tracer or disallow */ +#define SECCOMP_RET_ALLOW	 0x7fff0000U /* allow */ +#endif +#ifndef SECCOMP_RET_LOG +#define SECCOMP_RET_LOG		 0x7ffc0000U /* allow after logging */ +#endif + +#ifndef __NR_seccomp +# if defined(__i386__) +#  define __NR_seccomp 354 +# elif defined(__x86_64__) +#  define __NR_seccomp 317 +# elif defined(__arm__) +#  define __NR_seccomp 383 +# elif defined(__aarch64__) +#  define __NR_seccomp 277 +# elif defined(__hppa__) +#  define __NR_seccomp 338 +# elif defined(__powerpc__) +#  define __NR_seccomp 358 +# elif defined(__s390__) +#  define __NR_seccomp 348 +# else +#  warning "seccomp syscall number unknown for this architecture" +#  define __NR_seccomp 0xffff +# endif +#endif + +#ifndef SECCOMP_SET_MODE_STRICT +#define SECCOMP_SET_MODE_STRICT 0 +#endif + +#ifndef SECCOMP_SET_MODE_FILTER +#define SECCOMP_SET_MODE_FILTER 1 +#endif + +#ifndef SECCOMP_GET_ACTION_AVAIL +#define SECCOMP_GET_ACTION_AVAIL 2 +#endif + +#ifndef SECCOMP_FILTER_FLAG_TSYNC +#define SECCOMP_FILTER_FLAG_TSYNC 1 +#endif + +#ifndef SECCOMP_FILTER_FLAG_LOG +#define SECCOMP_FILTER_FLAG_LOG 2 +#endif + +#ifndef seccomp +int seccomp(unsigned int op, unsigned int flags, void *args) +{ +	errno = 0; +	return syscall(__NR_seccomp, op, flags, args); +} +#endif +  #if __BYTE_ORDER == __LITTLE_ENDIAN  #define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))  #elif __BYTE_ORDER == __BIG_ENDIAN @@ -136,7 +198,7 @@ TEST(no_new_privs_support)  	}  } -/* Tests kernel support by checking for a copy_from_user() fault on * NULL. */ +/* Tests kernel support by checking for a copy_from_user() fault on NULL. */  TEST(mode_filter_support)  {  	long ret; @@ -342,6 +404,28 @@ TEST(empty_prog)  	EXPECT_EQ(EINVAL, errno);  } +TEST(log_all) +{ +	struct sock_filter filter[] = { +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG), +	}; +	struct sock_fprog prog = { +		.len = (unsigned short)ARRAY_SIZE(filter), +		.filter = filter, +	}; +	long ret; +	pid_t parent = getppid(); + +	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); +	ASSERT_EQ(0, ret); + +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); +	ASSERT_EQ(0, ret); + +	/* getppid() should succeed and be logged (no check for logging) */ +	EXPECT_EQ(parent, syscall(__NR_getppid)); +} +  TEST_SIGNAL(unknown_ret_is_kill_inside, SIGSYS)  {  	struct sock_filter filter[] = { @@ -520,6 +604,117 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS)  	close(fd);  } +/* This is a thread task to die via seccomp filter violation. */ +void *kill_thread(void *data) +{ +	bool die = (bool)data; + +	if (die) { +		prctl(PR_GET_SECCOMP, 0, 0, 0, 0); +		return (void *)SIBLING_EXIT_FAILURE; +	} + +	return (void *)SIBLING_EXIT_UNKILLED; +} + +/* Prepare a thread that will kill itself or both of us. */ +void kill_thread_or_group(struct __test_metadata *_metadata, bool kill_process) +{ +	pthread_t thread; +	void *status; +	/* Kill only when calling __NR_prctl. */ +	struct sock_filter filter_thread[] = { +		BPF_STMT(BPF_LD|BPF_W|BPF_ABS, +			offsetof(struct seccomp_data, nr)), +		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_THREAD), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), +	}; +	struct sock_fprog prog_thread = { +		.len = (unsigned short)ARRAY_SIZE(filter_thread), +		.filter = filter_thread, +	}; +	struct sock_filter filter_process[] = { +		BPF_STMT(BPF_LD|BPF_W|BPF_ABS, +			offsetof(struct seccomp_data, nr)), +		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_PROCESS), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), +	}; +	struct sock_fprog prog_process = { +		.len = (unsigned short)ARRAY_SIZE(filter_process), +		.filter = filter_process, +	}; + +	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { +		TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); +	} + +	ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, +			     kill_process ? &prog_process : &prog_thread)); + +	/* +	 * Add the KILL_THREAD rule again to make sure that the KILL_PROCESS +	 * flag cannot be downgraded by a new filter. +	 */ +	ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog_thread)); + +	/* Start a thread that will exit immediately. */ +	ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)false)); +	ASSERT_EQ(0, pthread_join(thread, &status)); +	ASSERT_EQ(SIBLING_EXIT_UNKILLED, (unsigned long)status); + +	/* Start a thread that will die immediately. */ +	ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)true)); +	ASSERT_EQ(0, pthread_join(thread, &status)); +	ASSERT_NE(SIBLING_EXIT_FAILURE, (unsigned long)status); + +	/* +	 * If we get here, only the spawned thread died. Let the parent know +	 * the whole process didn't die (i.e. this thread, the spawner, +	 * stayed running). +	 */ +	exit(42); +} + +TEST(KILL_thread) +{ +	int status; +	pid_t child_pid; + +	child_pid = fork(); +	ASSERT_LE(0, child_pid); +	if (child_pid == 0) { +		kill_thread_or_group(_metadata, false); +		_exit(38); +	} + +	ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); + +	/* If only the thread was killed, we'll see exit 42. */ +	ASSERT_TRUE(WIFEXITED(status)); +	ASSERT_EQ(42, WEXITSTATUS(status)); +} + +TEST(KILL_process) +{ +	int status; +	pid_t child_pid; + +	child_pid = fork(); +	ASSERT_LE(0, child_pid); +	if (child_pid == 0) { +		kill_thread_or_group(_metadata, true); +		_exit(38); +	} + +	ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); + +	/* If the entire process was killed, we'll see SIGSYS. */ +	ASSERT_TRUE(WIFSIGNALED(status)); +	ASSERT_EQ(SIGSYS, WTERMSIG(status)); +} +  /* TODO(wad) add 64-bit versus 32-bit arg tests. */  TEST(arg_out_of_range)  { @@ -541,26 +736,30 @@ TEST(arg_out_of_range)  	EXPECT_EQ(EINVAL, errno);  } +#define ERRNO_FILTER(name, errno)					\ +	struct sock_filter _read_filter_##name[] = {			\ +		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,				\ +			offsetof(struct seccomp_data, nr)),		\ +		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),	\ +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | errno),	\ +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),		\ +	};								\ +	struct sock_fprog prog_##name = {				\ +		.len = (unsigned short)ARRAY_SIZE(_read_filter_##name),	\ +		.filter = _read_filter_##name,				\ +	} + +/* Make sure basic errno values are correctly passed through a filter. */  TEST(ERRNO_valid)  { -	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_read, 0, 1), -		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | E2BIG), -		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), -	}; -	struct sock_fprog prog = { -		.len = (unsigned short)ARRAY_SIZE(filter), -		.filter = filter, -	}; +	ERRNO_FILTER(valid, E2BIG);  	long ret;  	pid_t parent = getppid();  	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);  	ASSERT_EQ(0, ret); -	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_valid);  	ASSERT_EQ(0, ret);  	EXPECT_EQ(parent, syscall(__NR_getppid)); @@ -568,26 +767,17 @@ TEST(ERRNO_valid)  	EXPECT_EQ(E2BIG, errno);  } +/* Make sure an errno of zero is correctly handled by the arch code. */  TEST(ERRNO_zero)  { -	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_read, 0, 1), -		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 0), -		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), -	}; -	struct sock_fprog prog = { -		.len = (unsigned short)ARRAY_SIZE(filter), -		.filter = filter, -	}; +	ERRNO_FILTER(zero, 0);  	long ret;  	pid_t parent = getppid();  	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);  	ASSERT_EQ(0, ret); -	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_zero);  	ASSERT_EQ(0, ret);  	EXPECT_EQ(parent, syscall(__NR_getppid)); @@ -595,26 +785,21 @@ TEST(ERRNO_zero)  	EXPECT_EQ(0, read(0, NULL, 0));  } +/* + * The SECCOMP_RET_DATA mask is 16 bits wide, but errno is smaller. + * This tests that the errno value gets capped correctly, fixed by + * 580c57f10768 ("seccomp: cap SECCOMP_RET_ERRNO data to MAX_ERRNO"). + */  TEST(ERRNO_capped)  { -	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_read, 0, 1), -		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 4096), -		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), -	}; -	struct sock_fprog prog = { -		.len = (unsigned short)ARRAY_SIZE(filter), -		.filter = filter, -	}; +	ERRNO_FILTER(capped, 4096);  	long ret;  	pid_t parent = getppid();  	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);  	ASSERT_EQ(0, ret); -	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_capped);  	ASSERT_EQ(0, ret);  	EXPECT_EQ(parent, syscall(__NR_getppid)); @@ -622,6 +807,37 @@ TEST(ERRNO_capped)  	EXPECT_EQ(4095, errno);  } +/* + * Filters are processed in reverse order: last applied is executed first. + * Since only the SECCOMP_RET_ACTION mask is tested for return values, the + * SECCOMP_RET_DATA mask results will follow the most recently applied + * matching filter return (and not the lowest or highest value). + */ +TEST(ERRNO_order) +{ +	ERRNO_FILTER(first,  11); +	ERRNO_FILTER(second, 13); +	ERRNO_FILTER(third,  12); +	long ret; +	pid_t parent = getppid(); + +	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); +	ASSERT_EQ(0, ret); + +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_first); +	ASSERT_EQ(0, ret); + +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_second); +	ASSERT_EQ(0, ret); + +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_third); +	ASSERT_EQ(0, ret); + +	EXPECT_EQ(parent, syscall(__NR_getppid)); +	EXPECT_EQ(-1, read(0, NULL, 0)); +	EXPECT_EQ(12, errno); +} +  FIXTURE_DATA(TRAP) {  	struct sock_fprog prog;  }; @@ -676,7 +892,7 @@ TEST_F_SIGNAL(TRAP, ign, SIGSYS)  	syscall(__NR_getpid);  } -static struct siginfo TRAP_info; +static siginfo_t TRAP_info;  static volatile int TRAP_nr;  static void TRAP_action(int nr, siginfo_t *info, void *void_context)  { @@ -735,6 +951,7 @@ TEST_F(TRAP, handler)  FIXTURE_DATA(precedence) {  	struct sock_fprog allow; +	struct sock_fprog log;  	struct sock_fprog trace;  	struct sock_fprog error;  	struct sock_fprog trap; @@ -746,6 +963,13 @@ FIXTURE_SETUP(precedence)  	struct sock_filter allow_insns[] = {  		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),  	}; +	struct sock_filter log_insns[] = { +		BPF_STMT(BPF_LD|BPF_W|BPF_ABS, +			offsetof(struct seccomp_data, nr)), +		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG), +	};  	struct sock_filter trace_insns[] = {  		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,  			offsetof(struct seccomp_data, nr)), @@ -782,6 +1006,7 @@ FIXTURE_SETUP(precedence)  	memcpy(self->_x.filter, &_x##_insns, sizeof(_x##_insns)); \  	self->_x.len = (unsigned short)ARRAY_SIZE(_x##_insns)  	FILTER_ALLOC(allow); +	FILTER_ALLOC(log);  	FILTER_ALLOC(trace);  	FILTER_ALLOC(error);  	FILTER_ALLOC(trap); @@ -792,6 +1017,7 @@ FIXTURE_TEARDOWN(precedence)  {  #define FILTER_FREE(_x) if (self->_x.filter) free(self->_x.filter)  	FILTER_FREE(allow); +	FILTER_FREE(log);  	FILTER_FREE(trace);  	FILTER_FREE(error);  	FILTER_FREE(trap); @@ -809,6 +1035,8 @@ TEST_F(precedence, allow_ok)  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -833,6 +1061,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest, SIGSYS)  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -864,6 +1094,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest_in_any_order, SIGSYS)  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap); @@ -885,6 +1117,8 @@ TEST_F_SIGNAL(precedence, trap_is_second, SIGSYS)  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -910,6 +1144,8 @@ TEST_F_SIGNAL(precedence, trap_is_second_in_any_order, SIGSYS)  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -931,6 +1167,8 @@ TEST_F(precedence, errno_is_third)  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -949,6 +1187,8 @@ TEST_F(precedence, errno_is_third_in_any_order)  	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); @@ -971,6 +1211,8 @@ TEST_F(precedence, trace_is_fourth)  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);  	ASSERT_EQ(0, ret);  	/* Should work just fine. */ @@ -992,12 +1234,54 @@ TEST_F(precedence, trace_is_fourth_in_any_order)  	ASSERT_EQ(0, ret);  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);  	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret);  	/* Should work just fine. */  	EXPECT_EQ(parent, syscall(__NR_getppid));  	/* No ptracer */  	EXPECT_EQ(-1, syscall(__NR_getpid));  } +TEST_F(precedence, log_is_fifth) +{ +	pid_t mypid, parent; +	long ret; + +	mypid = getpid(); +	parent = getppid(); +	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); +	ASSERT_EQ(0, ret); + +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); +	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret); +	/* Should work just fine. */ +	EXPECT_EQ(parent, syscall(__NR_getppid)); +	/* Should also work just fine */ +	EXPECT_EQ(mypid, syscall(__NR_getpid)); +} + +TEST_F(precedence, log_is_fifth_in_any_order) +{ +	pid_t mypid, parent; +	long ret; + +	mypid = getpid(); +	parent = getppid(); +	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); +	ASSERT_EQ(0, ret); + +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); +	ASSERT_EQ(0, ret); +	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); +	ASSERT_EQ(0, ret); +	/* Should work just fine. */ +	EXPECT_EQ(parent, syscall(__NR_getppid)); +	/* Should also work just fine */ +	EXPECT_EQ(mypid, syscall(__NR_getpid)); +} +  #ifndef PTRACE_O_TRACESECCOMP  #define PTRACE_O_TRACESECCOMP	0x00000080  #endif @@ -1262,6 +1546,13 @@ TEST_F(TRACE_poke, getpid_runs_normally)  # error "Do not know how to find your architecture's registers and syscalls"  #endif +/* When the syscall return can't be changed, stub out the tests for it. */ +#ifdef SYSCALL_NUM_RET_SHARE_REG +# define EXPECT_SYSCALL_RETURN(val, action)	EXPECT_EQ(-1, action) +#else +# define EXPECT_SYSCALL_RETURN(val, action)	EXPECT_EQ(val, action) +#endif +  /* Use PTRACE_GETREGS and PTRACE_SETREGS when available. This is useful for   * architectures without HAVE_ARCH_TRACEHOOK (e.g. User-mode Linux).   */ @@ -1357,7 +1648,7 @@ void change_syscall(struct __test_metadata *_metadata,  #ifdef SYSCALL_NUM_RET_SHARE_REG  		TH_LOG("Can't modify syscall return on this architecture");  #else -		regs.SYSCALL_RET = 1; +		regs.SYSCALL_RET = EPERM;  #endif  #ifdef HAVE_GETREGS @@ -1426,6 +1717,8 @@ void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,  	if (nr == __NR_getpid)  		change_syscall(_metadata, tracee, __NR_getppid); +	if (nr == __NR_open) +		change_syscall(_metadata, tracee, -1);  }  FIXTURE_DATA(TRACE_syscall) { @@ -1480,6 +1773,28 @@ FIXTURE_TEARDOWN(TRACE_syscall)  		free(self->prog.filter);  } +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)); +} + +TEST_F(TRACE_syscall, ptrace_syscall_dropped) +{ +	/* 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 open syscall, resulting in EPERM. */ +	EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_open)); +} +  TEST_F(TRACE_syscall, syscall_allowed)  {  	long ret; @@ -1520,13 +1835,8 @@ TEST_F(TRACE_syscall, syscall_dropped)  	ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);  	ASSERT_EQ(0, ret); -#ifdef SYSCALL_NUM_RET_SHARE_REG -	/* gettid has been skipped */ -	EXPECT_EQ(-1, syscall(__NR_gettid)); -#else  	/* gettid has been skipped and an altered return value stored. */ -	EXPECT_EQ(1, syscall(__NR_gettid)); -#endif +	EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_gettid));  	EXPECT_NE(self->mytid, syscall(__NR_gettid));  } @@ -1557,6 +1867,7 @@ TEST_F(TRACE_syscall, skip_after_RET_TRACE)  	ASSERT_EQ(0, ret);  	/* Tracer will redirect getpid to getppid, and we should see EPERM. */ +	errno = 0;  	EXPECT_EQ(-1, syscall(__NR_getpid));  	EXPECT_EQ(EPERM, errno);  } @@ -1654,47 +1965,6 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)  	EXPECT_NE(self->mypid, syscall(__NR_getpid));  } -#ifndef __NR_seccomp -# if defined(__i386__) -#  define __NR_seccomp 354 -# elif defined(__x86_64__) -#  define __NR_seccomp 317 -# elif defined(__arm__) -#  define __NR_seccomp 383 -# elif defined(__aarch64__) -#  define __NR_seccomp 277 -# elif defined(__hppa__) -#  define __NR_seccomp 338 -# elif defined(__powerpc__) -#  define __NR_seccomp 358 -# elif defined(__s390__) -#  define __NR_seccomp 348 -# else -#  warning "seccomp syscall number unknown for this architecture" -#  define __NR_seccomp 0xffff -# endif -#endif - -#ifndef SECCOMP_SET_MODE_STRICT -#define SECCOMP_SET_MODE_STRICT 0 -#endif - -#ifndef SECCOMP_SET_MODE_FILTER -#define SECCOMP_SET_MODE_FILTER 1 -#endif - -#ifndef SECCOMP_FILTER_FLAG_TSYNC -#define SECCOMP_FILTER_FLAG_TSYNC 1 -#endif - -#ifndef seccomp -int seccomp(unsigned int op, unsigned int flags, void *args) -{ -	errno = 0; -	return syscall(__NR_seccomp, op, flags, args); -} -#endif -  TEST(seccomp_syscall)  {  	struct sock_filter filter[] = { @@ -1783,6 +2053,67 @@ TEST(seccomp_syscall_mode_lock)  	}  } +/* + * Test detection of known and unknown filter flags. Userspace needs to be able + * to check if a filter flag is supported by the current kernel and a good way + * of doing that is by attempting to enter filter mode, with the flag bit in + * question set, and a NULL pointer for the _args_ parameter. EFAULT indicates + * that the flag is valid and EINVAL indicates that the flag is invalid. + */ +TEST(detect_seccomp_filter_flags) +{ +	unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC, +				 SECCOMP_FILTER_FLAG_LOG }; +	unsigned int flag, all_flags; +	int i; +	long ret; + +	/* Test detection of known-good filter flags */ +	for (i = 0, all_flags = 0; i < ARRAY_SIZE(flags); i++) { +		flag = flags[i]; +		ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL); +		ASSERT_NE(ENOSYS, errno) { +			TH_LOG("Kernel does not support seccomp syscall!"); +		} +		EXPECT_EQ(-1, ret); +		EXPECT_EQ(EFAULT, errno) { +			TH_LOG("Failed to detect that a known-good filter flag (0x%X) is supported!", +			       flag); +		} + +		all_flags |= flag; +	} + +	/* Test detection of all known-good filter flags */ +	ret = seccomp(SECCOMP_SET_MODE_FILTER, all_flags, NULL); +	EXPECT_EQ(-1, ret); +	EXPECT_EQ(EFAULT, errno) { +		TH_LOG("Failed to detect that all known-good filter flags (0x%X) are supported!", +		       all_flags); +	} + +	/* Test detection of an unknown filter flag */ +	flag = -1; +	ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL); +	EXPECT_EQ(-1, ret); +	EXPECT_EQ(EINVAL, errno) { +		TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported!", +		       flag); +	} + +	/* +	 * Test detection of an unknown filter flag that may simply need to be +	 * added to this test +	 */ +	flag = flags[ARRAY_SIZE(flags) - 1] << 1; +	ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL); +	EXPECT_EQ(-1, ret); +	EXPECT_EQ(EINVAL, errno) { +		TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported! Does a new flag need to be added to this test?", +		       flag); +	} +} +  TEST(TSYNC_first)  {  	struct sock_filter filter[] = { @@ -2421,6 +2752,99 @@ TEST(syscall_restart)  		_metadata->passed = 0;  } +TEST_SIGNAL(filter_flag_log, SIGSYS) +{ +	struct sock_filter allow_filter[] = { +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), +	}; +	struct sock_filter kill_filter[] = { +		BPF_STMT(BPF_LD|BPF_W|BPF_ABS, +			offsetof(struct seccomp_data, nr)), +		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL), +		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), +	}; +	struct sock_fprog allow_prog = { +		.len = (unsigned short)ARRAY_SIZE(allow_filter), +		.filter = allow_filter, +	}; +	struct sock_fprog kill_prog = { +		.len = (unsigned short)ARRAY_SIZE(kill_filter), +		.filter = kill_filter, +	}; +	long ret; +	pid_t parent = getppid(); + +	ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); +	ASSERT_EQ(0, ret); + +	/* Verify that the FILTER_FLAG_LOG flag isn't accepted in strict mode */ +	ret = seccomp(SECCOMP_SET_MODE_STRICT, SECCOMP_FILTER_FLAG_LOG, +		      &allow_prog); +	ASSERT_NE(ENOSYS, errno) { +		TH_LOG("Kernel does not support seccomp syscall!"); +	} +	EXPECT_NE(0, ret) { +		TH_LOG("Kernel accepted FILTER_FLAG_LOG flag in strict mode!"); +	} +	EXPECT_EQ(EINVAL, errno) { +		TH_LOG("Kernel returned unexpected errno for FILTER_FLAG_LOG flag in strict mode!"); +	} + +	/* Verify that a simple, permissive filter can be added with no flags */ +	ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &allow_prog); +	EXPECT_EQ(0, ret); + +	/* See if the same filter can be added with the FILTER_FLAG_LOG flag */ +	ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG, +		      &allow_prog); +	ASSERT_NE(EINVAL, errno) { +		TH_LOG("Kernel does not support the FILTER_FLAG_LOG flag!"); +	} +	EXPECT_EQ(0, ret); + +	/* Ensure that the kill filter works with the FILTER_FLAG_LOG flag */ +	ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG, +		      &kill_prog); +	EXPECT_EQ(0, ret); + +	EXPECT_EQ(parent, syscall(__NR_getppid)); +	/* getpid() should never return. */ +	EXPECT_EQ(0, syscall(__NR_getpid)); +} + +TEST(get_action_avail) +{ +	__u32 actions[] = { SECCOMP_RET_KILL_THREAD, SECCOMP_RET_TRAP, +			    SECCOMP_RET_ERRNO, SECCOMP_RET_TRACE, +			    SECCOMP_RET_LOG,   SECCOMP_RET_ALLOW }; +	__u32 unknown_action = 0x10000000U; +	int i; +	long ret; + +	ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &actions[0]); +	ASSERT_NE(ENOSYS, errno) { +		TH_LOG("Kernel does not support seccomp syscall!"); +	} +	ASSERT_NE(EINVAL, errno) { +		TH_LOG("Kernel does not support SECCOMP_GET_ACTION_AVAIL operation!"); +	} +	EXPECT_EQ(ret, 0); + +	for (i = 0; i < ARRAY_SIZE(actions); i++) { +		ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &actions[i]); +		EXPECT_EQ(ret, 0) { +			TH_LOG("Expected action (0x%X) not available!", +			       actions[i]); +		} +	} + +	/* Check that an unknown action is handled properly (EOPNOTSUPP) */ +	ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &unknown_action); +	EXPECT_EQ(ret, -1); +	EXPECT_EQ(errno, EOPNOTSUPP); +} +  /*   * TODO:   * - add microbenchmarks @@ -2429,6 +2853,8 @@ TEST(syscall_restart)   * - endianness checking when appropriate   * - 64-bit arg prodding   * - arch value testing (x86 modes especially) + * - verify that FILTER_FLAG_LOG filters generate log messages + * - verify that RET_LOG generates log messages   * - ...   */ |