Commit d84f4fc6 authored by Matthew Dempsky's avatar Matthew Dempsky

sandbox: Convert remaining legacy tests to use policy classes

BUG=414363
R=jln@chromium.org

Review URL: https://codereview.chromium.org/588143007

Cr-Commit-Position: refs/heads/master@{#296212}
parent aaa3b662
......@@ -37,6 +37,7 @@
#include "base/time/time.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/linux_syscalls.h"
#include "third_party/lss/linux_syscall_support.h" // for MAKE_PROCESS_CPUCLOCK
......
......@@ -153,7 +153,6 @@ component("seccomp_bpf") {
"seccomp-bpf/linux_seccomp.h",
"seccomp-bpf/sandbox_bpf.cc",
"seccomp-bpf/sandbox_bpf.h",
"seccomp-bpf/sandbox_bpf_compatibility_policy.h",
"seccomp-bpf/sandbox_bpf_policy.cc",
"seccomp-bpf/sandbox_bpf_policy.h",
"seccomp-bpf/syscall.cc",
......
......@@ -5,6 +5,7 @@
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
......@@ -34,6 +35,7 @@
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/linux_seccomp.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/broker_process.h"
......@@ -107,7 +109,8 @@ intptr_t IncreaseCounter(const struct arch_seccomp_data& args, void* aux) {
class VerboseAPITestingPolicy : public SandboxBPFDSLPolicy {
public:
VerboseAPITestingPolicy(int* counter_ptr) : counter_ptr_(counter_ptr) {}
explicit VerboseAPITestingPolicy(int* counter_ptr)
: counter_ptr_(counter_ptr) {}
virtual ~VerboseAPITestingPolicy() {}
virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
......@@ -214,21 +217,30 @@ intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) {
return -ENOMEM;
}
ErrorCode BlacklistNanosleepPolicySigsys(SandboxBPF* sandbox,
int sysno,
int* aux) {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
switch (sysno) {
case __NR_nanosleep:
return sandbox->Trap(EnomemHandler, aux);
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
class BlacklistNanosleepTrapPolicy : public SandboxBPFDSLPolicy {
public:
explicit BlacklistNanosleepTrapPolicy(int* aux) : aux_(aux) {}
virtual ~BlacklistNanosleepTrapPolicy() {}
virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
switch (sysno) {
case __NR_nanosleep:
return Trap(EnomemHandler, aux_);
default:
return Allow();
}
}
}
private:
int* aux_;
DISALLOW_COPY_AND_ASSIGN(BlacklistNanosleepTrapPolicy);
};
BPF_TEST(SandboxBPF,
BasicBlacklistWithSigsys,
BlacklistNanosleepPolicySigsys,
BlacklistNanosleepTrapPolicy,
int /* (*BPF_AUX) */) {
// getpid() should work properly
errno = 0;
......@@ -505,26 +517,34 @@ intptr_t CountSyscalls(const struct arch_seccomp_data& args, void* aux) {
return SandboxBPF::ForwardSyscall(args);
}
ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, int* aux) {
// Set the global environment for unsafe traps once.
if (sysno == MIN_SYSCALL) {
class GreyListedPolicy : public SandboxBPFDSLPolicy {
public:
explicit GreyListedPolicy(int* aux) : aux_(aux) {
// Set the global environment for unsafe traps once.
EnableUnsafeTraps();
}
virtual ~GreyListedPolicy() {}
// Some system calls must always be allowed, if our policy wants to make
// use of UnsafeTrap()
if (SandboxBPF::IsRequiredForUnsafeTrap(sysno)) {
return ErrorCode(ErrorCode::ERR_ALLOWED);
} else if (sysno == __NR_getpid) {
// Disallow getpid()
return ErrorCode(EPERM);
} else if (SandboxBPF::IsValidSyscallNumber(sysno)) {
// Allow (and count) all other system calls.
return sandbox->UnsafeTrap(CountSyscalls, aux);
} else {
return ErrorCode(ENOSYS);
virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
// Some system calls must always be allowed, if our policy wants to make
// use of UnsafeTrap()
if (SandboxBPF::IsRequiredForUnsafeTrap(sysno)) {
return Allow();
} else if (sysno == __NR_getpid) {
// Disallow getpid()
return Error(EPERM);
} else {
// Allow (and count) all other system calls.
return UnsafeTrap(CountSyscalls, aux_);
}
}
}
private:
int* aux_;
DISALLOW_COPY_AND_ASSIGN(GreyListedPolicy);
};
BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* (*BPF_AUX) */) {
BPF_ASSERT(syscall(__NR_getpid) == -1);
......@@ -784,30 +804,36 @@ intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
}
}
ErrorCode DenyOpenPolicy(SandboxBPF* sandbox,
int sysno,
InitializedOpenBroker* iob) {
if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
}
class DenyOpenPolicy : public SandboxBPFDSLPolicy {
public:
explicit DenyOpenPolicy(InitializedOpenBroker* iob) : iob_(iob) {}
virtual ~DenyOpenPolicy() {}
switch (sysno) {
case __NR_faccessat:
virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
switch (sysno) {
case __NR_faccessat:
#if defined(__NR_access)
case __NR_access:
case __NR_access:
#endif
#if defined(__NR_open)
case __NR_open:
case __NR_open:
#endif
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
// the BrokerProcess object.
return ErrorCode(
sandbox->Trap(BrokerOpenTrapHandler, iob->broker_process()));
default:
return ErrorCode(ErrorCode::ERR_ALLOWED);
case __NR_openat:
// We get a InitializedOpenBroker class, but our trap handler wants
// the BrokerProcess object.
return Trap(BrokerOpenTrapHandler, iob_->broker_process());
default:
return Allow();
}
}
}
private:
InitializedOpenBroker* iob_;
DISALLOW_COPY_AND_ASSIGN(DenyOpenPolicy);
};
// We use a InitializedOpenBroker class, so that we can run unsandboxed
// code in its constructor, which is the only way to do so in a BPF_TEST.
......@@ -956,20 +982,18 @@ class EqualityStressTest {
}
}
ErrorCode Policy(SandboxBPF* sandbox, int sysno) {
if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
// FIXME: we should really not have to do that in a trivial policy
return ErrorCode(ENOSYS);
} else if (sysno < 0 || sysno >= (int)arg_values_.size() ||
IsReservedSyscall(sysno)) {
ResultExpr Policy(int sysno) {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
if (sysno < 0 || sysno >= (int)arg_values_.size() ||
IsReservedSyscall(sysno)) {
// We only return ErrorCode values for the system calls that
// are part of our test data. Every other system call remains
// allowed.
return ErrorCode(ErrorCode::ERR_ALLOWED);
return Allow();
} else {
// ToErrorCode() turns an ArgValue object into an ErrorCode that is
// suitable for use by a sandbox policy.
return ToErrorCode(sandbox, arg_values_[sysno]);
return ToErrorCode(arg_values_[sysno]);
}
}
......@@ -1116,42 +1140,38 @@ class EqualityStressTest {
}
}
ErrorCode ToErrorCode(SandboxBPF* sandbox, ArgValue* arg_value) {
// Compute the ErrorCode that should be returned, if none of our
ResultExpr ToErrorCode(ArgValue* arg_value) {
// Compute the ResultExpr that should be returned, if none of our
// tests succeed (i.e. the system call parameter doesn't match any
// of the values in arg_value->tests[].k_value).
ErrorCode err;
ResultExpr err;
if (arg_value->err) {
// If this was a leaf node, return the errno value that we expect to
// return from the BPF filter program.
err = ErrorCode(arg_value->err);
err = Error(arg_value->err);
} else {
// If this wasn't a leaf node yet, recursively descend into the rest
// of the tree. This will end up adding a few more SandboxBPF::Cond()
// tests to our ErrorCode.
err = ToErrorCode(sandbox, arg_value->arg_value);
err = ToErrorCode(arg_value->arg_value);
}
// Now, iterate over all the test cases that we want to compare against.
// This builds a chain of SandboxBPF::Cond() tests
// (aka "if ... elif ... elif ... elif ... fi")
for (int n = arg_value->size; n-- > 0;) {
ErrorCode matched;
ResultExpr matched;
// Again, we distinguish between leaf nodes and subtrees.
if (arg_value->tests[n].err) {
matched = ErrorCode(arg_value->tests[n].err);
matched = Error(arg_value->tests[n].err);
} else {
matched = ToErrorCode(sandbox, arg_value->tests[n].arg_value);
matched = ToErrorCode(arg_value->tests[n].arg_value);
}
// For now, all of our tests are limited to 32bit.
// We have separate tests that check the behavior of 32bit vs. 64bit
// conditional expressions.
err = sandbox->Cond(arg_value->argno,
ErrorCode::TP_32BIT,
ErrorCode::OP_EQUAL,
arg_value->tests[n].k_value,
matched,
err);
const Arg<uint32_t> arg(arg_value->argno);
err = If(arg == arg_value->tests[n].k_value, matched).Else(err);
}
return err;
}
......@@ -1221,12 +1241,20 @@ class EqualityStressTest {
static const int kMaxArgs = 6;
};
ErrorCode EqualityStressTestPolicy(SandboxBPF* sandbox,
int sysno,
EqualityStressTest* aux) {
DCHECK(aux);
return aux->Policy(sandbox, sysno);
}
class EqualityStressTestPolicy : public SandboxBPFDSLPolicy {
public:
explicit EqualityStressTestPolicy(EqualityStressTest* aux) : aux_(aux) {}
virtual ~EqualityStressTestPolicy() {}
virtual ResultExpr EvaluateSyscall(int sysno) const OVERRIDE {
return aux_->Policy(sysno);
}
private:
EqualityStressTest* aux_;
DISALLOW_COPY_AND_ASSIGN(EqualityStressTestPolicy);
};
BPF_TEST(SandboxBPF,
EqualityTests,
......
......@@ -5,6 +5,7 @@
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/syscall.h>
......
......@@ -133,7 +133,6 @@
'seccomp-bpf/linux_seccomp.h',
'seccomp-bpf/sandbox_bpf.cc',
'seccomp-bpf/sandbox_bpf.h',
'seccomp-bpf/sandbox_bpf_compatibility_policy.h',
'seccomp-bpf/sandbox_bpf_policy.cc',
'seccomp-bpf/sandbox_bpf_policy.h',
'seccomp-bpf/syscall.cc',
......
......@@ -5,6 +5,7 @@
#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/futex.h>
#include <sched.h>
#include <signal.h>
......
......@@ -5,15 +5,8 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "base/memory/scoped_ptr.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
#include "sandbox/linux/tests/sandbox_test_runner.h"
#include "sandbox/linux/tests/unit_tests.h"
namespace sandbox {
......@@ -24,16 +17,13 @@ namespace sandbox {
// This allows both the policy and the test function to take a pointer to an
// object of type "Aux" as a parameter. This is used to implement the BPF_TEST
// macro and should generally not be used directly.
template <class Aux>
template <class Policy, class Aux>
class BPFTesterCompatibilityDelegate : public BPFTesterDelegate {
public:
typedef Aux AuxType;
BPFTesterCompatibilityDelegate(
void (*test_function)(AuxType*),
typename CompatibilityPolicy<AuxType>::SyscallEvaluator policy_function)
: aux_(),
test_function_(test_function),
policy_function_(policy_function) {}
typedef void (*TestFunction)(Aux*);
explicit BPFTesterCompatibilityDelegate(TestFunction test_function)
: aux_(), test_function_(test_function) {}
virtual ~BPFTesterCompatibilityDelegate() {}
......@@ -42,8 +32,7 @@ class BPFTesterCompatibilityDelegate : public BPFTesterDelegate {
// running the test. In this process, the current object is guaranteed
// to live forever. So it's ok to pass aux_pointer_for_policy_ to
// the policy, which could in turn pass it to the kernel via Trap().
return scoped_ptr<SandboxBPFPolicy>(
new CompatibilityPolicy<AuxType>(policy_function_, &aux_));
return scoped_ptr<SandboxBPFPolicy>(new Policy(&aux_));
}
virtual void RunTestFunction() OVERRIDE {
......@@ -54,9 +43,9 @@ class BPFTesterCompatibilityDelegate : public BPFTesterDelegate {
}
private:
AuxType aux_;
void (*test_function_)(AuxType*);
typename CompatibilityPolicy<AuxType>::SyscallEvaluator policy_function_;
Aux aux_;
TestFunction test_function_;
DISALLOW_COPY_AND_ASSIGN(BPFTesterCompatibilityDelegate);
};
......
......@@ -5,6 +5,7 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
#include "base/logging.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h"
......@@ -66,34 +67,31 @@ namespace sandbox {
// This form of BPF_TEST is now discouraged (but still allowed) in favor of
// BPF_TEST_D and BPF_TEST_C.
// The |policy| parameter should be a SyscallEvaluator function pointer
// (which is now a deprecated way of expressing policies).
// BPF_TEST() takes a C++ data type as an optional fourth parameter. If
// present, this sets up a variable that can be accessed as "BPF_AUX". This
// variable will be passed as an argument to the "policy" function. Policies
// would typically use it as an argument to SandboxBPF::Trap(), if they want to
// communicate data between the BPF_TEST() and a Trap() function. The life-time
// of this object is the same as the life-time of the process running under the
// seccomp-bpf policy.
// The type specified in |aux| and the last parameter of the policy function
// must be compatible. |aux| must not be void.
// The |policy| parameter should be a SandboxBPFPolicy subclass.
// BPF_TEST() takes a C++ data type as an fourth parameter. A variable
// of this type will be allocated and a pointer to it will be
// available within the test function as "BPF_AUX". The pointer will
// also be passed as an argument to the policy's constructor. Policies
// would typically use it as an argument to SandboxBPF::Trap(), if
// they want to communicate data between the BPF_TEST() and a Trap()
// function. The life-time of this object is the same as the life-time
// of the process running under the seccomp-bpf policy.
// |aux| must not be void.
#define BPF_TEST(test_case_name, test_name, policy, aux) \
BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux)
// A BPF_DEATH_TEST is just the same as a BPF_TEST, but it assumes that the
// test will fail with a particular known error condition. Use the DEATH_XXX()
// macros from unit_tests.h to specify the expected error condition.
#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux) \
void BPF_TEST_##test_name( \
sandbox::BPFTesterCompatibilityDelegate<aux>::AuxType* BPF_AUX); \
TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
sandbox::SandboxBPFTestRunner bpf_test_runner( \
new sandbox::BPFTesterCompatibilityDelegate<aux>(BPF_TEST_##test_name, \
policy)); \
sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
} \
void BPF_TEST_##test_name( \
sandbox::BPFTesterCompatibilityDelegate<aux>::AuxType* BPF_AUX)
#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux) \
void BPF_TEST_##test_name(aux* BPF_AUX); \
TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
sandbox::SandboxBPFTestRunner bpf_test_runner( \
new sandbox::BPFTesterCompatibilityDelegate<policy, aux>( \
BPF_TEST_##test_name)); \
sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
} \
void BPF_TEST_##test_name(aux* BPF_AUX)
// This class takes a simple function pointer as a constructor parameter and a
// class name as a template parameter to implement the BPFTesterDelegate
......
......@@ -33,22 +33,24 @@ class FourtyTwo {
DISALLOW_COPY_AND_ASSIGN(FourtyTwo);
};
ErrorCode EmptyPolicyTakesClass(SandboxBPF* sandbox,
int sysno,
FourtyTwo* fourty_two) {
// |aux| should point to an instance of FourtyTwo.
BPF_ASSERT(fourty_two);
BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
} else {
class EmptyClassTakingPolicy : public SandboxBPFPolicy {
public:
explicit EmptyClassTakingPolicy(FourtyTwo* fourty_two) {
BPF_ASSERT(fourty_two);
BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
}
virtual ~EmptyClassTakingPolicy() {}
virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
int sysno) const OVERRIDE {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
};
BPF_TEST(BPFTest,
BPFAUXPointsToClass,
EmptyPolicyTakesClass,
EmptyClassTakingPolicy,
FourtyTwo /* *BPF_AUX */) {
// BPF_AUX should point to an instance of FourtyTwo.
BPF_ASSERT(BPF_AUX);
......@@ -62,14 +64,14 @@ TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) {
// Don't do anything, simply gives dynamic tools an opportunity to detect
// leaks.
{
BPFTesterCompatibilityDelegate<FourtyTwo> simple_delegate(
DummyTestFunction, EmptyPolicyTakesClass);
BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>
simple_delegate(DummyTestFunction);
}
{
// Test polymorphism.
scoped_ptr<BPFTesterDelegate> simple_delegate(
new BPFTesterCompatibilityDelegate<FourtyTwo>(DummyTestFunction,
EmptyPolicyTakesClass));
new BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>(
DummyTestFunction));
}
}
......
// 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.
#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_COMPATIBILITY_POLICY_H_
#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_COMPATIBILITY_POLICY_H_
#include "base/logging.h"
#include "base/macros.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
namespace sandbox {
// This class allows compatibility with the old, deprecated
// policies that were designed for SetSandboxPolicyDeprecated().
template <class AuxType>
class CompatibilityPolicy : public SandboxBPFPolicy {
public:
typedef ErrorCode (*SyscallEvaluator)(SandboxBPF* sandbox_compiler,
int system_call_number,
AuxType* aux);
CompatibilityPolicy(SyscallEvaluator syscall_evaluator, AuxType* aux)
: syscall_evaluator_(syscall_evaluator), aux_(aux) {}
virtual ~CompatibilityPolicy() {}
virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
int system_call_number) const OVERRIDE {
DCHECK(SandboxBPF::IsValidSyscallNumber(system_call_number));
return syscall_evaluator_(sandbox_compiler, system_call_number, aux_);
}
private:
SyscallEvaluator syscall_evaluator_;
AuxType* aux_;
DISALLOW_COPY_AND_ASSIGN(CompatibilityPolicy);
};
} // namespace sandbox
#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_COMPATIBILITY_POLICY_H_
......@@ -99,18 +99,26 @@ intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
return -ENOMEM;
}
ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox,
int sysno,
std::vector<uint64_t>* aux) {
if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
return ErrorCode(ENOSYS);
class CopyAllArgsOnUnamePolicy : public SandboxBPFPolicy {
public:
explicit CopyAllArgsOnUnamePolicy(std::vector<uint64_t>* aux) : aux_(aux) {}
virtual ~CopyAllArgsOnUnamePolicy() {}
virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox,
int sysno) const OVERRIDE {
DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
if (sysno == __NR_uname) {
return sandbox->Trap(CopySyscallArgsToAux, aux_);
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
if (sysno == __NR_uname) {
return sandbox->Trap(CopySyscallArgsToAux, aux);
} else {
return ErrorCode(ErrorCode::ERR_ALLOWED);
}
}
private:
std::vector<uint64_t>* aux_;
DISALLOW_COPY_AND_ASSIGN(CopyAllArgsOnUnamePolicy);
};
// We are testing Syscall::Call() by making use of a BPF filter that
// allows us
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment