// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/files/scoped_file.h" #include "base/macros.h" #include "base/posix/eintr_wrapper.h" #include "base/threading/thread.h" #include "build/build_config.h" #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" #include "sandbox/linux/seccomp-bpf/bpf_tests.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" #include "sandbox/linux/seccomp-bpf/syscall.h" #include "sandbox/linux/services/syscall_wrappers.h" #include "sandbox/linux/services/thread_helpers.h" #include "sandbox/linux/system_headers/linux_futex.h" #include "sandbox/linux/system_headers/linux_syscalls.h" #include "sandbox/linux/tests/test_utils.h" #include "sandbox/linux/tests/unit_tests.h" #if !defined(SO_PEEK_OFF) #define SO_PEEK_OFF 42 #endif namespace sandbox { namespace { // This also tests that read(), write() and fstat() are allowed. void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) { BPF_ASSERT_LE(0, read_end.get()); BPF_ASSERT_LE(0, write_end.get()); struct stat stat_buf; int sys_ret = fstat(read_end.get(), &stat_buf); BPF_ASSERT_EQ(0, sys_ret); BPF_ASSERT(S_ISFIFO(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode)); const ssize_t kTestTransferSize = 4; static const char kTestString[kTestTransferSize] = {'T', 'E', 'S', 'T'}; ssize_t transfered = 0; transfered = HANDLE_EINTR(write(write_end.get(), kTestString, kTestTransferSize)); BPF_ASSERT_EQ(kTestTransferSize, transfered); char read_buf[kTestTransferSize + 1] = {0}; transfered = HANDLE_EINTR(read(read_end.get(), read_buf, sizeof(read_buf))); BPF_ASSERT_EQ(kTestTransferSize, transfered); BPF_ASSERT_EQ(0, memcmp(kTestString, read_buf, kTestTransferSize)); } // Test that a few easy-to-test system calls are allowed. BPF_TEST_C(BaselinePolicy, BaselinePolicyBasicAllowed, BaselinePolicy) { BPF_ASSERT_EQ(0, sched_yield()); int pipefd[2]; int sys_ret = pipe(pipefd); BPF_ASSERT_EQ(0, sys_ret); TestPipeOrSocketPair(base::ScopedFD(pipefd[0]), base::ScopedFD(pipefd[1])); BPF_ASSERT_LE(1, getpid()); BPF_ASSERT_LE(0, getuid()); } BPF_TEST_C(BaselinePolicy, FchmodErrno, BaselinePolicy) { int ret = fchmod(-1, 07777); BPF_ASSERT_EQ(-1, ret); // Without the sandbox, this would EBADF instead. BPF_ASSERT_EQ(EPERM, errno); } BPF_TEST_C(BaselinePolicy, ForkErrno, BaselinePolicy) { errno = 0; pid_t pid = fork(); const int fork_errno = errno; TestUtils::HandlePostForkReturn(pid); BPF_ASSERT_EQ(-1, pid); BPF_ASSERT_EQ(EPERM, fork_errno); } pid_t ForkX86Glibc() { static pid_t ptid; return sys_clone(CLONE_PARENT_SETTID | SIGCHLD, nullptr, &ptid, nullptr, nullptr); } BPF_TEST_C(BaselinePolicy, ForkX86Eperm, BaselinePolicy) { errno = 0; pid_t pid = ForkX86Glibc(); const int fork_errno = errno; TestUtils::HandlePostForkReturn(pid); BPF_ASSERT_EQ(-1, pid); BPF_ASSERT_EQ(EPERM, fork_errno); } pid_t ForkARMGlibc() { static pid_t ctid; return sys_clone(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, nullptr, nullptr, &ctid, nullptr); } BPF_TEST_C(BaselinePolicy, ForkArmEperm, BaselinePolicy) { errno = 0; pid_t pid = ForkARMGlibc(); const int fork_errno = errno; TestUtils::HandlePostForkReturn(pid); BPF_ASSERT_EQ(-1, pid); BPF_ASSERT_EQ(EPERM, fork_errno); } BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) { base::Thread thread("sandbox_tests"); BPF_ASSERT(thread.Start()); } BPF_DEATH_TEST_C(BaselinePolicy, DisallowedCloneFlagCrashes, DEATH_SEGV_MESSAGE(GetCloneErrorMessageContentForTests()), BaselinePolicy) { pid_t pid = sys_clone(CLONE_THREAD | SIGCHLD); TestUtils::HandlePostForkReturn(pid); } BPF_DEATH_TEST_C(BaselinePolicy, DisallowedKillCrashes, DEATH_SEGV_MESSAGE(GetKillErrorMessageContentForTests()), BaselinePolicy) { BPF_ASSERT_NE(1, getpid()); kill(1, 0); _exit(0); } BPF_TEST_C(BaselinePolicy, CanKillSelf, BaselinePolicy) { int sys_ret = kill(getpid(), 0); BPF_ASSERT_EQ(0, sys_ret); } BPF_TEST_C(BaselinePolicy, Socketpair, BaselinePolicy) { int sv[2]; int sys_ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv); BPF_ASSERT_EQ(0, sys_ret); TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1])); sys_ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv); BPF_ASSERT_EQ(0, sys_ret); TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1])); } // Not all architectures can restrict the domain for socketpair(). #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) BPF_DEATH_TEST_C(BaselinePolicy, SocketpairWrongDomain, DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { int sv[2]; ignore_result(socketpair(AF_INET, SOCK_STREAM, 0, sv)); _exit(1); } #endif // defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) BPF_TEST_C(BaselinePolicy, EPERM_open, BaselinePolicy) { errno = 0; int sys_ret = open("/proc/cpuinfo", O_RDONLY); BPF_ASSERT_EQ(-1, sys_ret); BPF_ASSERT_EQ(EPERM, errno); } BPF_TEST_C(BaselinePolicy, EPERM_access, BaselinePolicy) { errno = 0; int sys_ret = access("/proc/cpuinfo", R_OK); BPF_ASSERT_EQ(-1, sys_ret); BPF_ASSERT_EQ(EPERM, errno); } BPF_TEST_C(BaselinePolicy, EPERM_getcwd, BaselinePolicy) { errno = 0; char buf[1024]; char* cwd = getcwd(buf, sizeof(buf)); BPF_ASSERT_EQ(NULL, cwd); BPF_ASSERT_EQ(EPERM, errno); } BPF_DEATH_TEST_C(BaselinePolicy, SIGSYS_InvalidSyscall, DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { Syscall::InvalidCall(); } // A failing test using this macro could be problematic since we perform // system calls by passing "0" as every argument. // The kernel could SIGSEGV the process or the system call itself could reboot // the machine. Some thoughts have been given when hand-picking the system // calls below to limit any potential side effects outside of the current // process. #define TEST_BASELINE_SIGSYS(sysno) \ BPF_DEATH_TEST_C(BaselinePolicy, \ SIGSYS_##sysno, \ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), \ BaselinePolicy) { \ syscall(sysno, 0, 0, 0, 0, 0, 0); \ _exit(1); \ } TEST_BASELINE_SIGSYS(__NR_acct); TEST_BASELINE_SIGSYS(__NR_chroot); TEST_BASELINE_SIGSYS(__NR_fanotify_init); TEST_BASELINE_SIGSYS(__NR_fgetxattr); TEST_BASELINE_SIGSYS(__NR_getcpu); TEST_BASELINE_SIGSYS(__NR_getitimer); TEST_BASELINE_SIGSYS(__NR_init_module); TEST_BASELINE_SIGSYS(__NR_io_cancel); TEST_BASELINE_SIGSYS(__NR_keyctl); TEST_BASELINE_SIGSYS(__NR_mq_open); TEST_BASELINE_SIGSYS(__NR_ptrace); TEST_BASELINE_SIGSYS(__NR_sched_setaffinity); TEST_BASELINE_SIGSYS(__NR_setpgid); TEST_BASELINE_SIGSYS(__NR_swapon); TEST_BASELINE_SIGSYS(__NR_sysinfo); TEST_BASELINE_SIGSYS(__NR_syslog); TEST_BASELINE_SIGSYS(__NR_timer_create); #if !defined(__aarch64__) TEST_BASELINE_SIGSYS(__NR_eventfd); TEST_BASELINE_SIGSYS(__NR_inotify_init); TEST_BASELINE_SIGSYS(__NR_vserver); #endif #if defined(LIBC_GLIBC) && !defined(OS_CHROMEOS) BPF_TEST_C(BaselinePolicy, FutexEINVAL, BaselinePolicy) { int ops[] = { FUTEX_CMP_REQUEUE_PI, FUTEX_CMP_REQUEUE_PI_PRIVATE, FUTEX_UNLOCK_PI_PRIVATE, }; for (int op : ops) { BPF_ASSERT_EQ(-1, syscall(__NR_futex, NULL, op, 0, NULL, NULL, 0)); BPF_ASSERT_EQ(EINVAL, errno); } } #else BPF_DEATH_TEST_C(BaselinePolicy, FutexWithRequeuePriorityInheritence, DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()), BaselinePolicy) { syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI, 0, NULL, NULL, 0); _exit(1); } BPF_DEATH_TEST_C(BaselinePolicy, FutexWithRequeuePriorityInheritencePrivate, DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()), BaselinePolicy) { syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, NULL, NULL, 0); _exit(1); } BPF_DEATH_TEST_C(BaselinePolicy, FutexWithUnlockPIPrivate, DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()), BaselinePolicy) { syscall(__NR_futex, NULL, FUTEX_UNLOCK_PI_PRIVATE, 0, NULL, NULL, 0); _exit(1); } #endif // defined(LIBC_GLIBC) && !defined(OS_CHROMEOS) BPF_TEST_C(BaselinePolicy, PrctlDumpable, BaselinePolicy) { const int is_dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); BPF_ASSERT(is_dumpable == 1 || is_dumpable == 0); const int prctl_ret = prctl(PR_SET_DUMPABLE, is_dumpable, 0, 0, 0, 0); BPF_ASSERT_EQ(0, prctl_ret); } // Workaround incomplete Android headers. #if !defined(PR_CAPBSET_READ) #define PR_CAPBSET_READ 23 #endif BPF_DEATH_TEST_C(BaselinePolicy, PrctlSigsys, DEATH_SEGV_MESSAGE(GetPrctlErrorMessageContentForTests()), BaselinePolicy) { prctl(PR_CAPBSET_READ, 0, 0, 0, 0); _exit(1); } BPF_TEST_C(BaselinePolicy, GetOrSetPriority, BaselinePolicy) { errno = 0; const int original_prio = getpriority(PRIO_PROCESS, 0); // Check errno instead of the return value since this system call can return // -1 as a valid value. BPF_ASSERT_EQ(0, errno); errno = 0; int rc = getpriority(PRIO_PROCESS, getpid()); BPF_ASSERT_EQ(0, errno); rc = getpriority(PRIO_PROCESS, getpid() + 1); BPF_ASSERT_EQ(-1, rc); BPF_ASSERT_EQ(EPERM, errno); rc = setpriority(PRIO_PROCESS, 0, original_prio); BPF_ASSERT_EQ(0, rc); rc = setpriority(PRIO_PROCESS, getpid(), original_prio); BPF_ASSERT_EQ(0, rc); errno = 0; rc = setpriority(PRIO_PROCESS, getpid() + 1, original_prio); BPF_ASSERT_EQ(-1, rc); BPF_ASSERT_EQ(EPERM, errno); } BPF_DEATH_TEST_C(BaselinePolicy, GetPrioritySigsys, DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { getpriority(PRIO_USER, 0); _exit(1); } BPF_DEATH_TEST_C(BaselinePolicy, ClockGettimeWithDisallowedClockCrashes, DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), BaselinePolicy) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC_RAW, &ts); } #if !defined(__i386__) BPF_DEATH_TEST_C(BaselinePolicy, GetSockOptWrongLevelSigsys, DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); int id; socklen_t peek_off_size = sizeof(id); getsockopt(fds[0], IPPROTO_TCP, SO_PEEK_OFF, &id, &peek_off_size); } BPF_DEATH_TEST_C(BaselinePolicy, GetSockOptWrongOptionSigsys, DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); int id; socklen_t peek_off_size = sizeof(id); getsockopt(fds[0], SOL_SOCKET, SO_DEBUG, &id, &peek_off_size); } BPF_DEATH_TEST_C(BaselinePolicy, SetSockOptWrongLevelSigsys, DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); int id; setsockopt(fds[0], IPPROTO_TCP, SO_PEEK_OFF, &id, sizeof(id)); } BPF_DEATH_TEST_C(BaselinePolicy, SetSockOptWrongOptionSigsys, DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), BaselinePolicy) { int fds[2]; PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); int id; setsockopt(fds[0], SOL_SOCKET, SO_DEBUG, &id, sizeof(id)); } #endif } // namespace } // namespace sandbox