diff options
author | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-07 04:41:11 +0000 |
---|---|---|
committer | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-07 04:41:11 +0000 |
commit | a0b7132c6bb5abbde1ccaa8e851ca599da7d6854 (patch) | |
tree | 09eb2528043e925f6e19c40ae3747fe790b02628 /sandbox | |
parent | adf1db98ca2fea1028b12dec91ceaaeae5977b0c (diff) | |
download | chromium_src-a0b7132c6bb5abbde1ccaa8e851ca599da7d6854.zip chromium_src-a0b7132c6bb5abbde1ccaa8e851ca599da7d6854.tar.gz chromium_src-a0b7132c6bb5abbde1ccaa8e851ca599da7d6854.tar.bz2 |
Linux sandbox: implement new BPF testing macros
Implement new BPF_TEST_C and BPF_TEST_D macros to allow writing new BPF
tests without using the deprecated "BPF policies as function pointers".
BUG=368970
R=mdempsky@chromium.org
TBR=jorgelo
Review URL: https://codereview.chromium.org/269303002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268668 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/linux/sandbox_linux.gypi | 3 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.cc (renamed from sandbox/linux/seccomp-bpf/bpf_tests.cc) | 6 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h | 81 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/bpf_tests.h | 167 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc | 75 |
5 files changed, 243 insertions, 89 deletions
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi index 9c56f8d..e86345e 100644 --- a/sandbox/linux/sandbox_linux.gypi +++ b/sandbox/linux/sandbox_linux.gypi @@ -77,7 +77,8 @@ 'conditions': [ [ 'use_seccomp_bpf==1', { 'sources': [ - 'seccomp-bpf/bpf_tests.cc', + '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', diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.cc b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.cc index b1ab719..2fa209b 100644 --- a/sandbox/linux/seccomp-bpf/bpf_tests.cc +++ b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.cc @@ -2,19 +2,19 @@ // 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 "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h" namespace sandbox { // static template <> -void* BPFTesterSimpleDelegate<void>::NewAux() { +void* BPFTesterCompatibilityDelegate<void>::NewAux() { return NULL; } // static template <> -void BPFTesterSimpleDelegate<void>::DeleteAux(void* aux) { +void BPFTesterCompatibilityDelegate<void>::DeleteAux(void* aux) { CHECK(!aux); } 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 0000000..c211d04 --- /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 ccfc651..235aad3 100644 --- a/sandbox/linux/seccomp-bpf/bpf_tests.h +++ b/sandbox/linux/seccomp-bpf/bpf_tests.h @@ -5,115 +5,122 @@ #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/debug/leak_annotations.h" -#include "base/memory/scoped_ptr.h" +#include "base/basictypes.h" #include "build/build_config.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf.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/seccomp-bpf/bpf_tester_compatibility_delegate.h" #include "sandbox/linux/tests/unit_tests.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::BPFTesterSimpleDelegate<aux>::AuxType* BPF_AUX); \ - TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \ - sandbox::SandboxBPFTestRunner bpf_test_runner( \ - new sandbox::BPFTesterSimpleDelegate<aux>(BPF_TEST_##test_name, \ - policy)); \ - sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \ - } \ - void BPF_TEST_##test_name( \ - sandbox::BPFTesterSimpleDelegate<aux>::AuxType* BPF_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 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 +#define BPF_ASSERT_EQ(x, y) BPF_ASSERT((x) == (y)) +#define BPF_ASSERT_NE(x, y) BPF_ASSERT((x) != (y)) +#define BPF_ASSERT_LT(x, y) BPF_ASSERT((x) < (y)) +#define BPF_ASSERT_GT(x, y) BPF_ASSERT((x) > (y)) +#define BPF_ASSERT_LE(x, y) BPF_ASSERT((x) <= (y)) +#define BPF_ASSERT_GE(x, y) BPF_ASSERT((x) >= (y)) -// 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. +// 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. +// 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...) \ +#define BPF_TEST(test_case_name, test_name, policy, aux...) \ BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux) -// Assertions are handled exactly the same as with a normal SANDBOX_TEST() -#define BPF_ASSERT SANDBOX_ASSERT -#define BPF_ASSERT_EQ(x, y) BPF_ASSERT((x) == (y)) -#define BPF_ASSERT_NE(x, y) BPF_ASSERT((x) != (y)) -#define BPF_ASSERT_LT(x, y) BPF_ASSERT((x) < (y)) -#define BPF_ASSERT_GT(x, y) BPF_ASSERT((x) > (y)) -#define BPF_ASSERT_LE(x, y) BPF_ASSERT((x) <= (y)) -#define BPF_ASSERT_GE(x, y) BPF_ASSERT((x) >= (y)) - -template <class Aux = void> +// 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: - typedef Aux AuxType; - BPFTesterSimpleDelegate( - 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 ~BPFTesterSimpleDelegate() { DeleteAux(aux_pointer_for_policy_); } + explicit BPFTesterSimpleDelegate(void (*test_function)(void)) + : test_function_(test_function) {} + virtual ~BPFTesterSimpleDelegate() {} 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_)); + return scoped_ptr<SandboxBPFPolicy>(new PolicyClass()); } - 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_); + DCHECK(test_function_); + test_function_(); } 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_; + void (*test_function_)(void); DISALLOW_COPY_AND_ASSIGN(BPFTesterSimpleDelegate); }; -// Specialization of NewAux that returns NULL; -template <> -void* BPFTesterSimpleDelegate<void>::NewAux(); -template <> -void BPFTesterSimpleDelegate<void>::DeleteAux(void* aux); - } // namespace sandbox #endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__ diff --git a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc index 41c3dd3..d83b8eda 100644 --- a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc +++ b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc @@ -5,6 +5,10 @@ #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" @@ -71,21 +75,82 @@ BPF_TEST(BPFTest, void DummyTestFunction(FourtyTwo *fourty_two) { } -TEST(BPFTest, BPFTesterSimpleDelegateLeakTest) { +TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) { // Don't do anything, simply gives dynamic tools an opportunity to detect // leaks. { - BPFTesterSimpleDelegate<FourtyTwo> simple_delegate(DummyTestFunction, - EmptyPolicyTakesClass); + BPFTesterCompatibilityDelegate<FourtyTwo> simple_delegate( + DummyTestFunction, EmptyPolicyTakesClass); } { // Test polymorphism. scoped_ptr<BPFTesterDelegate> simple_delegate( - new BPFTesterSimpleDelegate<FourtyTwo>(DummyTestFunction, - EmptyPolicyTakesClass)); + 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 |