Merge from Chromium at DEPS revision 269336

This commit was generated by merge_to_master.py.

Change-Id: I8b9c77f10eccd2a8b4c7ce373ffda18568af54ff
diff --git a/sandbox/linux/DEPS b/sandbox/linux/DEPS
new file mode 100644
index 0000000..5c5f476
--- /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 0cccd2e..e86345e 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 9764830..bf41471 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 0000000..e8000d3
--- /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 79b5b02..217bdac 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -97,6 +97,10 @@
     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 0000000..9182e07
--- /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 2f46401..29c5910 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 @@
 #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 792807a..5f8785e 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
@@ -350,7 +350,6 @@
 // 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 @@
     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 0000000..15b2b36
--- /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 0000000..2fa209b
--- /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 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 357e29c..8fa5579 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_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)
 
-// 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)
+// 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 @@
 #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 {
+// 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)
+
+// 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;
+  explicit BPFTesterSimpleDelegate(void (*test_function)(void))
+      : test_function_(test_function) {}
+  virtual ~BPFTesterSimpleDelegate() {}
 
-  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);
-
-      // 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));
-
-      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 0000000..d83b8ed
--- /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 1538fe8..497c343 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 @@
   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 @@
   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 67b84b9..2391c5d 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -73,7 +73,6 @@
   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 0000000..bd947ad
--- /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 0000000..ade1d49
--- /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 0000000..c1beba2
--- /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 5c5c627..3b7470b 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 @@
 
 // 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_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 @@
 }
 
 // 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 @@
 
 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 @@
 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 @@
   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 @@
   }
 }
 
-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 @@
                       (void*)NULL,
                       (void*)NULL,
                       (void*)NULL));
-  BPF_ASSERT(BPF_AUX == 3);
+  BPF_ASSERT(*BPF_AUX == 3);
   BPF_ASSERT(*name);
 }
 
@@ -724,8 +726,9 @@
   }
 }
 
-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 @@
 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 @@
   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 60db69b..bdeee4f 100644
--- a/sandbox/linux/seccomp-bpf/syscall_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -70,7 +70,7 @@
 
 // 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 @@
   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 @@
 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 @@
                             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 0000000..99a337d
--- /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 3300cb4..fc03cdd 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 @@
 
 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 @@
   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 @@
   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 @@
   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 @@
                << 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 332c63b..2bbad7a 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 @@
 // 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 @@
   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 @@
   // 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 @@
 }  // 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 0000000..4cb7102
--- /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 0000000..69e05ac
--- /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 0000000..1cb709f
--- /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 42b85a8..d3ee81b 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 @@
 // 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 @@
     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 bc1939c..9531595 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 @@
 // 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 @@
   ((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 @@
   // 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);