diff options
author | Torne (Richard Coles) <torne@google.com> | 2014-05-14 12:12:37 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2014-05-14 12:12:37 +0100 |
commit | 010d83a9304c5a91596085d917d248abff47903a (patch) | |
tree | 41ef1a01862f352f9653c7a9cfa817abefe2cce2 /sandbox | |
parent | 08c107de54178bb0990a09adec724924e8bc9486 (diff) | |
download | chromium_org-010d83a9304c5a91596085d917d248abff47903a.tar.gz |
Merge from Chromium at DEPS revision 269336
This commit was generated by merge_to_master.py.
Change-Id: I8b9c77f10eccd2a8b4c7ce373ffda18568af54ff
Diffstat (limited to 'sandbox')
52 files changed, 940 insertions, 263 deletions
diff --git a/sandbox/linux/DEPS b/sandbox/linux/DEPS new file mode 100644 index 0000000000..5c5f476b00 --- /dev/null +++ b/sandbox/linux/DEPS @@ -0,0 +1,25 @@ +include_rules = [ + # First, exclude everything. + # Exclude a few dependencies that are included in the root DEPS and that we + # don't need. + # Sadly, there is no way to exclude all root DEPS since the root has no name. + "-ipc", + "-library_loaders", + "-third_party", + "-url", + # Make sure that each subdirectory has to declare its dependencies in + # sandbox/ explicitly. + "-sandbox/linux", + + # Second, add what we want to allow. + # Anything included from sandbox/linux must be declared after this line or in + # a more specific DEPS file. + # base/, build/ and testing/ are already included in the global DEPS file, + # but be explicit. + "+base", + "+build", + "+testing", + "+sandbox/linux/sandbox_export.h", + # Everyone can use tests/ + "+sandbox/linux/tests", +] diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi index 0cccd2e9eb..e86345ee60 100644 --- a/sandbox/linux/sandbox_linux.gypi +++ b/sandbox/linux/sandbox_linux.gypi @@ -66,11 +66,28 @@ '../..', ], 'sources': [ + 'tests/sandbox_test_runner.h', + 'tests/sandbox_test_runner_function_pointer.cc', + 'tests/sandbox_test_runner_function_pointer.h', 'tests/test_utils.cc', 'tests/test_utils.h', 'tests/unit_tests.cc', 'tests/unit_tests.h', ], + 'conditions': [ + [ 'use_seccomp_bpf==1', { + 'sources': [ + 'seccomp-bpf/bpf_tester_compatibility_delegate.cc', + 'seccomp-bpf/bpf_tester_compatibility_delegate.h', + 'seccomp-bpf/bpf_tests.h', + 'seccomp-bpf/sandbox_bpf_test_runner.cc', + 'seccomp-bpf/sandbox_bpf_test_runner.h', + ], + 'dependencies': [ + 'seccomp_bpf', + ] + }], + ], }, { # The main sandboxing test target. @@ -112,6 +129,7 @@ '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.h', 'seccomp-bpf/syscall.cc', 'seccomp-bpf/syscall.h', diff --git a/sandbox/linux/sandbox_linux_test_sources.gypi b/sandbox/linux/sandbox_linux_test_sources.gypi index 9764830dcc..bf41471213 100644 --- a/sandbox/linux/sandbox_linux_test_sources.gypi +++ b/sandbox/linux/sandbox_linux_test_sources.gypi @@ -31,7 +31,8 @@ }], [ 'use_seccomp_bpf==1', { 'sources': [ - 'seccomp-bpf/bpf_tests.h', + 'seccomp-bpf-helpers/baseline_policy_unittest.cc', + 'seccomp-bpf/bpf_tests_unittest.cc', 'seccomp-bpf/codegen_unittest.cc', 'seccomp-bpf/errorcode_unittest.cc', 'seccomp-bpf/sandbox_bpf_unittest.cc', diff --git a/sandbox/linux/seccomp-bpf-helpers/DEPS b/sandbox/linux/seccomp-bpf-helpers/DEPS new file mode 100644 index 0000000000..e8000d3b32 --- /dev/null +++ b/sandbox/linux/seccomp-bpf-helpers/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+sandbox/linux/services", + "+sandbox/linux/seccomp-bpf", +] diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc index 79b5b023da..217bdac679 100644 --- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc +++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc @@ -97,6 +97,10 @@ ErrorCode EvaluateSyscallImpl(int fs_denied_errno, return ErrorCode(ErrorCode::ERR_ALLOWED); } + if (sysno == __NR_clone) { + return RestrictCloneToThreadsAndEPERMFork(sandbox); + } + #if defined(__x86_64__) || defined(__arm__) if (sysno == __NR_socketpair) { // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen. diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc new file mode 100644 index 0000000000..9182e076e8 --- /dev/null +++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc @@ -0,0 +1,115 @@ +// 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 <errno.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.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/services/linux_syscalls.h" +#include "sandbox/linux/services/thread_helpers.h" +#include "sandbox/linux/tests/unit_tests.h" + +namespace sandbox { + +namespace { + +// |pid| is the return value of a fork()-like call. This +// makes sure that if fork() succeeded the child exits +// and the parent waits for it. +void HandlePostForkReturn(pid_t pid) { + const int kChildExitCode = 1; + if (pid > 0) { + int status = 0; + PCHECK(pid == HANDLE_EINTR(waitpid(pid, &status, 0))); + CHECK(WIFEXITED(status)); + CHECK_EQ(kChildExitCode, WEXITSTATUS(status)); + } else if (pid == 0) { + _exit(kChildExitCode); + } +} + +// Check that HandlePostForkReturn works. +TEST(BaselinePolicy, HandlePostForkReturn) { + pid_t pid = fork(); + HandlePostForkReturn(pid); +} + +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); +} + +// TODO(jln): make this work with the sanitizers. +#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) + +BPF_TEST_C(BaselinePolicy, ForkErrno, BaselinePolicy) { + errno = 0; + pid_t pid = fork(); + const int fork_errno = errno; + HandlePostForkReturn(pid); + + BPF_ASSERT_EQ(-1, pid); + BPF_ASSERT_EQ(EPERM, fork_errno); +} + +pid_t ForkX86Glibc() { + return syscall(__NR_clone, CLONE_PARENT_SETTID | SIGCHLD); +} + +BPF_TEST_C(BaselinePolicy, ForkX86Eperm, BaselinePolicy) { + errno = 0; + pid_t pid = ForkX86Glibc(); + const int fork_errno = errno; + HandlePostForkReturn(pid); + + BPF_ASSERT_EQ(-1, pid); + BPF_ASSERT_EQ(EPERM, fork_errno); +} + +pid_t ForkARMGlibc() { + return syscall(__NR_clone, + CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD); +} + +BPF_TEST_C(BaselinePolicy, ForkArmEperm, BaselinePolicy) { + errno = 0; + pid_t pid = ForkARMGlibc(); + const int fork_errno = errno; + 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_MESSAGE(GetCloneErrorMessageContentForTests()), + BaselinePolicy) { + pid_t pid = syscall(__NR_clone, CLONE_THREAD | SIGCHLD); + HandlePostForkReturn(pid); +} + +#endif // !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) + +} // namespace + +} // namespace sandbox diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc index 2f4640130b..29c5910359 100644 --- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc @@ -19,6 +19,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "build/build_config.h" #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" @@ -51,25 +52,53 @@ inline bool IsArchitectureI386() { #endif } +inline bool IsAndroid() { +#if defined(OS_ANDROID) + return true; +#else + return false; +#endif +} + } // namespace. namespace sandbox { +// Allow Glibc's and Android pthread creation flags, crash on any other +// thread creation attempts and EPERM attempts to use neither +// CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations. ErrorCode RestrictCloneToThreadsAndEPERMFork(SandboxBPF* sandbox) { - // Glibc's pthread. - return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | - CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | - CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID, - ErrorCode(ErrorCode::ERR_ALLOWED), - sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - CLONE_PARENT_SETTID | SIGCHLD, - ErrorCode(EPERM), - // ARM - sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, - ErrorCode(EPERM), - sandbox->Trap(SIGSYSCloneFailure, NULL)))); + if (!IsAndroid()) { + const uint64_t kGlibcPthreadFlags = + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | + CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | + CLONE_CHILD_CLEARTID; + + return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + kGlibcPthreadFlags, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, + CLONE_VM | CLONE_THREAD, + sandbox->Trap(SIGSYSCloneFailure, NULL), + ErrorCode(EPERM))); + } else { + const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_THREAD | + CLONE_SYSVSEM; + const uint64_t kObsoleteAndroidCloneMask = + kAndroidCloneMask | CLONE_DETACHED; + + return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + kAndroidCloneMask, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, + kObsoleteAndroidCloneMask, + ErrorCode(ErrorCode::ERR_ALLOWED), + sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS, + CLONE_VM | CLONE_THREAD, + sandbox->Trap(SIGSYSCloneFailure, NULL), + ErrorCode(EPERM)))); + } } ErrorCode RestrictPrctl(SandboxBPF* sandbox) { diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc index 792807ac72..5f8785ea37 100644 --- a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc +++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc @@ -350,7 +350,6 @@ bool SyscallSets::IsKernelInternalApi(int sysno) { // This should be thought through in conjunction with IsFutex(). bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) { switch (sysno) { - case __NR_clone: // TODO(jln): restrict flags. case __NR_exit: case __NR_exit_group: case __NR_wait4: @@ -359,6 +358,7 @@ bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) { case __NR_waitpid: #endif return true; + case __NR_clone: // Should be parameter-restricted. case __NR_setns: // Privileged. case __NR_fork: #if defined(__i386__) || defined(__x86_64__) diff --git a/sandbox/linux/seccomp-bpf/DEPS b/sandbox/linux/seccomp-bpf/DEPS new file mode 100644 index 0000000000..15b2b36a3e --- /dev/null +++ b/sandbox/linux/seccomp-bpf/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+sandbox/linux/services" +] diff --git a/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.cc b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.cc new file mode 100644 index 0000000000..2fa209b4c7 --- /dev/null +++ b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.cc @@ -0,0 +1,21 @@ +// 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/bpf_tester_compatibility_delegate.h" + +namespace sandbox { + +// static +template <> +void* BPFTesterCompatibilityDelegate<void>::NewAux() { + return NULL; +} + +// static +template <> +void BPFTesterCompatibilityDelegate<void>::DeleteAux(void* aux) { + CHECK(!aux); +} + +} // namespace sandbox diff --git a/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h new file mode 100644 index 0000000000..c211d04343 --- /dev/null +++ b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h @@ -0,0 +1,81 @@ +// 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_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 { + +// This templated class allows building a BPFTesterDelegate from a +// deprecated-style BPF policy (that is a SyscallEvaluator function pointer, +// instead of a SandboxBPFPolicy class), specified in |policy_function| and a +// function pointer to a test in |test_function|. +// 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 = void> +class BPFTesterCompatibilityDelegate : public BPFTesterDelegate { + public: + typedef Aux AuxType; + BPFTesterCompatibilityDelegate( + void (*test_function)(AuxType*), + typename CompatibilityPolicy<AuxType>::SyscallEvaluator policy_function) + : aux_pointer_for_policy_(NULL), + test_function_(test_function), + policy_function_(policy_function) { + // This will be NULL iff AuxType is void. + aux_pointer_for_policy_ = NewAux(); + } + + virtual ~BPFTesterCompatibilityDelegate() { + DeleteAux(aux_pointer_for_policy_); + } + + virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() OVERRIDE { + // The current method is guaranteed to only run in the child process + // 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_pointer_for_policy_)); + } + + virtual void RunTestFunction() OVERRIDE { + // Run the actual test. + // The current object is guaranteed to live forever in the child process + // where this will run. + test_function_(aux_pointer_for_policy_); + } + + private: + // Allocate an object of type Aux. This is specialized to return NULL when + // trying to allocate a void. + static Aux* NewAux() { return new Aux(); } + static void DeleteAux(Aux* aux) { delete aux; } + + AuxType* aux_pointer_for_policy_; + void (*test_function_)(AuxType*); + typename CompatibilityPolicy<AuxType>::SyscallEvaluator policy_function_; + DISALLOW_COPY_AND_ASSIGN(BPFTesterCompatibilityDelegate); +}; + +// Specialization of NewAux that returns NULL; +template <> +void* BPFTesterCompatibilityDelegate<void>::NewAux(); +template <> +void BPFTesterCompatibilityDelegate<void>::DeleteAux(void* aux); + +} // namespace sandbox + +#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_ diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h index 357e29c731..8fa5579b34 100644 --- a/sandbox/linux/seccomp-bpf/bpf_tests.h +++ b/sandbox/linux/seccomp-bpf/bpf_tests.h @@ -5,42 +5,55 @@ #ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__ #define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__ -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> - +#include "base/basictypes.h" #include "build/build_config.h" +#include "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h" #include "sandbox/linux/tests/unit_tests.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" namespace sandbox { -// 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. -// A BPF_DEATH_TEST is always disabled under ThreadSanitizer, see -// crbug.com/243968. -#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux...) \ - void BPF_TEST_##test_name(sandbox::BPFTests<aux>::AuxType& BPF_AUX); \ - TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \ - sandbox::BPFTests<aux>::TestArgs arg(BPF_TEST_##test_name, policy); \ - sandbox::BPFTests<aux>::RunTestInProcess( \ - sandbox::BPFTests<aux>::TestWrapper, &arg, death); \ - } \ - void BPF_TEST_##test_name(sandbox::BPFTests<aux>::AuxType& BPF_AUX) - -// BPF_TEST() is a special version of SANDBOX_TEST(). It turns into a no-op, -// if the host does not have kernel support for running BPF filters. -// Also, it takes advantage of the Die class to avoid calling LOG(FATAL), from -// inside our tests, as we don't need or even want all the error handling that -// LOG(FATAL) would do. -// 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. -#define BPF_TEST(test_case_name, test_name, policy, aux...) \ - BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux) +// BPF_TEST_C() is a special version of SANDBOX_TEST(). It runs a test function +// in a sub-process, under a seccomp-bpf policy specified in +// |bpf_policy_class_name| without failing on configurations that are allowed +// to not support seccomp-bpf in their kernels. +// This is the preferred format for new BPF tests. |bpf_policy_class_name| is a +// class name (which will be default-constructed) that implements the +// SandboxBPFPolicy interface. +// The test function's body can simply follow. Test functions should use +// the BPF_ASSERT macros defined below, not GTEST's macros. The use of +// CHECK* macros is supported but less robust. +#define BPF_TEST_C(test_case_name, test_name, bpf_policy_class_name) \ + BPF_DEATH_TEST_C( \ + test_case_name, test_name, DEATH_SUCCESS(), bpf_policy_class_name) + +// Identical to BPF_TEST_C but allows to specify the nature of death. +#define BPF_DEATH_TEST_C( \ + test_case_name, test_name, death, bpf_policy_class_name) \ + void BPF_TEST_C_##test_name(); \ + TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \ + sandbox::SandboxBPFTestRunner bpf_test_runner( \ + new sandbox::BPFTesterSimpleDelegate<bpf_policy_class_name>( \ + BPF_TEST_C_##test_name)); \ + sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \ + } \ + void BPF_TEST_C_##test_name() + +// This form of BPF_TEST is a little verbose and should be reserved for complex +// tests where a lot of control is required. +// |bpf_tester_delegate_class| must be a classname implementing the +// BPFTesterDelegate interface. +#define BPF_TEST_D(test_case_name, test_name, bpf_tester_delegate_class) \ + BPF_DEATH_TEST_D( \ + test_case_name, test_name, DEATH_SUCCESS(), bpf_tester_delegate_class) + +// Identical to BPF_TEST_D but allows to specify the nature of death. +#define BPF_DEATH_TEST_D( \ + test_case_name, test_name, death, bpf_tester_delegate_class) \ + TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \ + sandbox::SandboxBPFTestRunner bpf_test_runner( \ + new bpf_tester_delegate_class()); \ + sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \ + } // Assertions are handled exactly the same as with a normal SANDBOX_TEST() #define BPF_ASSERT SANDBOX_ASSERT @@ -51,70 +64,61 @@ namespace sandbox { #define BPF_ASSERT_LE(x, y) BPF_ASSERT((x) <= (y)) #define BPF_ASSERT_GE(x, y) BPF_ASSERT((x) >= (y)) -// The "Aux" type is optional. We use an "empty" type by default, so that if -// the caller doesn't provide any type, all the BPF_AUX related data compiles -// to nothing. -template <class Aux = int[0]> -class BPFTests : public UnitTests { - public: - typedef Aux AuxType; - - class TestArgs { - public: - TestArgs(void (*t)(AuxType&), sandbox::SandboxBPF::EvaluateSyscall p) - : test_(t), policy_(p), aux_() {} - - void (*test() const)(AuxType&) { return test_; } - sandbox::SandboxBPF::EvaluateSyscall policy() const { return policy_; } - - private: - friend class BPFTests; - - void (*test_)(AuxType&); - sandbox::SandboxBPF::EvaluateSyscall policy_; - AuxType aux_; - }; - - static void TestWrapper(void* void_arg) { - TestArgs* arg = reinterpret_cast<TestArgs*>(void_arg); - sandbox::Die::EnableSimpleExit(); - if (sandbox::SandboxBPF::SupportsSeccompSandbox(-1) == - sandbox::SandboxBPF::STATUS_AVAILABLE) { - // Ensure the the sandbox is actually available at this time - int proc_fd; - BPF_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0); - BPF_ASSERT(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) == - sandbox::SandboxBPF::STATUS_AVAILABLE); +// 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. If |aux| is not specified, the policy function must +// take a void* as its last parameter (that is, must have the EvaluateSyscall +// type). +#define BPF_TEST(test_case_name, test_name, policy, aux...) \ + BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux) - // Initialize and then start the sandbox with our custom policy - sandbox::SandboxBPF sandbox; - sandbox.set_proc_fd(proc_fd); - sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_); - BPF_ASSERT(sandbox.StartSandbox( - sandbox::SandboxBPF::PROCESS_SINGLE_THREADED)); +// 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) + +// This class takes a simple function pointer as a constructor parameter and a +// class name as a template parameter to implement the BPFTesterDelegate +// interface which can be used to build BPF unittests with +// the SandboxBPFTestRunner class. +template <class PolicyClass> +class BPFTesterSimpleDelegate : public BPFTesterDelegate { + public: + explicit BPFTesterSimpleDelegate(void (*test_function)(void)) + : test_function_(test_function) {} + virtual ~BPFTesterSimpleDelegate() {} - arg->test()(arg->aux_); - } else { - printf("This BPF test is not fully running in this configuration!\n"); - // Android and Valgrind are the only configurations where we accept not - // having kernel BPF support. - if (!IsAndroid() && !IsRunningOnValgrind()) { - const bool seccomp_bpf_is_supported = false; - BPF_ASSERT(seccomp_bpf_is_supported); - } - // Call the compiler and verify the policy. That's the least we can do, - // if we don't have kernel support. - sandbox::SandboxBPF sandbox; - sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_); - sandbox::SandboxBPF::Program* program = - sandbox.AssembleFilter(true /* force_verification */); - delete program; - sandbox::UnitTests::IgnoreThisTest(); - } + virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() OVERRIDE { + return scoped_ptr<SandboxBPFPolicy>(new PolicyClass()); + } + virtual void RunTestFunction() OVERRIDE { + DCHECK(test_function_); + test_function_(); } private: - DISALLOW_IMPLICIT_CONSTRUCTORS(BPFTests); + void (*test_function_)(void); + DISALLOW_COPY_AND_ASSIGN(BPFTesterSimpleDelegate); }; } // namespace sandbox diff --git a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc new file mode 100644 index 0000000000..d83b8eda3e --- /dev/null +++ b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc @@ -0,0 +1,156 @@ +// 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/bpf_tests.h" + +#include <errno.h> +#include <sys/ptrace.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "build/build_config.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/services/linux_syscalls.h" +#include "sandbox/linux/tests/unit_tests.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +namespace { + +ErrorCode EmptyPolicy(SandboxBPF* sandbox, int sysno, void* aux) { + // |aux| should always be NULL since a type was not specified as an argument + // to BPF_TEST. + BPF_ASSERT(NULL == aux); + if (!SandboxBPF::IsValidSyscallNumber(sysno)) { + return ErrorCode(ENOSYS); + } else { + return ErrorCode(ErrorCode::ERR_ALLOWED); + } +} + +BPF_TEST(BPFTest, BPFAUXIsNull, EmptyPolicy) { + // Check that the implicit BPF_AUX argument is NULL when we + // don't specify a fourth parameter to BPF_TEST. + BPF_ASSERT(NULL == BPF_AUX); +} + +class FourtyTwo { + public: + static const int kMagicValue = 42; + FourtyTwo() : value_(kMagicValue) {} + int value() { return value_; } + + private: + int value_; + 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 { + return ErrorCode(ErrorCode::ERR_ALLOWED); + } +} + +BPF_TEST(BPFTest, + BPFAUXPointsToClass, + EmptyPolicyTakesClass, + FourtyTwo /* *BPF_AUX */) { + // BPF_AUX should point to an instance of FourtyTwo. + BPF_ASSERT(BPF_AUX); + BPF_ASSERT(FourtyTwo::kMagicValue == BPF_AUX->value()); +} + +void DummyTestFunction(FourtyTwo *fourty_two) { +} + +TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) { + // Don't do anything, simply gives dynamic tools an opportunity to detect + // leaks. + { + BPFTesterCompatibilityDelegate<FourtyTwo> simple_delegate( + DummyTestFunction, EmptyPolicyTakesClass); + } + { + // Test polymorphism. + scoped_ptr<BPFTesterDelegate> simple_delegate( + new BPFTesterCompatibilityDelegate<FourtyTwo>(DummyTestFunction, + EmptyPolicyTakesClass)); + } +} + +class EnosysPtracePolicy : public SandboxBPFPolicy { + public: + EnosysPtracePolicy() { + my_pid_ = syscall(__NR_getpid); + } + virtual ~EnosysPtracePolicy() { + // Policies should be able to bind with the process on which they are + // created. They should never be created in a parent process. + BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid)); + } + + virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, + int system_call_number) const OVERRIDE { + if (!SandboxBPF::IsValidSyscallNumber(system_call_number)) { + return ErrorCode(ENOSYS); + } else if (system_call_number == __NR_ptrace) { + // The EvaluateSyscall function should run in the process that created + // the current object. + BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid)); + return ErrorCode(ENOSYS); + } else { + return ErrorCode(ErrorCode::ERR_ALLOWED); + } + } + + private: + pid_t my_pid_; + DISALLOW_COPY_AND_ASSIGN(EnosysPtracePolicy); +}; + +class BasicBPFTesterDelegate : public BPFTesterDelegate { + public: + BasicBPFTesterDelegate() {} + virtual ~BasicBPFTesterDelegate() {} + + virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() OVERRIDE { + return scoped_ptr<SandboxBPFPolicy>(new EnosysPtracePolicy()); + } + virtual void RunTestFunction() OVERRIDE { + errno = 0; + int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL); + BPF_ASSERT(-1 == ret); + BPF_ASSERT(ENOSYS == errno); + } + + private: + DISALLOW_COPY_AND_ASSIGN(BasicBPFTesterDelegate); +}; + +// This is the most powerful and complex way to create a BPF test, but it +// requires a full class definition (BasicBPFTesterDelegate). +BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, BasicBPFTesterDelegate); + +// This is the simplest form of BPF tests. +BPF_TEST_C(BPFTest, BPFTestWithInlineTest, EnosysPtracePolicy) { + errno = 0; + int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL); + BPF_ASSERT(-1 == ret); + BPF_ASSERT(ENOSYS == errno); +} + +} // namespace + +} // namespace sandbox diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index 1538fe8347..497c343f28 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -25,6 +25,7 @@ #include "base/memory/scoped_ptr.h" #include "base/posix/eintr_wrapper.h" #include "sandbox/linux/seccomp-bpf/codegen.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" #include "sandbox/linux/seccomp-bpf/syscall.h" #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" @@ -202,25 +203,6 @@ intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) { SANDBOX_DIE(static_cast<char*>(aux)); } -// This class allows compatibility with the old, deprecated SetSandboxPolicy. -class CompatibilityPolicy : public SandboxBPFPolicy { - public: - CompatibilityPolicy(SandboxBPF::EvaluateSyscall syscall_evaluator, void* aux) - : syscall_evaluator_(syscall_evaluator), aux_(aux) { - DCHECK(syscall_evaluator_); - } - - virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, - int system_call_number) const OVERRIDE { - return syscall_evaluator_(sandbox_compiler, system_call_number, aux_); - } - - private: - SandboxBPF::EvaluateSyscall syscall_evaluator_; - void* aux_; - DISALLOW_COPY_AND_ASSIGN(CompatibilityPolicy); -}; - } // namespace SandboxBPF::SandboxBPF() @@ -498,7 +480,7 @@ void SandboxBPF::SetSandboxPolicyDeprecated(EvaluateSyscall syscall_evaluator, if (sandbox_has_started_ || !conds_) { SANDBOX_DIE("Cannot change policy after sandbox has started"); } - SetSandboxPolicy(new CompatibilityPolicy(syscall_evaluator, aux)); + SetSandboxPolicy(new CompatibilityPolicy<void>(syscall_evaluator, aux)); } // Don't take a scoped_ptr here, polymorphism make their use awkward. diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h index 67b84b9343..2391c5d7d3 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h @@ -73,7 +73,6 @@ class SANDBOX_EXPORT SandboxBPF { typedef ErrorCode (*EvaluateSyscall)(SandboxBPF* sandbox_compiler, int system_call_number, void* aux); - typedef std::vector<std::pair<EvaluateSyscall, void*> > Evaluators; // A vector of BPF instructions that need to be installed as a filter // program in the kernel. typedef std::vector<struct sock_filter> Program; diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h b/sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h new file mode 100644 index 0000000000..bd947ad521 --- /dev/null +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_compatibility_policy.h @@ -0,0 +1,42 @@ +// 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/basictypes.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 { + 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_ diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc new file mode 100644 index 0000000000..ade1d49d2b --- /dev/null +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc @@ -0,0 +1,70 @@ +// 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/sandbox_bpf_test_runner.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/tests/unit_tests.h" + +namespace sandbox { + +SandboxBPFTestRunner::SandboxBPFTestRunner( + BPFTesterDelegate* bpf_tester_delegate) + : bpf_tester_delegate_(bpf_tester_delegate) { +} + +SandboxBPFTestRunner::~SandboxBPFTestRunner() { +} + +void SandboxBPFTestRunner::Run() { + DCHECK(bpf_tester_delegate_); + sandbox::Die::EnableSimpleExit(); + + scoped_ptr<SandboxBPFPolicy> policy = + bpf_tester_delegate_->GetSandboxBPFPolicy(); + + if (sandbox::SandboxBPF::SupportsSeccompSandbox(-1) == + sandbox::SandboxBPF::STATUS_AVAILABLE) { + // Ensure the the sandbox is actually available at this time + int proc_fd; + SANDBOX_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0); + SANDBOX_ASSERT(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) == + sandbox::SandboxBPF::STATUS_AVAILABLE); + + // Initialize and then start the sandbox with our custom policy + sandbox::SandboxBPF sandbox; + sandbox.set_proc_fd(proc_fd); + sandbox.SetSandboxPolicy(policy.release()); + SANDBOX_ASSERT( + sandbox.StartSandbox(sandbox::SandboxBPF::PROCESS_SINGLE_THREADED)); + + // Run the actual test. + bpf_tester_delegate_->RunTestFunction(); + } else { + printf("This BPF test is not fully running in this configuration!\n"); + // Android and Valgrind are the only configurations where we accept not + // having kernel BPF support. + if (!IsAndroid() && !IsRunningOnValgrind()) { + const bool seccomp_bpf_is_supported = false; + SANDBOX_ASSERT(seccomp_bpf_is_supported); + } + // Call the compiler and verify the policy. That's the least we can do, + // if we don't have kernel support. + sandbox::SandboxBPF sandbox; + sandbox.SetSandboxPolicy(policy.release()); + sandbox::SandboxBPF::Program* program = + sandbox.AssembleFilter(true /* force_verification */); + delete program; + sandbox::UnitTests::IgnoreThisTest(); + } +} + +} // namespace sandbox diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h new file mode 100644 index 0000000000..c1beba2e60 --- /dev/null +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h @@ -0,0 +1,57 @@ +// 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_TEST_RUNNER_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" +#include "sandbox/linux/tests/sandbox_test_runner.h" + +namespace sandbox { + +// To create a SandboxBPFTestRunner object, one needs to implement this +// interface and pass an instance to the SandboxBPFTestRunner constructor. +// In the child process running the test, the BPFTesterDelegate object is +// guaranteed to not be destroyed until the child process terminates. +class BPFTesterDelegate { + public: + BPFTesterDelegate() {} + virtual ~BPFTesterDelegate() {} + + // This will instanciate a policy suitable for the test we want to run. It is + // guaranteed to only be called from the child process that will run the + // test. + virtual scoped_ptr<SandboxBPFPolicy> GetSandboxBPFPolicy() = 0; + // This will be called from a child process with the BPF sandbox turned on. + virtual void RunTestFunction() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(BPFTesterDelegate); +}; + +// This class implements the SandboxTestRunner interface and Run() will +// initialize a seccomp-bpf sandbox (specified by |bpf_tester_delegate|) and +// run a test function (via |bpf_tester_delegate|) if the current kernel +// configuration allows it. If it can not run the test under seccomp-bpf, +// Run() will still compile the policy which should allow to get some coverage +// under tools such as Valgrind. +class SandboxBPFTestRunner : public SandboxTestRunner { + public: + // This constructor takes ownership of the |bpf_tester_delegate| object. + // (It doesn't take a scoped_ptr since they make polymorphism verbose). + explicit SandboxBPFTestRunner(BPFTesterDelegate* bpf_tester_delegate); + virtual ~SandboxBPFTestRunner(); + + virtual void Run() OVERRIDE; + + private: + scoped_ptr<BPFTesterDelegate> bpf_tester_delegate_; + DISALLOW_COPY_AND_ASSIGN(SandboxBPFTestRunner); +}; + +} // namespace sandbox + +#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_ diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc index 5c5c6275e8..3b7470b417 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc @@ -21,6 +21,7 @@ #include <ostream> #include "base/bind.h" +#include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "build/build_config.h" #include "sandbox/linux/seccomp-bpf/bpf_tests.h" @@ -115,7 +116,10 @@ SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(VerboseAPITesting)) { // A simple blacklist test -ErrorCode BlacklistNanosleepPolicy(SandboxBPF*, int sysno, void*) { +ErrorCode BlacklistNanosleepPolicy(SandboxBPF*, int sysno, void* aux) { + // Since no type was specified in BPF_TEST as a fourth argument, + // |aux| must be NULL here. + BPF_ASSERT(NULL == aux); if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); @@ -136,7 +140,6 @@ BPF_TEST(SandboxBPF, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) { BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1); BPF_ASSERT(errno == EACCES); } - // Now do a simple whitelist test ErrorCode WhitelistGetpidPolicy(SandboxBPF*, int sysno, void*) { @@ -161,7 +164,6 @@ BPF_TEST(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) { } // A simple blacklist policy, with a SIGSYS handler - intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) { // We also check that the auxiliary data is correct SANDBOX_ASSERT(aux); @@ -171,7 +173,7 @@ intptr_t EnomemHandler(const struct arch_seccomp_data& args, void* aux) { ErrorCode BlacklistNanosleepPolicySigsys(SandboxBPF* sandbox, int sysno, - void* aux) { + int* aux) { if (!SandboxBPF::IsValidSyscallNumber(sysno)) { // FIXME: we should really not have to do that in a trivial policy return ErrorCode(ENOSYS); @@ -188,20 +190,20 @@ ErrorCode BlacklistNanosleepPolicySigsys(SandboxBPF* sandbox, BPF_TEST(SandboxBPF, BasicBlacklistWithSigsys, BlacklistNanosleepPolicySigsys, - int /* BPF_AUX */) { + int /* (*BPF_AUX) */) { // 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 - BPF_AUX = -1; + *BPF_AUX = -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(BPF_AUX == kExpectedReturnValue); + BPF_ASSERT(*BPF_AUX == kExpectedReturnValue); } // A simple test that verifies we can return arbitrary errno values. @@ -444,7 +446,7 @@ intptr_t CountSyscalls(const struct arch_seccomp_data& args, void* aux) { return SandboxBPF::ForwardSyscall(args); } -ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, void* aux) { +ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, int* aux) { // The use of UnsafeTrap() causes us to print a warning message. This is // generally desirable, but it results in the unittest failing, as it doesn't // expect any messages on "stderr". So, temporarily disable messages. The @@ -477,12 +479,12 @@ ErrorCode GreyListedPolicy(SandboxBPF* sandbox, int sysno, void* aux) { } } -BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* BPF_AUX */) { +BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* (*BPF_AUX) */) { BPF_ASSERT(syscall(__NR_getpid) == -1); BPF_ASSERT(errno == EPERM); - BPF_ASSERT(BPF_AUX == 0); + BPF_ASSERT(*BPF_AUX == 0); BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid)); - BPF_ASSERT(BPF_AUX == 2); + BPF_ASSERT(*BPF_AUX == 2); char name[17] = {}; BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, @@ -490,7 +492,7 @@ BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* BPF_AUX */) { (void*)NULL, (void*)NULL, (void*)NULL)); - BPF_ASSERT(BPF_AUX == 3); + BPF_ASSERT(*BPF_AUX == 3); BPF_ASSERT(*name); } @@ -724,8 +726,9 @@ intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args, } } -ErrorCode DenyOpenPolicy(SandboxBPF* sandbox, int sysno, void* aux) { - InitializedOpenBroker* iob = static_cast<InitializedOpenBroker*>(aux); +ErrorCode DenyOpenPolicy(SandboxBPF* sandbox, + int sysno, + InitializedOpenBroker* iob) { if (!SandboxBPF::IsValidSyscallNumber(sysno)) { return ErrorCode(ENOSYS); } @@ -752,9 +755,9 @@ ErrorCode DenyOpenPolicy(SandboxBPF* sandbox, int sysno, void* aux) { BPF_TEST(SandboxBPF, UseOpenBroker, DenyOpenPolicy, - InitializedOpenBroker /* BPF_AUX */) { - BPF_ASSERT(BPF_AUX.initialized()); - BrokerProcess* broker_process = BPF_AUX.broker_process(); + InitializedOpenBroker /* (*BPF_AUX) */) { + BPF_ASSERT(BPF_AUX->initialized()); + BrokerProcess* broker_process = BPF_AUX->broker_process(); BPF_ASSERT(broker_process != NULL); // First, use the broker "manually" @@ -1161,15 +1164,18 @@ class EqualityStressTest { static const int kMaxArgs = 6; }; -ErrorCode EqualityStressTestPolicy(SandboxBPF* sandbox, int sysno, void* aux) { - return reinterpret_cast<EqualityStressTest*>(aux)->Policy(sandbox, sysno); +ErrorCode EqualityStressTestPolicy(SandboxBPF* sandbox, + int sysno, + EqualityStressTest* aux) { + DCHECK(aux); + return aux->Policy(sandbox, sysno); } BPF_TEST(SandboxBPF, EqualityTests, EqualityStressTestPolicy, - EqualityStressTest /* BPF_AUX */) { - BPF_AUX.VerifyFilter(); + EqualityStressTest /* (*BPF_AUX) */) { + BPF_AUX->VerifyFilter(); } ErrorCode EqualityArgumentWidthPolicy(SandboxBPF* sandbox, int sysno, void*) { diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc index 60db69bcd6..bdeee4fe2d 100644 --- a/sandbox/linux/seccomp-bpf/syscall_unittest.cc +++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc @@ -70,7 +70,7 @@ TEST(Syscall, TrivialSyscallOneArg) { // SIGSYS trap handler that will be called on __NR_uname. intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) { - // |aux| is a pointer to our BPF_AUX. + // |aux| is our BPF_AUX pointer. std::vector<uint64_t>* const seen_syscall_args = static_cast<std::vector<uint64_t>*>(aux); BPF_ASSERT(arraysize(args.args) == 6); @@ -78,7 +78,9 @@ intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) { return -ENOMEM; } -ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox, int sysno, void* aux) { +ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox, + int sysno, + std::vector<uint64_t>* aux) { if (!SandboxBPF::IsValidSyscallNumber(sysno)) { return ErrorCode(ENOSYS); } @@ -94,7 +96,7 @@ ErrorCode CopyAllArgsOnUnamePolicy(SandboxBPF* sandbox, int sysno, void* aux) { BPF_TEST(Syscall, SyntheticSixArgs, CopyAllArgsOnUnamePolicy, - std::vector<uint64_t> /* BPF_AUX */) { + std::vector<uint64_t> /* (*BPF_AUX) */) { const int kExpectedValue = 42; // In this test we only pass integers to the kernel. We might want to make // additional tests to try other types. What we will see depends on @@ -116,17 +118,17 @@ BPF_TEST(Syscall, syscall_args[5]) == -ENOMEM); // We expect the trap handler to have copied the 6 arguments. - BPF_ASSERT(BPF_AUX.size() == 6); + BPF_ASSERT(BPF_AUX->size() == 6); // Don't loop here so that we can see which argument does cause the failure // easily from the failing line. // uint64_t is the type passed to our SIGSYS handler. - BPF_ASSERT(BPF_AUX[0] == static_cast<uint64_t>(syscall_args[0])); - BPF_ASSERT(BPF_AUX[1] == static_cast<uint64_t>(syscall_args[1])); - BPF_ASSERT(BPF_AUX[2] == static_cast<uint64_t>(syscall_args[2])); - BPF_ASSERT(BPF_AUX[3] == static_cast<uint64_t>(syscall_args[3])); - BPF_ASSERT(BPF_AUX[4] == static_cast<uint64_t>(syscall_args[4])); - BPF_ASSERT(BPF_AUX[5] == static_cast<uint64_t>(syscall_args[5])); + BPF_ASSERT((*BPF_AUX)[0] == static_cast<uint64_t>(syscall_args[0])); + BPF_ASSERT((*BPF_AUX)[1] == static_cast<uint64_t>(syscall_args[1])); + BPF_ASSERT((*BPF_AUX)[2] == static_cast<uint64_t>(syscall_args[2])); + BPF_ASSERT((*BPF_AUX)[3] == static_cast<uint64_t>(syscall_args[3])); + BPF_ASSERT((*BPF_AUX)[4] == static_cast<uint64_t>(syscall_args[4])); + BPF_ASSERT((*BPF_AUX)[5] == static_cast<uint64_t>(syscall_args[5])); } TEST(Syscall, ComplexSyscallSixArgs) { diff --git a/sandbox/linux/suid/client/DEPS b/sandbox/linux/suid/client/DEPS new file mode 100644 index 0000000000..99a337d772 --- /dev/null +++ b/sandbox/linux/suid/client/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+sandbox/linux/services", +] diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc index 3300cb440f..fc03cdd099 100644 --- a/sandbox/linux/suid/client/setuid_sandbox_client.cc +++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc @@ -5,6 +5,8 @@ #include "sandbox/linux/suid/client/setuid_sandbox_client.h" #include <fcntl.h> +#include <stdlib.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> @@ -136,7 +138,7 @@ namespace sandbox { SetuidSandboxClient* SetuidSandboxClient::Create() { base::Environment* environment(base::Environment::Create()); - SetuidSandboxClient* sandbox_client(new(SetuidSandboxClient)); + SetuidSandboxClient* sandbox_client(new SetuidSandboxClient); CHECK(environment); sandbox_client->env_ = environment; @@ -152,6 +154,21 @@ SetuidSandboxClient::~SetuidSandboxClient() { delete env_; } +void SetuidSandboxClient::CloseDummyFile() { + // When we're launched through the setuid sandbox, SetupLaunchOptions + // arranges for kZygoteIdFd to be a dummy file descriptor to satisfy an + // ancient setuid sandbox ABI requirement. However, the descriptor is no + // longer needed, so we can simply close it right away now. + CHECK(IsSuidSandboxChild()); + + // Sanity check that kZygoteIdFd refers to a pipe. + struct stat st; + PCHECK(0 == fstat(kZygoteIdFd, &st)); + CHECK(S_ISFIFO(st.st_mode)); + + PCHECK(0 == IGNORE_EINTR(close(kZygoteIdFd))); +} + bool SetuidSandboxClient::ChrootMe() { int ipc_fd = GetIPCDescriptor(env_); @@ -226,12 +243,6 @@ bool SetuidSandboxClient::IsDisabledViaEnvironment() { return false; } -int SetuidSandboxClient::GetUniqueToChildFileDescriptor() { - // The setuid binary is hard-wired to close this in the helper process it - // creates. - return kZygoteIdFd; -} - base::FilePath SetuidSandboxClient::GetSandboxBinaryPath() { base::FilePath sandbox_binary; base::FilePath exe_dir; @@ -256,8 +267,7 @@ base::FilePath SetuidSandboxClient::GetSandboxBinaryPath() { return sandbox_binary; } -void SetuidSandboxClient::PrependWrapper(base::CommandLine* cmd_line, - base::LaunchOptions* options) { +void SetuidSandboxClient::PrependWrapper(base::CommandLine* cmd_line) { std::string sandbox_binary(GetSandboxBinaryPath().value()); struct stat st; if (sandbox_binary.empty() || stat(sandbox_binary.c_str(), &st) != 0) { @@ -275,15 +285,30 @@ void SetuidSandboxClient::PrependWrapper(base::CommandLine* cmd_line, << sandbox_binary << " is owned by root and has mode 4755."; } - if (cmd_line) { - cmd_line->PrependWrapper(sandbox_binary); - } + cmd_line->PrependWrapper(sandbox_binary); +} - if (options) { - // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used. - options->allow_new_privs = true; - UnsetExpectedEnvironmentVariables(&options->environ); - } +void SetuidSandboxClient::SetupLaunchOptions( + base::LaunchOptions* options, + base::FileHandleMappingVector* fds_to_remap, + base::ScopedFD* dummy_fd) { + DCHECK(options); + DCHECK(fds_to_remap); + + // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used. + options->allow_new_privs = true; + UnsetExpectedEnvironmentVariables(&options->environ); + + // Set dummy_fd to the reading end of a closed pipe. + int pipe_fds[2]; + PCHECK(0 == pipe(pipe_fds)); + PCHECK(0 == IGNORE_EINTR(close(pipe_fds[1]))); + dummy_fd->reset(pipe_fds[0]); + + // We no longer need a dummy socket for discovering the child's PID, + // but the sandbox is still hard-coded to expect a file descriptor at + // kZygoteIdFd. Fixing this requires a sandbox API change. :( + fds_to_remap->push_back(std::make_pair(dummy_fd->get(), kZygoteIdFd)); } void SetuidSandboxClient::SetupLaunchEnvironment() { diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.h b/sandbox/linux/suid/client/setuid_sandbox_client.h index 332c63b69c..2bbad7a8bd 100644 --- a/sandbox/linux/suid/client/setuid_sandbox_client.h +++ b/sandbox/linux/suid/client/setuid_sandbox_client.h @@ -8,14 +8,10 @@ #include "base/basictypes.h" #include "base/callback_forward.h" #include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/process/launch.h" #include "sandbox/linux/sandbox_export.h" -namespace base { -class CommandLine; -class Environment; -struct LaunchOptions; -} - namespace sandbox { // Helper class to use the setuid sandbox. This class is to be used both @@ -28,23 +24,21 @@ namespace sandbox { // 1. A calls SetupLaunchEnvironment() // 2. A sets up a CommandLine and then amends it with // PrependWrapper() (or manually, by relying on GetSandboxBinaryPath()). -// 3. A makes sure that GetUniqueToChildFileDescriptor() is an existing file -// descriptor that can be closed by the helper process created by the -// setuid sandbox. (This is the right file descriptor to use for magic -// "must-be-unique" sockets that are use to identify processes across -// pid namespaces.) +// 3. A uses SetupLaunchOptions() to arrange for a dummy descriptor for the +// setuid sandbox ABI. // 4. A launches B with base::LaunchProcess, using the amended CommandLine. -// 5. B performs various initializations that require access to the file +// 5. B uses CloseDummyFile() to close the dummy file descriptor. +// 6. B performs various initializations that require access to the file // system. -// 5.b (optional) B uses sandbox::Credentials::HasOpenDirectory() to verify +// 6.b (optional) B uses sandbox::Credentials::HasOpenDirectory() to verify // that no directory is kept open (which would allow bypassing the setuid // sandbox). -// 6. B should be prepared to assume the role of init(1). In particular, B +// 7. B should be prepared to assume the role of init(1). In particular, B // cannot receive any signal from any other process, excluding SIGKILL. // If B dies, all the processes in the namespace will die. // B can fork() and the parent can assume the role of init(1), by using // CreateInitProcessReaper(). -// 7. B requests being chroot-ed through ChrootMe() and +// 8. B requests being chroot-ed through ChrootMe() and // requests other sandboxing status via the status functions. class SANDBOX_EXPORT SetuidSandboxClient { public: @@ -52,6 +46,8 @@ class SANDBOX_EXPORT SetuidSandboxClient { static class SetuidSandboxClient* Create(); ~SetuidSandboxClient(); + // Close the dummy file descriptor leftover from the sandbox ABI. + void CloseDummyFile(); // Ask the setuid helper over the setuid sandbox IPC channel to chroot() us // to an empty directory. // Will only work if we have been launched through the setuid helper. @@ -76,22 +72,21 @@ class SANDBOX_EXPORT SetuidSandboxClient { // The setuid sandbox may still be disabled via the environment. // This is tracked in crbug.com/245376. bool IsDisabledViaEnvironment(); - // When using the setuid sandbox, an extra helper process is created. - // Unfortunately, this helper process is hard-wired to close a specific file - // descriptor. - // The caller must make sure that GetUniqueToChildFileDescriptor() is an - // existing file descriptor that can be closed by the helper process. It's ok - // to make it a dummy, useless file descriptor if needed. - int GetUniqueToChildFileDescriptor(); // Get the sandbox binary path. This method knows about the // CHROME_DEVEL_SANDBOX environment variable used for user-managed builds. If // the sandbox binary cannot be found, it will return an empty FilePath. base::FilePath GetSandboxBinaryPath(); - // Modify |cmd_line| and |options| to launch via the setuid sandbox. Crash if - // the setuid sandbox binary cannot be found. Either can be NULL if the caller - // needs additional control. - void PrependWrapper(base::CommandLine* cmd_line, - base::LaunchOptions* options); + // Modify |cmd_line| to launch via the setuid sandbox. Crash if the setuid + // sandbox binary cannot be found. |cmd_line| must not be NULL. + void PrependWrapper(base::CommandLine* cmd_line); + // Set-up the launch options for launching via the setuid sandbox. Caller is + // responsible for keeping |dummy_fd| alive until LaunchProcess() completes. + // |options| and |fds_to_remap| must not be NULL. + // (Keeping |dummy_fd| alive is an unfortunate historical artifact of the + // chrome-sandbox ABI.) + void SetupLaunchOptions(base::LaunchOptions* options, + base::FileHandleMappingVector* fds_to_remap, + base::ScopedFD* dummy_fd); // Set-up the environment. This should be done prior to launching the setuid // helper. void SetupLaunchEnvironment(); @@ -106,4 +101,3 @@ class SANDBOX_EXPORT SetuidSandboxClient { } // namespace sandbox #endif // SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_ - diff --git a/sandbox/linux/tests/sandbox_test_runner.h b/sandbox/linux/tests/sandbox_test_runner.h new file mode 100644 index 0000000000..4cb7102ff9 --- /dev/null +++ b/sandbox/linux/tests/sandbox_test_runner.h @@ -0,0 +1,25 @@ +// 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_TESTS_SANDBOX_TEST_RUNNER_H_ +#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_ + +#include "base/basictypes.h" + +namespace sandbox { + +// A simple "runner" class to implement tests. +class SandboxTestRunner { + public: + SandboxTestRunner() {} + virtual ~SandboxTestRunner() {} + virtual void Run() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(SandboxTestRunner); +}; + +} // namespace sandbox + +#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_ diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc new file mode 100644 index 0000000000..69e05ac4e0 --- /dev/null +++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc @@ -0,0 +1,25 @@ +// 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/tests/sandbox_test_runner_function_pointer.h" + +#include "base/logging.h" +#include "build/build_config.h" + +namespace sandbox { + +SandboxTestRunnerFunctionPointer::SandboxTestRunnerFunctionPointer( + void (*function_to_run)(void)) + : function_to_run_(function_to_run) { +} + +SandboxTestRunnerFunctionPointer::~SandboxTestRunnerFunctionPointer() { +} + +void SandboxTestRunnerFunctionPointer::Run() { + DCHECK(function_to_run_); + function_to_run_(); +} + +} // namespace sandbox diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.h b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h new file mode 100644 index 0000000000..1cb709f14c --- /dev/null +++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h @@ -0,0 +1,26 @@ +// 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_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_ +#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_ + +#include "base/basictypes.h" +#include "sandbox/linux/tests/sandbox_test_runner.h" + +namespace sandbox { + +class SandboxTestRunnerFunctionPointer : public SandboxTestRunner { + public: + SandboxTestRunnerFunctionPointer(void (*function_to_run)(void)); + virtual ~SandboxTestRunnerFunctionPointer() OVERRIDE; + virtual void Run() OVERRIDE; + + private: + void (*function_to_run_)(void); + DISALLOW_COPY_AND_ASSIGN(SandboxTestRunnerFunctionPointer); +}; + +} // namespace sandbox + +#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER__FUNCTION_POINTER_H_ diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc index 42b85a8d6f..d3ee81b36b 100644 --- a/sandbox/linux/tests/unit_tests.cc +++ b/sandbox/linux/tests/unit_tests.cc @@ -12,6 +12,7 @@ #include <unistd.h> #include "base/file_util.h" +#include "base/posix/eintr_wrapper.h" #include "base/third_party/valgrind/valgrind.h" #include "build/build_config.h" #include "sandbox/linux/tests/unit_tests.h" @@ -105,10 +106,10 @@ static void SetProcessTimeout(int time_in_seconds) { // in the BPF sandbox, as it potentially makes global state changes and as // it also tends to raise fatal errors, if the code has been used in an // insecure manner. -void UnitTests::RunTestInProcess(UnitTests::Test test, - void* arg, +void UnitTests::RunTestInProcess(SandboxTestRunner* test_runner, DeathCheck death, const void* death_aux) { + CHECK(test_runner); // We need to fork(), so we can't be multi-threaded, as threads could hold // locks. int num_threads = CountThreads(); @@ -174,7 +175,7 @@ void UnitTests::RunTestInProcess(UnitTests::Test test, struct rlimit no_core = {0}; setrlimit(RLIMIT_CORE, &no_core); - test(arg); + test_runner->Run(); _exit(kExpectedValue); } diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h index bc1939c9a1..9531595009 100644 --- a/sandbox/linux/tests/unit_tests.h +++ b/sandbox/linux/tests/unit_tests.h @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "build/build_config.h" +#include "sandbox/linux/tests/sandbox_test_runner_function_pointer.h" #include "testing/gtest/include/gtest/gtest.h" namespace sandbox { @@ -62,12 +63,13 @@ bool IsRunningOnValgrind(); // that the test actually dies. The death test only passes if the death occurs // in the expected fashion, as specified by "death" and "death_aux". These two // parameters are typically set to one of the DEATH_XXX() macros. -#define SANDBOX_DEATH_TEST(test_case_name, test_name, death) \ - void TEST_##test_name(void*); \ - TEST(test_case_name, test_name) { \ - sandbox::UnitTests::RunTestInProcess(TEST_##test_name, NULL, death); \ - } \ - void TEST_##test_name(void*) +#define SANDBOX_DEATH_TEST(test_case_name, test_name, death) \ + void TEST_##test_name(void); \ + TEST(test_case_name, test_name) { \ + SandboxTestRunnerFunctionPointer sandbox_test_runner(TEST_##test_name); \ + sandbox::UnitTests::RunTestInProcess(&sandbox_test_runner, death); \ + } \ + void TEST_##test_name(void) // Define a new test case that runs inside of a GTest death test. This is // necessary, as most of our tests by definition make global and irreversible @@ -88,9 +90,10 @@ bool IsRunningOnValgrind(); ((expr) ? static_cast<void>(0) : sandbox::UnitTests::AssertionFailure( \ SANDBOX_STR(expr), __FILE__, __LINE__)) +// This class allows to run unittests in their own process. The main method is +// RunTestInProcess(). class UnitTests { public: - typedef void (*Test)(void*); typedef void (*DeathCheck)(int status, const std::string& msg, const void* aux); @@ -99,8 +102,12 @@ class UnitTests { // directly. It is automatically invoked by SANDBOX_TEST(). Most sandboxing // functions make global irreversible changes to the execution environment // and must therefore execute in their own isolated process. - static void RunTestInProcess(Test test, - void* arg, + // |test_runner| must implement the SandboxTestRunner interface and will run + // in a subprocess. + // Note: since the child process (created with fork()) will never return from + // RunTestInProcess(), |test_runner| is guaranteed to exist for the lifetime + // of the child process. + static void RunTestInProcess(SandboxTestRunner* test_runner, DeathCheck death, const void* death_aux); diff --git a/sandbox/sandbox_services.target.darwin-arm.mk b/sandbox/sandbox_services.target.darwin-arm.mk index 3a93b3ea19..248a767a67 100644 --- a/sandbox/sandbox_services.target.darwin-arm.mk +++ b/sandbox/sandbox_services.target.darwin-arm.mk @@ -45,7 +45,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -133,7 +132,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/sandbox_services.target.darwin-x86.mk b/sandbox/sandbox_services.target.darwin-x86.mk index 47be915aed..3eb1812e00 100644 --- a/sandbox/sandbox_services.target.darwin-x86.mk +++ b/sandbox/sandbox_services.target.darwin-x86.mk @@ -47,7 +47,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -135,7 +134,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/sandbox_services.target.darwin-x86_64.mk b/sandbox/sandbox_services.target.darwin-x86_64.mk index 5982b2c1fe..ee7ac3d5ed 100644 --- a/sandbox/sandbox_services.target.darwin-x86_64.mk +++ b/sandbox/sandbox_services.target.darwin-x86_64.mk @@ -47,7 +47,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -135,7 +134,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/sandbox_services.target.linux-arm.mk b/sandbox/sandbox_services.target.linux-arm.mk index 3a93b3ea19..248a767a67 100644 --- a/sandbox/sandbox_services.target.linux-arm.mk +++ b/sandbox/sandbox_services.target.linux-arm.mk @@ -45,7 +45,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -133,7 +132,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/sandbox_services.target.linux-x86.mk b/sandbox/sandbox_services.target.linux-x86.mk index 47be915aed..3eb1812e00 100644 --- a/sandbox/sandbox_services.target.linux-x86.mk +++ b/sandbox/sandbox_services.target.linux-x86.mk @@ -47,7 +47,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -135,7 +134,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/sandbox_services.target.linux-x86_64.mk b/sandbox/sandbox_services.target.linux-x86_64.mk index 5982b2c1fe..ee7ac3d5ed 100644 --- a/sandbox/sandbox_services.target.linux-x86_64.mk +++ b/sandbox/sandbox_services.target.linux-x86_64.mk @@ -47,7 +47,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -135,7 +134,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/sandbox_services_headers.target.darwin-arm.mk b/sandbox/sandbox_services_headers.target.darwin-arm.mk index 054f04cd31..d2b380eef7 100644 --- a/sandbox/sandbox_services_headers.target.darwin-arm.mk +++ b/sandbox/sandbox_services_headers.target.darwin-arm.mk @@ -40,7 +40,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -125,7 +124,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/sandbox_services_headers.target.darwin-x86.mk b/sandbox/sandbox_services_headers.target.darwin-x86.mk index a9f35a99ac..cf8421a11e 100644 --- a/sandbox/sandbox_services_headers.target.darwin-x86.mk +++ b/sandbox/sandbox_services_headers.target.darwin-x86.mk @@ -42,7 +42,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -127,7 +126,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/sandbox_services_headers.target.darwin-x86_64.mk b/sandbox/sandbox_services_headers.target.darwin-x86_64.mk index bc9fbc3f5e..4d6658efe3 100644 --- a/sandbox/sandbox_services_headers.target.darwin-x86_64.mk +++ b/sandbox/sandbox_services_headers.target.darwin-x86_64.mk @@ -42,7 +42,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -127,7 +126,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/sandbox_services_headers.target.linux-arm.mk b/sandbox/sandbox_services_headers.target.linux-arm.mk index 054f04cd31..d2b380eef7 100644 --- a/sandbox/sandbox_services_headers.target.linux-arm.mk +++ b/sandbox/sandbox_services_headers.target.linux-arm.mk @@ -40,7 +40,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -125,7 +124,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/sandbox_services_headers.target.linux-x86.mk b/sandbox/sandbox_services_headers.target.linux-x86.mk index a9f35a99ac..cf8421a11e 100644 --- a/sandbox/sandbox_services_headers.target.linux-x86.mk +++ b/sandbox/sandbox_services_headers.target.linux-x86.mk @@ -42,7 +42,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -127,7 +126,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/sandbox_services_headers.target.linux-x86_64.mk b/sandbox/sandbox_services_headers.target.linux-x86_64.mk index bc9fbc3f5e..4d6658efe3 100644 --- a/sandbox/sandbox_services_headers.target.linux-x86_64.mk +++ b/sandbox/sandbox_services_headers.target.linux-x86_64.mk @@ -42,7 +42,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -127,7 +126,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf.target.darwin-arm.mk b/sandbox/seccomp_bpf.target.darwin-arm.mk index 97a5d03e8d..e232e263fc 100644 --- a/sandbox/seccomp_bpf.target.darwin-arm.mk +++ b/sandbox/seccomp_bpf.target.darwin-arm.mk @@ -50,7 +50,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -137,7 +136,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/seccomp_bpf.target.darwin-x86.mk b/sandbox/seccomp_bpf.target.darwin-x86.mk index 1feffcddc3..be3488fa02 100644 --- a/sandbox/seccomp_bpf.target.darwin-x86.mk +++ b/sandbox/seccomp_bpf.target.darwin-x86.mk @@ -52,7 +52,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -139,7 +138,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf.target.darwin-x86_64.mk b/sandbox/seccomp_bpf.target.darwin-x86_64.mk index 677a5ef678..6180638e1a 100644 --- a/sandbox/seccomp_bpf.target.darwin-x86_64.mk +++ b/sandbox/seccomp_bpf.target.darwin-x86_64.mk @@ -52,7 +52,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -139,7 +138,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf.target.linux-arm.mk b/sandbox/seccomp_bpf.target.linux-arm.mk index 97a5d03e8d..e232e263fc 100644 --- a/sandbox/seccomp_bpf.target.linux-arm.mk +++ b/sandbox/seccomp_bpf.target.linux-arm.mk @@ -50,7 +50,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -137,7 +136,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/seccomp_bpf.target.linux-x86.mk b/sandbox/seccomp_bpf.target.linux-x86.mk index 1feffcddc3..be3488fa02 100644 --- a/sandbox/seccomp_bpf.target.linux-x86.mk +++ b/sandbox/seccomp_bpf.target.linux-x86.mk @@ -52,7 +52,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -139,7 +138,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf.target.linux-x86_64.mk b/sandbox/seccomp_bpf.target.linux-x86_64.mk index 677a5ef678..6180638e1a 100644 --- a/sandbox/seccomp_bpf.target.linux-x86_64.mk +++ b/sandbox/seccomp_bpf.target.linux-x86_64.mk @@ -52,7 +52,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -139,7 +138,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf_helpers.target.darwin-arm.mk b/sandbox/seccomp_bpf_helpers.target.darwin-arm.mk index 891944d7c7..2f1569a824 100644 --- a/sandbox/seccomp_bpf_helpers.target.darwin-arm.mk +++ b/sandbox/seccomp_bpf_helpers.target.darwin-arm.mk @@ -44,7 +44,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -131,7 +130,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/seccomp_bpf_helpers.target.darwin-x86.mk b/sandbox/seccomp_bpf_helpers.target.darwin-x86.mk index 9f6a44238d..c537e98163 100644 --- a/sandbox/seccomp_bpf_helpers.target.darwin-x86.mk +++ b/sandbox/seccomp_bpf_helpers.target.darwin-x86.mk @@ -46,7 +46,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -133,7 +132,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf_helpers.target.darwin-x86_64.mk b/sandbox/seccomp_bpf_helpers.target.darwin-x86_64.mk index 1309413df3..59ad6c3983 100644 --- a/sandbox/seccomp_bpf_helpers.target.darwin-x86_64.mk +++ b/sandbox/seccomp_bpf_helpers.target.darwin-x86_64.mk @@ -46,7 +46,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -133,7 +132,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf_helpers.target.linux-arm.mk b/sandbox/seccomp_bpf_helpers.target.linux-arm.mk index 891944d7c7..2f1569a824 100644 --- a/sandbox/seccomp_bpf_helpers.target.linux-arm.mk +++ b/sandbox/seccomp_bpf_helpers.target.linux-arm.mk @@ -44,7 +44,6 @@ MY_CFLAGS_Debug := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ @@ -131,7 +130,6 @@ MY_CFLAGS_Release := \ -pipe \ -fPIC \ -fno-tree-sra \ - -fuse-ld=gold \ -Wno-psabi \ -ffunction-sections \ -funwind-tables \ diff --git a/sandbox/seccomp_bpf_helpers.target.linux-x86.mk b/sandbox/seccomp_bpf_helpers.target.linux-x86.mk index 9f6a44238d..c537e98163 100644 --- a/sandbox/seccomp_bpf_helpers.target.linux-x86.mk +++ b/sandbox/seccomp_bpf_helpers.target.linux-x86.mk @@ -46,7 +46,6 @@ MY_CFLAGS_Debug := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -133,7 +132,6 @@ MY_CFLAGS_Release := \ -mfpmath=sse \ -mmmx \ -m32 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ diff --git a/sandbox/seccomp_bpf_helpers.target.linux-x86_64.mk b/sandbox/seccomp_bpf_helpers.target.linux-x86_64.mk index 1309413df3..59ad6c3983 100644 --- a/sandbox/seccomp_bpf_helpers.target.linux-x86_64.mk +++ b/sandbox/seccomp_bpf_helpers.target.linux-x86_64.mk @@ -46,7 +46,6 @@ MY_CFLAGS_Debug := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ @@ -133,7 +132,6 @@ MY_CFLAGS_Release := \ -Wno-unused-local-typedefs \ -m64 \ -march=x86-64 \ - -fuse-ld=gold \ -ffunction-sections \ -funwind-tables \ -g \ |