chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#include <memory>
#include <vector>

#include "base/containers/adapters.h"
#include "base/containers/contains.h"

#if defined(ANDROID)
// Work-around for buggy headers in Android's NDK
#define __user
#endif
#include <linux/futex.h>

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/posix/eintr_wrapper.h"
#include "base/synchronization/waitable_event.h"
#include "base/system/sys_info.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include "sandbox/linux/bpf_dsl/errorcode.h"
#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h"
#include "sandbox/linux/bpf_dsl/policy.h"
#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/syscall.h"
#include "sandbox/linux/seccomp-bpf/trap.h"
#include "sandbox/linux/services/syscall_wrappers.h"
#include "sandbox/linux/services/thread_helpers.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
#include "sandbox/linux/tests/scoped_temporary_file.h"
#include "sandbox/linux/tests/unit_tests.h"
#include "testing/gtest/include/gtest/gtest.h"

// Workaround for Android's prctl.h file.
#ifndef PR_GET_ENDIAN
#define PR_GET_ENDIAN
#endif
#ifndef PR_CAPBSET_READ
#define PR_CAPBSET_READ
#define PR_CAPBSET_DROP
#endif

namespace sandbox {
namespace bpf_dsl {

namespace {

const int kExpectedReturnValue =;
const char kSandboxDebuggingEnv[] =;

// Set the global environment to allow the use of UnsafeTrap() policies.
void EnableUnsafeTraps() {}

// BPF_TEST does a lot of the boiler-plate code around setting up a
// policy and optional passing data between the caller, the policy and
// any Trap() handlers. This is great for writing short and concise tests,
// and it helps us accidentally forgetting any of the crucial steps in
// setting up the sandbox. But it wouldn't hurt to have at least one test
// that explicitly walks through all these steps.

intptr_t IncreaseCounter(const struct arch_seccomp_data& args, void* aux) {}

class VerboseAPITestingPolicy : public Policy {};

SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(VerboseAPITesting)) {}

// A simple denylist test

class DenylistNanosleepPolicy : public Policy {};

BPF_TEST_C(SandboxBPF, ApplyBasicDenylistPolicy, DenylistNanosleepPolicy) {

BPF_TEST_C(SandboxBPF, UseVsyscall, DenylistNanosleepPolicy) {}

bool IsSyscallForTestHarness(int sysno) {}

// Now do a simple allowlist test

class AllowlistGetpidPolicy : public Policy {};

BPF_TEST_C(SandboxBPF, ApplyBasicAllowlistPolicy, AllowlistGetpidPolicy) {

// A simple denylist policy, with a SIGSYS handler
intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) {}

class DenylistNanosleepTrapPolicy : public Policy {};

BPF_TEST(SandboxBPF,
         BasicDenylistWithSigsys,
         DenylistNanosleepTrapPolicy,
         int /* (*BPF_AUX) */) {}

// A simple test that verifies we can return arbitrary errno values.

class ErrnoTestPolicy : public Policy {};

ResultExpr ErrnoTestPolicy::EvaluateSyscall(int sysno) const {}

BPF_TEST_C(SandboxBPF, ErrnoTest, ErrnoTestPolicy) {}

// Testing the stacking of two sandboxes

class StackingPolicyPartOne : public Policy {};

class StackingPolicyPartTwo : public Policy {};

// Depending on DCHECK being enabled or not the test may create some output.
// Therefore explicitly specify the death test to allow some noise.
BPF_DEATH_TEST_C(SandboxBPF,
                 StackingPolicy,
                 DEATH_SUCCESS_ALLOW_NOISE(),
                 StackingPolicyPartOne) {}

// A more complex, but synthetic policy. This tests the correctness of the BPF
// program by iterating through all syscalls and checking for an errno that
// depends on the syscall number. Unlike the Verifier, this exercises the BPF
// interpreter in the kernel.

// We try to make sure we exercise optimizations in the BPF compiler. We make
// sure that the compiler can have an opportunity to coalesce syscalls with
// contiguous numbers and we also make sure that disjoint sets can return the
// same errno.
int SysnoToRandomErrno(int sysno) {}

class SyntheticPolicy : public Policy {};

BPF_TEST_C(SandboxBPF, SyntheticPolicy, SyntheticPolicy) {}

#if defined(__arm__)
// A simple policy that tests whether ARM private system calls are supported
// by our BPF compiler and by the BPF interpreter in the kernel.

// For ARM private system calls, return an errno equal to their offset from
// MIN_PRIVATE_SYSCALL plus 1 (to avoid NUL errno).
int ArmPrivateSysnoToErrno(int sysno) {
  if (sysno >= static_cast<int>(MIN_PRIVATE_SYSCALL) &&
      sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
    return (sysno - MIN_PRIVATE_SYSCALL) + 1;
  }
  return ENOSYS;
}

class ArmPrivatePolicy : public Policy {
 public:
  ArmPrivatePolicy() = default;

  ArmPrivatePolicy(const ArmPrivatePolicy&) = delete;
  ArmPrivatePolicy& operator=(const ArmPrivatePolicy&) = delete;

  ~ArmPrivatePolicy() override = default;

  ResultExpr EvaluateSyscall(int sysno) const override {
    DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
    // Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
    // ARM private system calls.
    if (sysno >= static_cast<int>(__ARM_NR_set_tls + 1) &&
        sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
      return Error(ArmPrivateSysnoToErrno(sysno));
    }
    return Allow();
  }
};

BPF_TEST_C(SandboxBPF, ArmPrivatePolicy, ArmPrivatePolicy) {
  for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
       syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
       ++syscall_number) {
    errno = 0;
    BPF_ASSERT(syscall(syscall_number) == -1);
    BPF_ASSERT(errno == ArmPrivateSysnoToErrno(syscall_number));
  }
}
#endif  // defined(__arm__)

intptr_t CountSyscalls(const struct arch_seccomp_data& args, void* aux) {}

class GreyListedPolicy : public Policy {};

BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* (*BPF_AUX) */) {}

SANDBOX_TEST(SandboxBPF, EnableUnsafeTrapsInSigSysHandler) {

intptr_t PrctlHandler(const struct arch_seccomp_data& args, void*) {}

class PrctlPolicy : public Policy {};

BPF_TEST_C(SandboxBPF, ForwardSyscall, PrctlPolicy) {}

intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void*) {}

class RedirectAllSyscallsPolicy : public Policy {};

ResultExpr RedirectAllSyscallsPolicy::EvaluateSyscall(int sysno) const {}

#if !defined(ADDRESS_SANITIZER)
// ASan does not allow changing the signal handler for SIGBUS, and treats it as
// a fatal signal.

int bus_handler_fd_ =;

void SigBusHandler(int, siginfo_t* info, void* void_context) {}

BPF_TEST_C(SandboxBPF, SigBus, RedirectAllSyscallsPolicy) {}
#endif  // !defined(ADDRESS_SANITIZER)

BPF_TEST_C(SandboxBPF, SigMask, RedirectAllSyscallsPolicy) {}

BPF_TEST_C(SandboxBPF, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {}

// Simple test demonstrating how to use SandboxBPF::Cond()

class SimpleCondTestPolicy : public Policy {};

ResultExpr SimpleCondTestPolicy::EvaluateSyscall(int sysno) const {}

BPF_TEST_C(SandboxBPF, SimpleCondTest, SimpleCondTestPolicy) {}

// This test exercises the SandboxBPF::Cond() method by building a complex
// tree of conditional equality operations. It then makes system calls and
// verifies that they return the values that we expected from our BPF
// program.
class EqualityStressTest {};

class EqualityStressTestPolicy : public Policy {};

BPF_TEST(SandboxBPF,
         EqualityTests,
         EqualityStressTestPolicy,
         EqualityStressTest /* (*BPF_AUX) */) {

class EqualityArgumentWidthPolicy : public Policy {};

ResultExpr EqualityArgumentWidthPolicy::EvaluateSyscall(int sysno) const {}

BPF_TEST_C(SandboxBPF, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {

#if __SIZEOF_POINTER__ > 4
// On 32bit machines, there is no way to pass a 64bit argument through the
// syscall interface. So, we have to skip the part of the test that requires
// 64bit arguments.
BPF_DEATH_TEST_C(SandboxBPF,
                 EqualityArgumentUnallowed64bit,
                 DEATH_MESSAGE("Unexpected 64bit argument detected"),
                 EqualityArgumentWidthPolicy) {}
#endif

class EqualityWithNegativeArgumentsPolicy : public Policy {};

BPF_TEST_C(SandboxBPF,
           EqualityWithNegativeArguments,
           EqualityWithNegativeArgumentsPolicy) {

#if __SIZEOF_POINTER__ > 4
BPF_DEATH_TEST_C(SandboxBPF,
                 EqualityWithNegative64bitArguments,
                 DEATH_MESSAGE("Unexpected 64bit argument detected"),
                 EqualityWithNegativeArgumentsPolicy) {}
#endif

class AllBitTestPolicy : public Policy {};

ResultExpr AllBitTestPolicy::HasAllBits32(uint32_t bits) {}

ResultExpr AllBitTestPolicy::HasAllBits64(uint64_t bits) {}

ResultExpr AllBitTestPolicy::EvaluateSyscall(int sysno) const {}

// Define a macro that performs tests using our test policy.
// NOTE: Not all of the arguments in this macro are actually used!
//       They are here just to serve as documentation of the conditions
//       implemented in the test policy.
//       Most notably, "op" and "mask" are unused by the macro. If you want
//       to make changes to these values, you will have to edit the
//       test policy instead.
#define BITMASK_TEST(testcase, arg, op, mask, expected_value)

// Our uname() system call returns ErrorCode(1) for success and
// ErrorCode(0) for failure. Syscall::Call() turns this into an
// exit code of -1 or 0.
#define EXPECT_FAILURE
#define EXPECT_SUCCESS

// A couple of our tests behave differently on 32bit and 64bit systems, as
// there is no way for a 32bit system call to pass in a 64bit system call
// argument "arg".
// We expect these tests to succeed on 64bit systems, but to tail on 32bit
// systems.
#define EXPT64_SUCCESS
BPF_TEST_C(SandboxBPF, AllBitTests, AllBitTestPolicy) {

class AnyBitTestPolicy : public Policy {};

ResultExpr AnyBitTestPolicy::HasAnyBits32(uint32_t bits) {}

ResultExpr AnyBitTestPolicy::HasAnyBits64(uint64_t bits) {}

ResultExpr AnyBitTestPolicy::EvaluateSyscall(int sysno) const {}

BPF_TEST_C(SandboxBPF, AnyBitTests, AnyBitTestPolicy) {

class MaskedEqualTestPolicy : public Policy {};

ResultExpr MaskedEqualTestPolicy::MaskedEqual32(uint32_t mask, uint32_t value) {}

ResultExpr MaskedEqualTestPolicy::MaskedEqual64(uint64_t mask, uint64_t value) {}

ResultExpr MaskedEqualTestPolicy::EvaluateSyscall(int sysno) const {}

#define MASKEQ_TEST(rulenum, arg, expected_result)

BPF_TEST_C(SandboxBPF, MaskedEqualTests, MaskedEqualTestPolicy) {

intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void* aux) {}

class PthreadPolicyEquality : public Policy {};

ResultExpr PthreadPolicyEquality::EvaluateSyscall(int sysno) const {}

class PthreadPolicyBitMask : public Policy {};

BoolExpr PthreadPolicyBitMask::HasAnyBits(const Arg<unsigned long>& arg,
                                          unsigned long bits) {}

BoolExpr PthreadPolicyBitMask::HasAllBits(const Arg<unsigned long>& arg,
                                          unsigned long bits) {}

ResultExpr PthreadPolicyBitMask::EvaluateSyscall(int sysno) const {}

static void* ThreadFnc(void* arg) {}

static void PthreadTest() {}

BPF_TEST_C(SandboxBPF, PthreadEquality, PthreadPolicyEquality) {

BPF_TEST_C(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) {

// libc might not define these even though the kernel supports it.
#ifndef PTRACE_O_TRACESECCOMP
#define PTRACE_O_TRACESECCOMP
#endif

#ifdef PTRACE_EVENT_SECCOMP
#define IS_SECCOMP_EVENT
#else
// When Debian/Ubuntu backported seccomp-bpf support into earlier kernels, they
// changed the value of PTRACE_EVENT_SECCOMP from 7 to 8, since 7 was taken by
// PTRACE_EVENT_STOP (upstream chose to renumber PTRACE_EVENT_STOP to 128).  If
// PTRACE_EVENT_SECCOMP isn't defined, we have no choice but to consider both
// values here.
#define IS_SECCOMP_EVENT(status)
#endif

#if defined(__arm__)
#ifndef PTRACE_SET_SYSCALL
#define PTRACE_SET_SYSCALL
#endif
#endif

#if defined(__aarch64__)
#ifndef PTRACE_GETREGS
#if defined(__GLIBC__)
#define PTRACE_GETREGS
#else
#define PTRACE_GETREGS
#endif  // defined(__GLIBC__)
#endif  // !defined(PTRACE_GETREGS)
#endif  // defined(__aarch64__)

#if defined(__aarch64__)
#ifndef PTRACE_SETREGS
#if defined(__GLIBC__)
#define PTRACE_SETREGS
#else
#define PTRACE_SETREGS
#endif  // defined(__GLIBC__)
#endif  // !defined(PTRACE_SETREGS)
#endif  // defined(__aarch64__)

// Changes the syscall to run for a child being sandboxed using seccomp-bpf with
// PTRACE_O_TRACESECCOMP.  Should only be called when the child is stopped on
// PTRACE_EVENT_SECCOMP.
//
// regs should contain the current set of registers of the child, obtained using
// PTRACE_GETREGS.
//
// Depending on the architecture, this may modify regs, so the caller is
// responsible for committing these changes using PTRACE_SETREGS.
#if !defined(__arm__) && !defined(__aarch64__) && !defined(__mips__)
long SetSyscall(pid_t pid, regs_struct* regs, int syscall_number) {}
#endif

const uint16_t kTraceData =;

class TraceAllPolicy : public Policy {};

SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(SeccompRetTrace)) {}

// Android does not expose pread64 nor pwrite64.
#if !BUILDFLAG(IS_ANDROID)

bool FullPwrite64(int fd, const char* buffer, size_t count, off64_t offset) {}

bool FullPread64(int fd, char* buffer, size_t count, off64_t offset) {}

bool pread_64_was_forwarded =;

class TrapPread64Policy : public Policy {};

// pread(2) takes a 64 bits offset. On 32 bits systems, it will be split
// between two arguments. In this test, we make sure that ForwardSyscall() can
// forward it properly.
BPF_TEST_C(SandboxBPF, Pread64, TrapPread64Policy) {}

#endif  // !BUILDFLAG(IS_ANDROID)

void* TsyncApplyToTwoThreadsFunc(void* cond_ptr) {}

SANDBOX_TEST(SandboxBPF, Tsync) {}

class AllowAllPolicy : public Policy {};

SANDBOX_DEATH_TEST(
    SandboxBPF,
    StartMultiThreadedAsSingleThreaded,
    DEATH_MESSAGE(
        ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests())) {}

// A stub handler for the UnsafeTrap. Never called.
intptr_t NoOpHandler(const struct arch_seccomp_data& args, void*) {}

class UnsafeTrapWithCondPolicy : public Policy {};

BPF_TEST_C(SandboxBPF, UnsafeTrapWithCond, UnsafeTrapWithCondPolicy) {

}  // namespace

}  // namespace bpf_dsl
}  // namespace sandbox