blob: 1422b5918d9a2d875bd5366a30f27a365995ac16 [file] [log] [blame]
// Copyright (c) 2012 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 <ostream>
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/verifier.h"
#include "testing/gtest/include/gtest/gtest.h"
using namespace playground2;
namespace {
const int kExpectedReturnValue = 42;
#if defined(__arm__)
const int kArmPublicSysnoCeiling = __NR_SYSCALL_BASE + 1024;
#endif
// This test should execute no matter whether we have kernel support. So,
// we make it a TEST() instead of a BPF_TEST().
TEST(SandboxBpf, CallSupports) {
// We check that we don't crash, but it's ok if the kernel doesn't
// support it.
bool seccomp_bpf_supported =
Sandbox::supportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE;
// We want to log whether or not seccomp BPF is actually supported
// since actual test coverage depends on it.
RecordProperty("SeccompBPFSupported",
seccomp_bpf_supported ? "true." : "false.");
std::cout << "Seccomp BPF supported: "
<< (seccomp_bpf_supported ? "true." : "false.")
<< "\n";
}
SANDBOX_TEST(SandboxBpf, CallSupportsTwice) {
Sandbox::supportsSeccompSandbox(-1);
Sandbox::supportsSeccompSandbox(-1);
}
// A simple blacklist test
ErrorCode BlacklistNanosleepPolicy(int sysno) {
if (sysno < static_cast<int>(MIN_SYSCALL) ||
sysno > static_cast<int>(MAX_SYSCALL)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_nanosleep:
return ErrorCode(EACCES);
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
// nanosleep() should be denied
const struct timespec ts = {0, 0};
errno = 0;
BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1);
BPF_ASSERT(errno == EACCES);
}
// Now do a simple whitelist test
ErrorCode WhitelistGetpidPolicy(int sysno) {
switch (sysno) {
case __NR_getpid:
case __NR_exit_group:
return ErrorCode(ErrorCode::ERR_ALLOWED);
default:
return ErrorCode(ENOMEM);
}
}
BPF_TEST(SandboxBpf, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
// getpid() should be allowed
errno = 0;
BPF_ASSERT(syscall(__NR_getpid) > 0);
BPF_ASSERT(errno == 0);
// getpgid() should be denied
BPF_ASSERT(getpgid(0) == -1);
BPF_ASSERT(errno == ENOMEM);
}
// A simple blacklist policy, with a SIGSYS handler
// TODO: provide an API to provide the auxiliary data pointer
// to the evaluator
static int BlacklistNanosleepPolicySigsysAuxData;
intptr_t EnomemHandler(const struct arch_seccomp_data& args, void *aux) {
// We also check that the auxiliary data is correct
SANDBOX_ASSERT(aux);
*(static_cast<int*>(aux)) = kExpectedReturnValue;
return -ENOMEM;
}
ErrorCode BlacklistNanosleepPolicySigsys(int sysno) {
if (sysno < static_cast<int>(MIN_SYSCALL) ||
sysno > static_cast<int>(MAX_SYSCALL)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
}
switch (sysno) {
case __NR_nanosleep:
return Sandbox::Trap(EnomemHandler,
static_cast<void *>(&BlacklistNanosleepPolicySigsysAuxData));
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys,
BlacklistNanosleepPolicySigsys) {
// getpid() should work properly
errno = 0;
BPF_ASSERT(syscall(__NR_getpid) > 0);
BPF_ASSERT(errno == 0);
// Our Auxiliary Data, should be reset by the signal handler
BlacklistNanosleepPolicySigsysAuxData = -1;
const struct timespec ts = {0, 0};
BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1);
BPF_ASSERT(errno == ENOMEM);
// We expect the signal handler to modify AuxData
BPF_ASSERT(
BlacklistNanosleepPolicySigsysAuxData == kExpectedReturnValue);
}
// 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) {
// Small contiguous sets of 3 system calls return an errno equal to the
// index of that set + 1 (so that we never return a NUL errno).
return ((sysno & ~3) >> 2) % 29 + 1;
}
ErrorCode SyntheticPolicy(int sysno) {
if (sysno < static_cast<int>(MIN_SYSCALL) ||
sysno > static_cast<int>(MAX_SYSCALL)) {
// FIXME: we should really not have to do that in a trivial policy.
return ErrorCode(ENOSYS);
}
// TODO(jorgelo): remove this restriction once crbug.com/141694 is fixed.
#if defined(__arm__)
if (sysno > kArmPublicSysnoCeiling)
return ErrorCode(ENOSYS);
#endif
// TODO(markus): allow calls to write(). This should start working as soon
// as we switch to the new code generator. Currently we are blowing up,
// because our jumptable is getting too big.
if (sysno == __NR_exit_group /* || sysno == __NR_write */) {
// exit_group() is special, we really need it to work.
// write() is needed for BPF_ASSERT() to report a useful error message.
return ErrorCode(ErrorCode::ERR_ALLOWED);
} else {
return ErrorCode(SysnoToRandomErrno(sysno));
}
}
BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) {
// Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int
// overflow.
BPF_ASSERT(
std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
static_cast<int>(MAX_SYSCALL));
// TODO(jorgelo): remove this limit once crbug.com/141694 is fixed.
#if defined(__arm__)
const int sysno_ceiling = kArmPublicSysnoCeiling;
#else
const int sysno_ceiling = static_cast<int>(MAX_SYSCALL);
#endif
for (int syscall_number = static_cast<int>(MIN_SYSCALL);
syscall_number <= sysno_ceiling;
++syscall_number) {
if (syscall_number == __NR_exit_group ||
syscall_number == __NR_write) {
// exit_group() is special
continue;
}
errno = 0;
BPF_ASSERT(syscall(syscall_number) == -1);
BPF_ASSERT(errno == SysnoToRandomErrno(syscall_number));
}
}
} // namespace