summaryrefslogtreecommitdiffstats
path: root/sandbox
diff options
context:
space:
mode:
authorjln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-07 04:41:11 +0000
committerjln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-07 04:41:11 +0000
commita0b7132c6bb5abbde1ccaa8e851ca599da7d6854 (patch)
tree09eb2528043e925f6e19c40ae3747fe790b02628 /sandbox
parentadf1db98ca2fea1028b12dec91ceaaeae5977b0c (diff)
downloadchromium_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.gypi3
-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.h81
-rw-r--r--sandbox/linux/seccomp-bpf/bpf_tests.h167
-rw-r--r--sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc75
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