// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * * Test code for seccomp bpf.
*/
#define _GNU_SOURCE #include <sys/types.h>
/* * 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
#ifndef SECCOMP_IOCTL_NOTIF_ADDFD /* On success, the return value is the remote process's added fd number */ #define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOW(3, \ struct seccomp_notif_addfd)
/* Have TH_LOG report actual location filecmp() is used. */ #define filecmp(pid1, pid2, fd1, fd2) ({ \ int _ret; \
\
_ret = __filecmp(pid1, pid2, fd1, fd2); \ if (_ret != 0) { \ if (_ret < 0 && errno == ENOSYS) { \
TH_LOG("kcmp() syscall missing (test is less accurate)");\
_ret = 0; \
} \
} \
_ret; })
TEST(kcmp)
{ int ret;
ret = __filecmp(getpid(), getpid(), 1, 1);
EXPECT_EQ(ret, 0); if (ret != 0 && errno == ENOSYS)
SKIP(return, "Kernel does not support kcmp() (missing CONFIG_KCMP?)");
}
TEST(mode_strict_support)
{ long ret;
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, NULL, NULL);
ASSERT_EQ(0, ret) {
TH_LOG("Kernel does not support CONFIG_SECCOMP");
}
syscall(__NR_exit, 0);
}
TEST_SIGNAL(mode_strict_cannot_call_prctl, SIGKILL)
{ long ret;
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, NULL, NULL);
ASSERT_EQ(0, ret) {
TH_LOG("Kernel does not support CONFIG_SECCOMP");
}
syscall(__NR_prctl, PR_SET_SECCOMP, SECCOMP_MODE_FILTER,
NULL, NULL, NULL);
EXPECT_FALSE(true) {
TH_LOG("Unreachable!");
}
}
/* Note! This doesn't test no new privs behavior */
TEST(no_new_privs_support)
{ long ret;
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
EXPECT_EQ(0, ret) {
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
}
}
/* Tests kernel support by checking for a copy_from_user() fault on NULL. */
TEST(mode_filter_support)
{ long ret;
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
ASSERT_EQ(0, ret) {
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
}
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, NULL, NULL);
EXPECT_EQ(-1, ret);
EXPECT_EQ(EFAULT, errno) {
TH_LOG("Kernel does not support CONFIG_SECCOMP_FILTER!");
}
}
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
ASSERT_EQ(0, ret);
prog.filter = filter;
prog.len = count;
/* Too many filter instructions in a single filter. */
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
ASSERT_NE(0, ret) {
TH_LOG("Installing %d insn filter was allowed", prog.len);
}
/* One less is okay, though. */
prog.len -= 1;
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
ASSERT_EQ(0, ret) {
TH_LOG("Installing %d insn filter wasn't allowed", prog.len);
}
}
TEST(filter_chain_limits)
{ int i; int count = BPF_MAXINSNS; struct sock_filter allow[] = {
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
}; struct sock_filter *filter; struct sock_fprog prog = { }; long ret;
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
ASSERT_EQ(0, ret);
prog.filter = filter;
prog.len = 1;
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
ASSERT_EQ(0, ret);
prog.len = count;
/* Too many total filter instructions. */ for (i = 0; i < MAX_INSNS_PER_PATH; i++) {
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0); if (ret != 0) break;
}
ASSERT_NE(0, ret) {
TH_LOG("Allowed %d %d-insn filters (total with penalties:%d)",
i, count, i * (count + 4));
}
}
/* * Add the KILL_THREAD rule again to make sure that the KILL_PROCESS * flag cannot be downgraded by a new filter.
*/ if (kill_how == KILL_PROCESS)
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, (unsignedlong)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, (unsignedlong)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);
}
/* If the entire process was killed, we'll see SIGSYS. */
EXPECT_TRUE(WIFSIGNALED(status)) {
TH_LOG("Unknown SECCOMP_RET is only killing the thread?");
}
ASSERT_EQ(SIGSYS, WTERMSIG(status));
}
/* Make sure basic errno values are correctly passed through a filter. */
TEST(ERRNO_valid)
{
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_valid);
ASSERT_EQ(0, ret);
/* Make sure an errno of zero is correctly handled by the arch code. */
TEST(ERRNO_zero)
{
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_zero);
ASSERT_EQ(0, ret);
EXPECT_EQ(parent, syscall(__NR_getppid)); /* "errno" of 0 is ok. */
EXPECT_EQ(0, read(-1, 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)
{
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_capped);
ASSERT_EQ(0, ret);
/* * 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);
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);
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->kill);
ASSERT_EQ(0, ret); /* Should work just fine. */
res = syscall(__NR_getppid);
EXPECT_EQ(parent, res); /* getpid() should never return. */
res = syscall(__NR_getpid);
EXPECT_EQ(0, res);
}
TEST_F_SIGNAL(precedence, kill_is_highest_in_any_order, SIGSYS)
{
pid_t parent; long 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;
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));
}
ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
EXPECT_EQ(0, ret); /* If this fails, don't try to recover. */
ASSERT_EQ(0x1001, msg) {
kill(tracee, SIGKILL);
} /* * Poke in the message. * Registers are not touched to try to keep this relatively arch * agnostic.
*/
ret = ptrace(PTRACE_POKEDATA, tracee, info->poke_addr, 0x1001);
EXPECT_EQ(0, ret);
}
#ifdefined(__x86_64__) # define ARCH_REGS struct user_regs_struct # define SYSCALL_NUM(_regs) (_regs).orig_rax # define SYSCALL_RET(_regs) (_regs).rax #elifdefined(__i386__) # define ARCH_REGS struct user_regs_struct # define SYSCALL_NUM(_regs) (_regs).orig_eax # define SYSCALL_RET(_regs) (_regs).eax #elifdefined(__arm__) # define ARCH_REGS struct pt_regs # define SYSCALL_NUM(_regs) (_regs).ARM_r7 # ifndef PTRACE_SET_SYSCALL # define PTRACE_SET_SYSCALL 23 # endif # define SYSCALL_NUM_SET(_regs, _nr) \
EXPECT_EQ(0, ptrace(PTRACE_SET_SYSCALL, tracee, NULL, _nr)) # define SYSCALL_RET(_regs) (_regs).ARM_r0 #elifdefined(__aarch64__) # define ARCH_REGS struct user_pt_regs # define SYSCALL_NUM(_regs) (_regs).regs[8] # ifndef NT_ARM_SYSTEM_CALL # define NT_ARM_SYSTEM_CALL 0x404 # endif # define SYSCALL_NUM_SET(_regs, _nr) \ do { \ struct iovec __v; \
typeof(_nr) __nr = (_nr); \
__v.iov_base = &__nr; \
__v.iov_len = sizeof(__nr); \
EXPECT_EQ(0, ptrace(PTRACE_SETREGSET, tracee, \
NT_ARM_SYSTEM_CALL, &__v)); \
} while (0) # define SYSCALL_RET(_regs) (_regs).regs[0] #elifdefined(__loongarch__) # define ARCH_REGS struct user_pt_regs # define SYSCALL_NUM(_regs) (_regs).regs[11] # define SYSCALL_RET(_regs) (_regs).regs[4] #elifdefined(__riscv) && __riscv_xlen == 64 # define ARCH_REGS struct user_regs_struct # define SYSCALL_NUM(_regs) (_regs).a7 # define SYSCALL_RET(_regs) (_regs).a0 #elifdefined(__csky__) # define ARCH_REGS struct pt_regs # ifdefined(__CSKYABIV2__) # define SYSCALL_NUM(_regs) (_regs).regs[3] # else # define SYSCALL_NUM(_regs) (_regs).regs[9] # endif # define SYSCALL_RET(_regs) (_regs).a0 #elifdefined(__hppa__) # define ARCH_REGS struct user_regs_struct # define SYSCALL_NUM(_regs) (_regs).gr[20] # define SYSCALL_RET(_regs) (_regs).gr[28] #elifdefined(__powerpc__) # define ARCH_REGS struct pt_regs # define SYSCALL_NUM(_regs) (_regs).gpr[0] # define SYSCALL_RET(_regs) (_regs).gpr[3] # define SYSCALL_RET_SET(_regs, _val) \ do { \
typeof(_val) _result = (_val); \ if ((_regs.trap & 0xfff0) == 0x3000) { \ /* \ * scv 0 system call uses -ve result \ * for error, so no need to adjust. \
*/
SYSCALL_RET(_regs) = _result; \
} else { \ /* \ * A syscall error is signaled by the \ * CR0 SO bit and the code is stored as \ * a positive value. \
*/ if (_result < 0) { \
SYSCALL_RET(_regs) = -_result; \
(_regs).ccr |= 0x10000000; \
} else { \
SYSCALL_RET(_regs) = _result; \
(_regs).ccr &= ~0x10000000; \
} \
} \
} while (0) # define SYSCALL_RET_SET_ON_PTRACE_EXIT #elifdefined(__s390__) # define ARCH_REGS s390_regs # define SYSCALL_NUM(_regs) (_regs).gprs[2] # define SYSCALL_RET_SET(_regs, _val) \
TH_LOG("Can't modify syscall return on this architecture") #elifdefined(__mips__) # include <asm/unistd_nr_n32.h> # include <asm/unistd_nr_n64.h> # include <asm/unistd_nr_o32.h> # define ARCH_REGS struct pt_regs # define SYSCALL_NUM(_regs) \
({ \
typeof((_regs).regs[2]) _nr; \ if ((_regs).regs[2] == __NR_O32_Linux) \
_nr = (_regs).regs[4]; \ else \
_nr = (_regs).regs[2]; \
_nr; \
}) # define SYSCALL_NUM_SET(_regs, _nr) \ do { \ if ((_regs).regs[2] == __NR_O32_Linux) \
(_regs).regs[4] = _nr; \ else \
(_regs).regs[2] = _nr; \
} while (0) # define SYSCALL_RET_SET(_regs, _val) \
TH_LOG("Can't modify syscall return on this architecture") #elifdefined(__xtensa__) # define ARCH_REGS struct user_pt_regs # define SYSCALL_NUM(_regs) (_regs).syscall /* * On xtensa syscall return value is in the register * a2 of the current window which is not fixed.
*/ #define SYSCALL_RET(_regs) (_regs).a[(_regs).windowbase * 4 + 2] #elifdefined(__sh__) # define ARCH_REGS struct pt_regs # define SYSCALL_NUM(_regs) (_regs).regs[3] # define SYSCALL_RET(_regs) (_regs).regs[0] #elifdefined(__mc68000__) # define ARCH_REGS struct user_regs_struct # define SYSCALL_NUM(_regs) (_regs).orig_d0 # define SYSCALL_RET(_regs) (_regs).d0 #else # error "Do not know how to find your architecture's registers and syscalls" #endif
/* * Most architectures can change the syscall by just updating the * associated register. This is the default if not defined above.
*/ #ifndef SYSCALL_NUM_SET # define SYSCALL_NUM_SET(_regs, _nr) \ do { \
SYSCALL_NUM(_regs) = (_nr); \
} while (0) #endif /* * Most architectures can change the syscall return value by just * writing to the SYSCALL_RET register. This is the default if not * defined above. If an architecture cannot set the return value * (for example when the syscall and return value register is * shared), report it with TH_LOG() in an arch-specific definition * of SYSCALL_RET_SET() above, and leave SYSCALL_RET undefined.
*/ #if !defined(SYSCALL_RET) && !defined(SYSCALL_RET_SET) # error "One of SYSCALL_RET or SYSCALL_RET_SET is needed for this arch" #endif #ifndef SYSCALL_RET_SET # define SYSCALL_RET_SET(_regs, _val) \ do { \
SYSCALL_RET(_regs) = (_val); \
} while (0) #endif
/* When the syscall return can't be changed, stub out the tests for it. */ #ifndef SYSCALL_RET # define EXPECT_SYSCALL_RETURN(val, action) EXPECT_EQ(-1, action) #else # define EXPECT_SYSCALL_RETURN(val, action) \ do { \
errno = 0; \ if (val < 0) { \
EXPECT_EQ(-1, action); \
EXPECT_EQ(-(val), errno); \
} else { \
EXPECT_EQ(val, action); \
} \
} while (0) #endif
/* * Some architectures (e.g. powerpc) can only set syscall * return values on syscall exit during ptrace.
*/ constbool ptrace_entry_set_syscall_nr = true; constbool ptrace_entry_set_syscall_ret = #ifndef SYSCALL_RET_SET_ON_PTRACE_EXIT true; #else false; #endif
/* Flush any register changes made. */ if (memcmp(&orig, ®s, sizeof(orig)) != 0)
EXPECT_EQ(0, ARCH_SETREGS(regs));
}
/* Change only syscall number. */ void change_syscall_nr(struct __test_metadata *_metadata,
pid_t tracee, long syscall)
{
__change_syscall(_metadata, tracee, &syscall, NULL);
}
/* Change syscall return value (and set syscall number to -1). */ void change_syscall_ret(struct __test_metadata *_metadata,
pid_t tracee, long ret)
{ long syscall = -1;
/* * The traditional way to tell PTRACE_SYSCALL entry/exit * is by counting.
*/
entry = !entry;
/* Make sure we got an appropriate message. */
ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
EXPECT_EQ(0, ret);
EXPECT_EQ(entry ? PTRACE_EVENTMSG_SYSCALL_ENTRY
: PTRACE_EVENTMSG_SYSCALL_EXIT, msg);
/* * Some architectures only support setting return values during * syscall exit under ptrace, and on exit the syscall number may * no longer be available. Therefore, save the initial sycall * number here, so it can be examined during both entry and exit * phases.
*/ if (entry)
self->syscall_nr = get_syscall(_metadata, tracee);
/* * Depending on the architecture's syscall setting abilities, we * pick which things to set during this phase (entry or exit).
*/ if (entry == ptrace_entry_set_syscall_nr)
syscall_nr = &syscall_nr_val; if (entry == ptrace_entry_set_syscall_ret)
syscall_ret = &syscall_ret_val;
/* Now handle the actual rewriting cases. */ switch (self->syscall_nr) { case __NR_getpid:
syscall_nr_val = __NR_getppid; /* Never change syscall return for this case. */
syscall_ret = NULL; break; case __NR_gettid:
syscall_nr_val = -1;
syscall_ret_val = 45000; break; case __NR_openat:
syscall_nr_val = -1;
syscall_ret_val = -ESRCH; break; default: /* Unhandled, do nothing. */ return;
}
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;
};
/* Prepare some testable syscall results. */
self->mytid = syscall(__NR_gettid);
ASSERT_GT(self->mytid, 0);
ASSERT_NE(self->mytid, 1) {
TH_LOG("Running this test as init is not supported. :)");
}
TEST(negative_ENOSYS)
{ #ifdefined(__arm__)
SKIP(return, "arm32 does not support calling syscall -1"); #endif /* * There should be no difference between an "internal" skip * and userspace asking for syscall "-1".
*/
errno = 0;
EXPECT_EQ(-1, syscall(-1));
EXPECT_EQ(errno, ENOSYS); /* And no difference for "still not valid but not -1". */
errno = 0;
EXPECT_EQ(-1, syscall(-101));
EXPECT_EQ(errno, ENOSYS);
}
TEST_F(TRACE_syscall, syscall_allowed)
{ /* getppid works as expected (no changes). */
EXPECT_EQ(self->parent, syscall(__NR_getppid));
EXPECT_NE(self->mypid, syscall(__NR_getppid));
}
TEST_F(TRACE_syscall, syscall_redirected)
{ /* getpid has been redirected to getppid as expected. */
EXPECT_EQ(self->parent, syscall(__NR_getpid));
EXPECT_NE(self->mypid, syscall(__NR_getpid));
}
TEST_F(TRACE_syscall, syscall_errno)
{ /* Tracer should skip the open syscall, resulting in ESRCH. */
EXPECT_SYSCALL_RETURN(-ESRCH, syscall(__NR_openat));
}
TEST_F(TRACE_syscall, syscall_faked)
{ /* Tracer skips the gettid syscall and store altered return value. */
EXPECT_SYSCALL_RETURN(45000, syscall(__NR_gettid));
}
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!");
}
/* Reject insane operation. */
ret = seccomp(-1, 0, &prog);
ASSERT_NE(ENOSYS, errno) {
TH_LOG("Kernel does not support seccomp syscall!");
}
EXPECT_EQ(EINVAL, errno) {
TH_LOG("Did not reject crazy op value!");
}
/* Reject strict with flags or pointer. */
ret = seccomp(SECCOMP_SET_MODE_STRICT, -1, NULL);
EXPECT_EQ(EINVAL, errno) {
TH_LOG("Did not reject mode strict with flags!");
}
ret = seccomp(SECCOMP_SET_MODE_STRICT, 0, &prog);
EXPECT_EQ(EINVAL, errno) {
TH_LOG("Did not reject mode strict with uargs!");
}
/* Reject insane args for filter. */
ret = seccomp(SECCOMP_SET_MODE_FILTER, -1, &prog);
EXPECT_EQ(EINVAL, errno) {
TH_LOG("Did not reject crazy filter flags!");
}
ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, NULL);
EXPECT_EQ(EFAULT, errno) {
TH_LOG("Did not reject NULL filter!");
}
ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
EXPECT_EQ(0, errno) {
TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER: %s",
strerror(errno));
}
}
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
ASSERT_EQ(0, ret) {
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
}
ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
ASSERT_NE(ENOSYS, errno) {
TH_LOG("Kernel does not support seccomp syscall!");
}
EXPECT_EQ(0, ret) {
TH_LOG("Could not install filter!");
}
/* Make sure neither entry point will switch to strict. */
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0);
EXPECT_EQ(EINVAL, errno) {
TH_LOG("Switched to mode strict!");
}
ret = seccomp(SECCOMP_SET_MODE_STRICT, 0, NULL);
EXPECT_EQ(EINVAL, errno) {
TH_LOG("Switched to mode strict!");
}
}
/* * 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)
{ unsignedint flags[] = { SECCOMP_FILTER_FLAG_TSYNC,
SECCOMP_FILTER_FLAG_LOG,
SECCOMP_FILTER_FLAG_SPEC_ALLOW,
SECCOMP_FILTER_FLAG_NEW_LISTENER,
SECCOMP_FILTER_FLAG_TSYNC_ESRCH }; unsignedint exclusive[] = {
SECCOMP_FILTER_FLAG_TSYNC,
SECCOMP_FILTER_FLAG_NEW_LISTENER }; unsignedint flag, all_flags, exclusive_mask; int i; long ret;
/* Test detection of individual known-good filter flags */ for (i = 0, all_flags = 0; i < ARRAY_SIZE(flags); i++) { int bits = 0;
flag = flags[i]; /* Make sure the flag is a single bit! */ while (flag) { if (flag & 0x1)
bits ++;
flag >>= 1;
}
ASSERT_EQ(1, bits);
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 combined. But * for the exclusive flags we need to mask them out and try them * individually for the "all flags" testing.
*/
exclusive_mask = 0; for (i = 0; i < ARRAY_SIZE(exclusive); i++)
exclusive_mask |= exclusive[i]; for (i = 0; i < ARRAY_SIZE(exclusive); i++) {
flag = all_flags & ~exclusive_mask;
flag |= exclusive[i];
ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
EXPECT_EQ(-1, ret);
EXPECT_EQ(EFAULT, errno) {
TH_LOG("Failed to detect that all known-good filter flags (0x%X) are supported!",
flag);
}
}
/* Test detection of an unknown filter flags, without exclusives. */
flag = -1;
flag &= ~exclusive_mask;
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);
}
}
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
ASSERT_EQ(0, ret) {
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
}
ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC,
&prog);
ASSERT_NE(ENOSYS, errno) {
TH_LOG("Kernel does not support seccomp syscall!");
}
EXPECT_EQ(0, ret) {
TH_LOG("Could not install initial filter with TSYNC!");
}
}
/* * To avoid joining joined threads (which is not allowed by Bionic), * make sure we both successfully join and clear the tid to skip a * later join attempt during fixture teardown. Any remaining threads * will be directly killed during teardown.
*/ #define PTHREAD_JOIN(tid, status) \ do { \
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.52 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.