Merge from Chromium at DEPS revision r190564

This commit was generated by merge_to_master.py.

Change-Id: Icadecbce29854b8fa25fd335b2c1949b5ca5d170
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index c02cd31..f9cef2b 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -3,6 +3,33 @@
 # found in the LICENSE file.
 
 {
+  'variables': {
+    'conditions': [
+      ['OS=="linux"', {
+        'compile_suid_client': 1,
+      }, {
+        'compile_suid_client': 0,
+      }],
+      ['((OS=="linux" or OS=="android") and '
+             '(target_arch=="ia32" or target_arch=="x64" or '
+              'target_arch=="arm"))', {
+        'compile_seccomp_bpf': 1,
+      }, {
+        'compile_seccomp_bpf': 0,
+      }],
+    ],
+  },
+  'target_defaults': {
+    'target_conditions': [
+      # All linux/ files will automatically be excluded on Android
+      # so make sure we re-include them explicitly.
+      ['OS == "android"', {
+        'sources/': [
+          ['include', '^linux/'],
+        ],
+      }],
+    ],
+  },
   'targets': [
     # We have two principal targets: sandbox and sandbox_linux_unittests
     # All other targets are listed as dependencies.
@@ -12,9 +39,14 @@
       'target_name': 'sandbox',
       'type': 'none',
       'dependencies': [
-        'suid_sandbox_client',
+        'sandbox_services',
       ],
       'conditions': [
+        [ 'compile_suid_client==1', {
+          'dependencies': [
+            'suid_sandbox_client',
+          ],
+        }],
         # Only compile in the seccomp mode 1 code for the flag combination
         # where we support it.
         [ 'OS=="linux" and (target_arch=="ia32" or target_arch=="x64") '
@@ -24,9 +56,7 @@
           ],
         }],
         # Similarly, compile seccomp BPF when we support it
-        [ 'OS=="linux" and (target_arch=="ia32" or target_arch=="x64" '
-                           'or target_arch=="arm")', {
-          'type': 'static_library',
+        [ 'compile_seccomp_bpf==1', {
           'dependencies': [
             'seccomp_bpf',
           ],
@@ -34,30 +64,25 @@
       ],
     },
     {
+      # The main sandboxing test target.
       'target_name': 'sandbox_linux_unittests',
+      'includes': [
+        'sandbox_linux_test_sources.gypi',
+      ],
       'type': 'executable',
-      'dependencies': [
-        'sandbox',
-        '../testing/gtest.gyp:gtest',
+    },
+    {
+      # This target is the shared library used by Android APK (i.e.
+      # JNI-friendly) tests.
+      'target_name': 'sandbox_linux_jni_unittests',
+      'includes': [
+        'sandbox_linux_test_sources.gypi',
       ],
-      'sources': [
-        'tests/main.cc',
-        'tests/unit_tests.cc',
-        'tests/unit_tests.h',
-        'suid/client/setuid_sandbox_client_unittest.cc',
-      ],
-      'include_dirs': [
-        '../..',
-      ],
+      'type': 'shared_library',
       'conditions': [
-        [ 'OS=="linux" and (target_arch=="ia32" or target_arch=="x64" '
-                           'or target_arch=="arm")', {
-          'sources': [
-            'seccomp-bpf/bpf_tests.h',
-            'seccomp-bpf/codegen_unittest.cc',
-            'seccomp-bpf/errorcode_unittest.cc',
-            'seccomp-bpf/sandbox_bpf_unittest.cc',
-            'seccomp-bpf/syscall_iterator_unittest.cc',
+        [ 'OS == "android" and gtest_target_type == "shared_library"', {
+          'dependencies': [
+            '../testing/android/native_test.gyp:native_test_native_code',
           ],
         }],
       ],
@@ -75,15 +100,22 @@
         'seccomp-bpf/errorcode.cc',
         'seccomp-bpf/errorcode.h',
         'seccomp-bpf/instruction.h',
+        'seccomp-bpf/linux_seccomp.h',
+        'seccomp-bpf/port.h',
         'seccomp-bpf/sandbox_bpf.cc',
         'seccomp-bpf/sandbox_bpf.h',
+        'seccomp-bpf/syscall.cc',
+        'seccomp-bpf/syscall.h',
         'seccomp-bpf/syscall_iterator.cc',
         'seccomp-bpf/syscall_iterator.h',
+        'seccomp-bpf/trap.cc',
+        'seccomp-bpf/trap.h',
         'seccomp-bpf/verifier.cc',
         'seccomp-bpf/verifier.h',
       ],
       'dependencies': [
         '../base/base.gyp:base',
+        'sandbox_services_headers',
       ],
       'include_dirs': [
         '../..',
@@ -110,7 +142,37 @@
         '../..',
       ],
     },
+    { 'target_name': 'sandbox_services',
+      'type': 'static_library',
+      'sources': [
+        'services/broker_process.cc',
+        'services/broker_process.h',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+    },
+    { 'target_name': 'sandbox_services_headers',
+      'type': 'none',
+      'sources': [
+        'services/android_arm_ucontext.h',
+        'services/android_ucontext.h',
+        'services/android_i386_ucontext.h',
+        'services/arm_linux_syscalls.h',
+        'services/linux_syscalls.h',
+        'services/x86_32_linux_syscalls.h',
+        'services/x86_64_linux_syscalls.h',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+    },
     {
+      # We make this its own target so that it does not interfere
+      # with our tests.
       'target_name': 'libc_urandom_override',
       'type': 'static_library',
       'sources': [
@@ -140,6 +202,27 @@
         '..',
       ],
     },
-
+  ],
+  'conditions': [
+    # Strategy copied from base_unittests_apk in base/base.gyp.
+    [ 'OS=="android" and gtest_target_type == "shared_library"', {
+      'targets': [
+        {
+        'target_name': 'sandbox_linux_jni_unittests_apk',
+        'type': 'none',
+        'variables': {
+          'test_suite_name': 'sandbox_linux_jni_unittests',
+          'input_shlib_path':
+              '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)'
+              'sandbox_linux_jni_unittests'
+              '<(SHARED_LIB_SUFFIX)',
+        },
+        'dependencies': [
+          'sandbox_linux_jni_unittests',
+        ],
+        'includes': [ '../../build/apk_test.gypi' ],
+        }
+      ],
+    }],
   ],
 }
diff --git a/sandbox/linux/sandbox_linux_test_sources.gypi b/sandbox/linux/sandbox_linux_test_sources.gypi
new file mode 100644
index 0000000..0bc0aa1
--- /dev/null
+++ b/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -0,0 +1,38 @@
+# Copyright (c) 2012 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.
+
+# Tests need to be compiled in the same link unit, so we have to list them
+# in a separate .gypi file.
+{
+  'dependencies': [
+    'sandbox',
+    '../testing/gtest.gyp:gtest',
+  ],
+  'include_dirs': [
+    '../..',
+  ],
+  'sources': [
+    'tests/main.cc',
+    'tests/unit_tests.cc',
+    'tests/unit_tests.h',
+    'services/broker_process_unittest.cc',
+  ],
+  'conditions': [
+    [ 'compile_suid_client==1', {
+      'sources': [
+        'suid/client/setuid_sandbox_client_unittest.cc',
+      ],
+    }],
+    [ 'compile_seccomp_bpf==1', {
+      'sources': [
+        'seccomp-bpf/bpf_tests.h',
+        'seccomp-bpf/codegen_unittest.cc',
+        'seccomp-bpf/errorcode_unittest.cc',
+        'seccomp-bpf/sandbox_bpf_unittest.cc',
+        'seccomp-bpf/syscall_iterator_unittest.cc',
+        'seccomp-bpf/syscall_unittest.cc',
+      ],
+    }],
+  ],
+}
diff --git a/sandbox/linux/seccomp-bpf/Makefile b/sandbox/linux/seccomp-bpf/Makefile
index a697198..6b35580 100644
--- a/sandbox/linux/seccomp-bpf/Makefile
+++ b/sandbox/linux/seccomp-bpf/Makefile
@@ -1,8 +1,8 @@
 DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC -I.
-DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -DSECCOMP_BPF_VALGRIND_HACKS -include valgrind/valgrind.h -iquote ../../..
+DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -iquote ../../..
 DEF_LDFLAGS = -g -lpthread
 DEPFLAGS = -MMD -MF .$@.d
-MODS := demo sandbox_bpf die codegen errorcode syscall_iterator util verifier
+MODS := demo sandbox_bpf basicblock codegen die errorcode syscall syscall_iterator trap verifier
 OBJS64 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o64/')
 OBJS32 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o32/')
 ALL_OBJS = $(OBJS32) $(OBJS64)
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h
index 8da25f9..680ece6 100644
--- a/sandbox/linux/seccomp-bpf/bpf_tests.h
+++ b/sandbox/linux/seccomp-bpf/bpf_tests.h
@@ -5,12 +5,29 @@
 #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 "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.
+#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, test_name) {                                           \
+    sandbox::BpfTests<aux>::TestArgs arg(BPF_TEST_##test_name, policy);       \
+    sandbox::BpfTests<aux>::RunTestInProcess(                                 \
+                                   sandbox::BpfTests<aux>::TestWrapper, &arg, \
+                                   death);                                    \
+  }                                                                           \
+  void BPF_TEST_##test_name(sandbox::BpfTests<aux>::AuxType& BPF_AUX)
+
 // BPF_TEST() is a special version of SANDBOX_TEST(). It turns into a no-op,
 // if the host does not have kernel support for running BPF filters.
 // Also, it takes advantage of the Die class to avoid calling LOG(FATAL), from
@@ -22,13 +39,8 @@
 // would typically use it as an argument to Sandbox::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...)                   \
-  void BPF_TEST_##test_name(sandbox::BpfTests<aux>::AuxType& BPF_AUX);        \
-  TEST(test_case_name, test_name) {                                           \
-    sandbox::BpfTests<aux>::TestArgs arg(BPF_TEST_##test_name, policy);       \
-    sandbox::BpfTests<aux>::RunTestInProcess(                                 \
-                                   sandbox::BpfTests<aux>::TestWrapper, &arg);\
-  }                                                                           \
-  void BPF_TEST_##test_name(sandbox::BpfTests<aux>::AuxType& BPF_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
@@ -64,24 +76,30 @@
   static void TestWrapper(void *void_arg) {
     TestArgs *arg = reinterpret_cast<TestArgs *>(void_arg);
     playground2::Die::EnableSimpleExit();
-    if (playground2::Sandbox::supportsSeccompSandbox(-1) ==
+    if (playground2::Sandbox::SupportsSeccompSandbox(-1) ==
         playground2::Sandbox::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(playground2::Sandbox::supportsSeccompSandbox(proc_fd) ==
+      BPF_ASSERT(playground2::Sandbox::SupportsSeccompSandbox(proc_fd) ==
                  playground2::Sandbox::STATUS_AVAILABLE);
 
       // Initialize and then start the sandbox with our custom policy
-      playground2::Sandbox::setProcFd(proc_fd);
-      playground2::Sandbox::setSandboxPolicy(arg->policy(), &arg->aux_);
-      playground2::Sandbox::startSandbox();
+      playground2::Sandbox sandbox;
+      sandbox.set_proc_fd(proc_fd);
+      sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
+      sandbox.Sandbox::StartSandbox();
 
       arg->test()(arg->aux_);
     } else {
-      // TODO(markus): (crbug.com/141545) Call the compiler and verify the
-      //   policy. That's the least we can do, if we don't have kernel support.
-      playground2::Sandbox::setSandboxPolicy(arg->policy(), NULL);
+      // Call the compiler and verify the policy. That's the least we can do,
+      // if we don't have kernel support.
+      playground2::Sandbox sandbox;
+      sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
+      playground2::Sandbox::Program *program =
+          sandbox.AssembleFilter(true /* force_verification */);
+      delete program;
+      sandbox::UnitTests::IgnoreThisTest();
     }
   }
 
diff --git a/sandbox/linux/seccomp-bpf/codegen.cc b/sandbox/linux/seccomp-bpf/codegen.cc
index 8b36315..17b5d84 100644
--- a/sandbox/linux/seccomp-bpf/codegen.cc
+++ b/sandbox/linux/seccomp-bpf/codegen.cc
@@ -2,9 +2,36 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <stdio.h>
+
 #include "sandbox/linux/seccomp-bpf/codegen.h"
 
 
+namespace {
+
+// Helper function for Traverse().
+void TraverseRecursively(std::set<playground2::Instruction *> *visited,
+                         playground2::Instruction *instruction) {
+  if (visited->find(instruction) == visited->end()) {
+    visited->insert(instruction);
+    switch (BPF_CLASS(instruction->code)) {
+    case BPF_JMP:
+      if (BPF_OP(instruction->code) != BPF_JA) {
+        TraverseRecursively(visited, instruction->jf_ptr);
+      }
+      TraverseRecursively(visited, instruction->jt_ptr);
+      break;
+    case BPF_RET:
+      break;
+    default:
+      TraverseRecursively(visited, instruction->next);
+      break;
+    }
+  }
+}
+
+}  // namespace
+
 namespace playground2 {
 
 CodeGen::CodeGen()
@@ -33,7 +60,27 @@
     switch (BPF_CLASS(iter->code)) {
     case BPF_LD:
       if (iter->code == BPF_LD+BPF_W+BPF_ABS) {
-        fprintf(stderr, "LOAD %d\n", (int)iter->k);
+        fprintf(stderr, "LOAD %d  // ", (int)iter->k);
+        if (iter->k == offsetof(struct arch_seccomp_data, nr)) {
+          fprintf(stderr, "System call number\n");
+        } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) {
+          fprintf(stderr, "Architecture\n");
+        } else if (iter->k == offsetof(struct arch_seccomp_data,
+                                       instruction_pointer)) {
+          fprintf(stderr, "Instruction pointer (LSB)\n");
+        } else if (iter->k == offsetof(struct arch_seccomp_data,
+                                       instruction_pointer) + 4) {
+          fprintf(stderr, "Instruction pointer (MSB)\n");
+        } else if (iter->k >= offsetof(struct arch_seccomp_data, args) &&
+                   iter->k <  offsetof(struct arch_seccomp_data, args)+48 &&
+                   (iter->k-offsetof(struct arch_seccomp_data, args))%4 == 0) {
+          fprintf(stderr, "Argument %d (%cSB)\n",
+                  (int)(iter->k-offsetof(struct arch_seccomp_data, args))/8,
+                  (iter->k-offsetof(struct arch_seccomp_data,
+                                    args))%8 ? 'M' : 'L');
+        } else {
+          fprintf(stderr, "???\n");
+        }
       } else {
         fprintf(stderr, "LOAD ???\n");
       }
@@ -52,7 +99,31 @@
       }
       break;
     case BPF_RET:
-      fprintf(stderr, "RET 0x%x\n", iter->k);
+      fprintf(stderr, "RET 0x%x  // ", iter->k);
+      if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) {
+        fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA);
+      } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+        fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA);
+      } else if (iter->k == SECCOMP_RET_ALLOW) {
+        fprintf(stderr, "Allowed\n");
+      } else {
+        fprintf(stderr, "???\n");
+      }
+      break;
+    case BPF_ALU:
+      fprintf(stderr, BPF_OP(iter->code) == BPF_NEG
+              ? "A := -A\n" : "A := A %s 0x%x\n",
+              BPF_OP(iter->code) == BPF_ADD ? "+"  :
+              BPF_OP(iter->code) == BPF_SUB ? "-"  :
+              BPF_OP(iter->code) == BPF_MUL ? "*"  :
+              BPF_OP(iter->code) == BPF_DIV ? "/"  :
+              BPF_OP(iter->code) == BPF_MOD ? "%"  :
+              BPF_OP(iter->code) == BPF_OR  ? "|"  :
+              BPF_OP(iter->code) == BPF_XOR ? "^"  :
+              BPF_OP(iter->code) == BPF_AND ? "&"  :
+              BPF_OP(iter->code) == BPF_LSH ? "<<" :
+              BPF_OP(iter->code) == BPF_RSH ? ">>" : "???",
+              (int)iter->k);
       break;
     default:
       fprintf(stderr, "???\n");
@@ -145,6 +216,17 @@
   return;
 }
 
+void CodeGen::Traverse(Instruction *instruction,
+                       void (*fnc)(Instruction *, void *), void *aux) {
+  std::set<Instruction *> visited;
+  TraverseRecursively(&visited, instruction);
+  for (std::set<Instruction *>::const_iterator iter = visited.begin();
+       iter != visited.end();
+       ++iter) {
+    fnc(*iter, aux);
+  }
+}
+
 void CodeGen::FindBranchTargets(const Instruction& instructions,
                                 BranchTargets *branch_targets) {
   // Follow all possible paths through the "instructions" graph and compute
diff --git a/sandbox/linux/seccomp-bpf/codegen.h b/sandbox/linux/seccomp-bpf/codegen.h
index b7d1d39..88521c2 100644
--- a/sandbox/linux/seccomp-bpf/codegen.h
+++ b/sandbox/linux/seccomp-bpf/codegen.h
@@ -77,6 +77,15 @@
   // or if a (conditional) jump still has an unsatisfied target.
   void JoinInstructions(Instruction *head, Instruction *tail);
 
+  // Traverse the graph of instructions and visit each instruction once.
+  // Traversal order is implementation-defined. It is acceptable to make
+  // changes to the graph from within the callback function. These changes
+  // do not affect traversal.
+  // The "fnc" function gets called with both the instruction and the opaque
+  // "aux" pointer.
+  void Traverse(Instruction *, void (*fnc)(Instruction *, void *aux),
+                void *aux);
+
   // Compiles the graph of instructions into a BPF program that can be passed
   // to the kernel. Please note that this function modifies the graph in place
   // and must therefore only be called once per graph.
diff --git a/sandbox/linux/seccomp-bpf/demo.cc b/sandbox/linux/seccomp-bpf/demo.cc
index 02fd8a0..b2622ec 100644
--- a/sandbox/linux/seccomp-bpf/demo.cc
+++ b/sandbox/linux/seccomp-bpf/demo.cc
@@ -10,6 +10,7 @@
 #include <netinet/udp.h>
 #include <pthread.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -26,12 +27,10 @@
 #include <unistd.h>
 
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-#include "sandbox/linux/seccomp-bpf/util.h"
 
 using playground2::arch_seccomp_data;
 using playground2::ErrorCode;
 using playground2::Sandbox;
-using playground2::Util;
 
 #define ERR EPERM
 
@@ -41,6 +40,106 @@
 // actually enforce restrictions in a meaningful way:
 #define _exit(x) do { } while (0)
 
+namespace {
+
+bool SendFds(int transport, const void *buf, size_t len, ...) {
+  int count = 0;
+  va_list ap;
+  va_start(ap, len);
+  while (va_arg(ap, int) >= 0) {
+    ++count;
+  }
+  va_end(ap);
+  if (!count) {
+    return false;
+  }
+  char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
+  memset(cmsg_buf, 0, sizeof(cmsg_buf));
+  struct iovec  iov[2] = { { 0 } };
+  struct msghdr msg    = { 0 };
+  int dummy            = 0;
+  iov[0].iov_base      = &dummy;
+  iov[0].iov_len       = sizeof(dummy);
+  if (buf && len > 0) {
+    iov[1].iov_base    = const_cast<void *>(buf);
+    iov[1].iov_len     = len;
+  }
+  msg.msg_iov          = iov;
+  msg.msg_iovlen       = (buf && len > 0) ? 2 : 1;
+  msg.msg_control      = cmsg_buf;
+  msg.msg_controllen   = CMSG_LEN(count*sizeof(int));
+  struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level     = SOL_SOCKET;
+  cmsg->cmsg_type      = SCM_RIGHTS;
+  cmsg->cmsg_len       = CMSG_LEN(count*sizeof(int));
+  va_start(ap, len);
+  for (int i = 0, fd; (fd = va_arg(ap, int)) >= 0; ++i) {
+    (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i] = fd;
+  }
+  return sendmsg(transport, &msg, 0) ==
+      static_cast<ssize_t>(sizeof(dummy) + ((buf && len > 0) ? len : 0));
+}
+
+bool GetFds(int transport, void *buf, size_t *len, ...) {
+  int count = 0;
+  va_list ap;
+  va_start(ap, len);
+  for (int *fd; (fd = va_arg(ap, int *)) != NULL; ++count) {
+    *fd = -1;
+  }
+  va_end(ap);
+  if (!count) {
+    return false;
+  }
+  char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
+  memset(cmsg_buf, 0, sizeof(cmsg_buf));
+  struct iovec iov[2] = { { 0 } };
+  struct msghdr msg   = { 0 };
+  int err;
+  iov[0].iov_base     = &err;
+  iov[0].iov_len      = sizeof(int);
+  if (buf && len && *len > 0) {
+    iov[1].iov_base   = buf;
+    iov[1].iov_len    = *len;
+  }
+  msg.msg_iov         = iov;
+  msg.msg_iovlen      = (buf && len && *len > 0) ? 2 : 1;
+  msg.msg_control     = cmsg_buf;
+  msg.msg_controllen  = CMSG_LEN(count*sizeof(int));
+  ssize_t bytes = recvmsg(transport, &msg, 0);
+  if (len) {
+    *len = bytes > static_cast<int>(sizeof(int)) ? bytes - sizeof(int) : 0;
+  }
+  if (bytes != static_cast<ssize_t>(sizeof(int) + iov[1].iov_len)) {
+    if (bytes >= 0) {
+      errno = 0;
+    }
+    return false;
+  }
+  if (err) {
+    // "err" is the first four bytes of the payload. If these are non-zero,
+    // the sender on the other side of the socketpair sent us an errno value.
+    // We don't expect to get any file handles in this case.
+    errno = err;
+    return false;
+  }
+  struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+  if ((msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) ||
+      !cmsg                                    ||
+      cmsg->cmsg_level != SOL_SOCKET           ||
+      cmsg->cmsg_type  != SCM_RIGHTS           ||
+      cmsg->cmsg_len   != CMSG_LEN(count*sizeof(int))) {
+    errno = EBADF;
+    return false;
+  }
+  va_start(ap, len);
+  for (int *fd, i = 0; (fd = va_arg(ap, int *)) != NULL; ++i) {
+    *fd = (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i];
+  }
+  va_end(ap);
+  return true;
+}
+
 
 // POSIX doesn't define any async-signal safe function for converting
 // an integer to ASCII. We'll have to define our own version.
@@ -48,7 +147,7 @@
 // conversion was successful or NULL otherwise. It never writes more than "sz"
 // bytes. Output will be truncated as needed, and a NUL character is always
 // appended.
-static char *itoa_r(int i, char *buf, size_t sz) {
+char *itoa_r(int i, char *buf, size_t sz) {
   // Make sure we can write at least one NUL byte.
   size_t n = 1;
   if (n > sz) {
@@ -116,8 +215,7 @@
 // might try to evaluate the system call in user-space, instead.
 // The only notable complication is that this function must be async-signal
 // safe. This restricts the libary functions that we can call.
-static intptr_t defaultHandler(const struct arch_seccomp_data& data,
-                               void *) {
+intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) {
   static const char msg0[] = "Disallowed system call #";
   static const char msg1[] = "\n";
   char buf[sizeof(msg0) - 1 + 25 + sizeof(msg1)];
@@ -137,92 +235,102 @@
   return -ERR;
 }
 
-static ErrorCode evaluator(int sysno) {
+ErrorCode Evaluator(Sandbox *sandbox, int sysno, void *) {
   switch (sysno) {
-  #if defined(__NR_accept)
-    case __NR_accept: case __NR_accept4:
+#if defined(__NR_accept)
+  case __NR_accept: case __NR_accept4:
 #endif
-    case __NR_alarm:
-    case __NR_brk:
-    case __NR_clock_gettime:
-    case __NR_close:
-    case __NR_dup: case __NR_dup2:
-    case __NR_epoll_create: case __NR_epoll_ctl: case __NR_epoll_wait:
-    case __NR_exit: case __NR_exit_group:
-    case __NR_fcntl:
+  case __NR_alarm:
+  case __NR_brk:
+  case __NR_clock_gettime:
+  case __NR_close:
+  case __NR_dup: case __NR_dup2:
+  case __NR_epoll_create: case __NR_epoll_ctl: case __NR_epoll_wait:
+  case __NR_exit: case __NR_exit_group:
+  case __NR_fcntl:
 #if defined(__NR_fcntl64)
-    case __NR_fcntl64:
+  case __NR_fcntl64:
 #endif
-    case __NR_fdatasync:
-    case __NR_fstat:
+  case __NR_fdatasync:
+  case __NR_fstat:
 #if defined(__NR_fstat64)
-    case __NR_fstat64:
+  case __NR_fstat64:
 #endif
-    case __NR_ftruncate:
-    case __NR_futex:
-    case __NR_getdents: case __NR_getdents64:
-    case __NR_getegid:
+  case __NR_ftruncate:
+  case __NR_futex:
+  case __NR_getdents: case __NR_getdents64:
+  case __NR_getegid:
 #if defined(__NR_getegid32)
-    case __NR_getegid32:
+  case __NR_getegid32:
 #endif
-    case __NR_geteuid:
+  case __NR_geteuid:
 #if defined(__NR_geteuid32)
-    case __NR_geteuid32:
+  case __NR_geteuid32:
 #endif
-    case __NR_getgid:
+  case __NR_getgid:
 #if defined(__NR_getgid32)
-    case __NR_getgid32:
+  case __NR_getgid32:
 #endif
-    case __NR_getitimer: case __NR_setitimer:
+  case __NR_getitimer: case __NR_setitimer:
 #if defined(__NR_getpeername)
-    case __NR_getpeername:
+  case __NR_getpeername:
 #endif
-    case __NR_getpid: case __NR_gettid:
+  case __NR_getpid: case __NR_gettid:
 #if defined(__NR_getsockname)
-    case __NR_getsockname:
+  case __NR_getsockname:
 #endif
-    case __NR_gettimeofday:
-    case __NR_getuid:
+  case __NR_gettimeofday:
+  case __NR_getuid:
 #if defined(__NR_getuid32)
-    case __NR_getuid32:
+  case __NR_getuid32:
 #endif
 #if defined(__NR__llseek)
-    case __NR__llseek:
+  case __NR__llseek:
 #endif
-    case __NR_lseek:
-    case __NR_nanosleep:
-    case __NR_pipe: case __NR_pipe2:
-    case __NR_poll:
-    case __NR_pread64: case __NR_preadv:
-    case __NR_pwrite64: case __NR_pwritev:
-    case __NR_read: case __NR_readv:
-    case __NR_restart_syscall:
-    case __NR_set_robust_list:
-    case __NR_rt_sigaction:
+  case __NR_lseek:
+  case __NR_nanosleep:
+  case __NR_pipe: case __NR_pipe2:
+  case __NR_poll:
+  case __NR_pread64: case __NR_preadv:
+  case __NR_pwrite64: case __NR_pwritev:
+  case __NR_read: case __NR_readv:
+  case __NR_restart_syscall:
+  case __NR_set_robust_list:
+  case __NR_rt_sigaction:
 #if defined(__NR_sigaction)
-    case __NR_sigaction:
+  case __NR_sigaction:
 #endif
 #if defined(__NR_signal)
-    case __NR_signal:
+  case __NR_signal:
 #endif
-    case __NR_rt_sigprocmask:
+  case __NR_rt_sigprocmask:
 #if defined(__NR_sigprocmask)
-    case __NR_sigprocmask:
+  case __NR_sigprocmask:
 #endif
 #if defined(__NR_shutdown)
-    case __NR_shutdown:
+  case __NR_shutdown:
 #endif
-    case __NR_rt_sigreturn:
+  case __NR_rt_sigreturn:
 #if defined(__NR_sigreturn)
-    case __NR_sigreturn:
+  case __NR_sigreturn:
 #endif
 #if defined(__NR_socketpair)
-    case __NR_socketpair:
+  case __NR_socketpair:
 #endif
-    case __NR_time:
-    case __NR_uname:
-    case __NR_write: case __NR_writev:
-      return ErrorCode(ErrorCode::ERR_ALLOWED);
+  case __NR_time:
+  case __NR_uname:
+  case __NR_write: case __NR_writev:
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+
+  case __NR_prctl:
+    // Allow PR_SET_DUMPABLE and PR_GET_DUMPABLE. Do not allow anything else.
+    return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+                         PR_SET_DUMPABLE,
+                         ErrorCode(ErrorCode::ERR_ALLOWED),
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+                         PR_GET_DUMPABLE,
+                         ErrorCode(ErrorCode::ERR_ALLOWED),
+           sandbox->Trap(DefaultHandler, NULL)));
 
   // The following system calls are temporarily permitted. This must be
   // tightened later. But we currently don't implement enough of the sandboxing
@@ -250,7 +358,6 @@
 #endif
   case __NR_getrlimit:
   case __NR_ioctl:
-  case __NR_prctl:
   case __NR_clone:
   case __NR_munmap: case __NR_mprotect: case __NR_madvise:
   case __NR_remap_file_pages:
@@ -258,15 +365,15 @@
 
   // Everything that isn't explicitly allowed is denied.
   default:
-    return Sandbox::Trap(defaultHandler, NULL);
+    return sandbox->Trap(DefaultHandler, NULL);
   }
 }
 
-static void *threadFnc(void *arg) {
+void *ThreadFnc(void *arg) {
   return arg;
 }
 
-static void *sendmsgStressThreadFnc(void *arg) {
+void *SendmsgStressThreadFnc(void *arg) {
   if (arg) { }
   static const int repetitions = 100;
   static const int kNumFds = 3;
@@ -278,8 +385,8 @@
     }
     size_t len = 4;
     char buf[4];
-    if (!Util::sendFds(fds[0], "test", 4, fds[1], fds[1], fds[1], -1) ||
-        !Util::getFds(fds[1], buf, &len, fds+2, fds+3, fds+4, NULL) ||
+    if (!SendFds(fds[0], "test", 4, fds[1], fds[1], fds[1], -1) ||
+        !GetFds(fds[1], buf, &len, fds+2, fds+3, fds+4, NULL) ||
         len != 4 ||
         memcmp(buf, "test", len) ||
         write(fds[2], "demo", 4) != 4 ||
@@ -298,22 +405,25 @@
   return NULL;
 }
 
+}  // namespace
+
 int main(int argc, char *argv[]) {
   if (argc) { }
   if (argv) { }
   int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY);
-  if (Sandbox::supportsSeccompSandbox(proc_fd) !=
+  if (Sandbox::SupportsSeccompSandbox(proc_fd) !=
       Sandbox::STATUS_AVAILABLE) {
     perror("sandbox");
     _exit(1);
   }
-  Sandbox::setProcFd(proc_fd);
-  Sandbox::setSandboxPolicy(evaluator, NULL);
-  Sandbox::startSandbox();
+  Sandbox sandbox;
+  sandbox.set_proc_fd(proc_fd);
+  sandbox.SetSandboxPolicy(Evaluator, NULL);
+  sandbox.StartSandbox();
 
   // Check that we can create threads
   pthread_t thr;
-  if (!pthread_create(&thr, NULL, threadFnc,
+  if (!pthread_create(&thr, NULL, ThreadFnc,
                       reinterpret_cast<void *>(0x1234))) {
     void *ret;
     pthread_join(thr, &ret);
@@ -367,8 +477,8 @@
   }
   size_t len = 4;
   char buf[4];
-  if (!Util::sendFds(fds[0], "test", 4, fds[1], -1) ||
-      !Util::getFds(fds[1], buf, &len, fds+2, NULL) ||
+  if (!SendFds(fds[0], "test", 4, fds[1], -1) ||
+      !GetFds(fds[1], buf, &len, fds+2, NULL) ||
       len != 4 ||
       memcmp(buf, "test", len) ||
       write(fds[2], "demo", 4) != 4 ||
@@ -401,7 +511,7 @@
   pthread_t sendmsgStressThreads[kSendmsgStressNumThreads];
   for (int i = 0; i < kSendmsgStressNumThreads; ++i) {
     if (pthread_create(sendmsgStressThreads + i, NULL,
-                       sendmsgStressThreadFnc, NULL)) {
+                       SendmsgStressThreadFnc, NULL)) {
       perror("pthread_create");
       _exit(1);
     }
diff --git a/sandbox/linux/seccomp-bpf/die.cc b/sandbox/linux/seccomp-bpf/die.cc
index b141424..4962c4d 100644
--- a/sandbox/linux/seccomp-bpf/die.cc
+++ b/sandbox/linux/seccomp-bpf/die.cc
@@ -2,9 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <errno.h>
+#include <linux/unistd.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+
 #include <string>
 
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
 
 
 namespace playground2 {
@@ -15,7 +21,7 @@
   // Especially, since we are dealing with system call filters. Continuing
   // execution would be very bad in most cases where ExitGroup() gets called.
   // So, we'll try a few other strategies too.
-  syscall(__NR_exit_group, 1);
+  SandboxSyscall(__NR_exit_group, 1);
 
   // We have no idea what our run-time environment looks like. So, signal
   // handlers might or might not do the right thing. Try to reset settings
@@ -23,7 +29,7 @@
   // succeeded in doing so. Nonetheless, triggering a fatal signal could help
   // us terminate.
   signal(SIGSEGV, SIG_DFL);
-  syscall(__NR_prctl, PR_SET_DUMPABLE, (void *)0, (void *)0, (void *)0);
+  SandboxSyscall(__NR_prctl, PR_SET_DUMPABLE, (void *)0, (void *)0, (void *)0);
   if (*(volatile char *)0) { }
 
   // If there is no way for us to ask for the program to exit, the next
@@ -32,7 +38,7 @@
   // We in fact retry the system call inside of our loop so that it will
   // stand out when somebody tries to diagnose the problem by using "strace".
   for (;;) {
-    syscall(__NR_exit_group, 1);
+    SandboxSyscall(__NR_exit_group, 1);
   }
 }
 
@@ -49,6 +55,16 @@
   ExitGroup();
 }
 
+void Die::SandboxInfo(const char *msg, const char *file, int line) {
+  if (!suppress_info_) {
+  #if defined(SECCOMP_BPF_STANDALONE)
+    Die::LogToStderr(msg, file, line);
+  #else
+    logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg;
+  #endif
+  }
+}
+
 void Die::LogToStderr(const char *msg, const char *file, int line) {
   if (msg) {
     char buf[40];
@@ -57,10 +73,11 @@
 
     // No need to loop. Short write()s are unlikely and if they happen we
     // probably prefer them over a loop that blocks.
-    if (HANDLE_EINTR(write(2, s.c_str(), s.length()))) { }
+    if (HANDLE_EINTR(SandboxSyscall(__NR_write, 2, s.c_str(), s.length()))) { }
   }
 }
 
-bool Die::simple_exit_ = false;
+bool Die::simple_exit_   = false;
+bool Die::suppress_info_ = false;
 
 }  // namespace
diff --git a/sandbox/linux/seccomp-bpf/die.h b/sandbox/linux/seccomp-bpf/die.h
index 608afde..f15f108 100644
--- a/sandbox/linux/seccomp-bpf/die.h
+++ b/sandbox/linux/seccomp-bpf/die.h
@@ -5,13 +5,19 @@
 #ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
 #define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
 
+#include "sandbox/linux/seccomp-bpf/port.h"
+
+
 namespace playground2 {
 
 class Die {
  public:
   // This is the main API for using this file. Prints a error message and
   // exits with a fatal error.
-  #define SANDBOX_DIE(m) Die::SandboxDie(m, __FILE__, __LINE__)
+  #define SANDBOX_DIE(m) playground2::Die::SandboxDie(m, __FILE__, __LINE__)
+
+  // Adds an informational message to the log file or stderr as appropriate.
+  #define SANDBOX_INFO(m) playground2::Die::SandboxInfo(m, __FILE__, __LINE__)
 
   // Terminate the program, even if the current sandbox policy prevents some
   // of the more commonly used functions used for exiting.
@@ -25,6 +31,10 @@
   static void SandboxDie(const char *msg, const char *file, int line)
     __attribute__((noreturn));
 
+  // This method gets called by SANDBOX_INFO(). There is normally no reason
+  // to call it directly unless you are defining your own logging macro.
+  static void SandboxInfo(const char *msg, const char *file, int line);
+
   // Writes a message to stderr. Used as a fall-back choice, if we don't have
   // any other way to report an error.
   static void LogToStderr(const char *msg, const char *file, int line);
@@ -36,8 +46,13 @@
   // unit tests or in the supportsSeccompSandbox() method).
   static void EnableSimpleExit() { simple_exit_ = true; }
 
+  // Sometimes we need to disable all informational messages (e.g. from within
+  // unittests).
+  static void SuppressInfoMessages(bool flag) { suppress_info_ = flag; }
+
  private:
   static bool simple_exit_;
+  static bool suppress_info_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(Die);
 };
diff --git a/sandbox/linux/seccomp-bpf/errorcode.cc b/sandbox/linux/seccomp-bpf/errorcode.cc
index cc79cb6..ab89d73 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.cc
+++ b/sandbox/linux/seccomp-bpf/errorcode.cc
@@ -2,7 +2,8 @@
 // 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.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
 
 
 namespace playground2 {
@@ -22,10 +23,12 @@
   }
 }
 
-ErrorCode::ErrorCode(ErrorCode::TrapFnc fnc, const void *aux, uint16_t id)
+ErrorCode::ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe,
+                     uint16_t id)
     : error_type_(ET_TRAP),
       fnc_(fnc),
       aux_(const_cast<void *>(aux)),
+      safe_(safe),
       err_(SECCOMP_RET_TRAP + id) {
 }
 
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
index 2b941ee..61ec110 100644
--- a/sandbox/linux/seccomp-bpf/errorcode.h
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -5,14 +5,17 @@
 #ifndef SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
 #define SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
 
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
 namespace playground2 {
 
 struct arch_seccomp_data;
 
-// This class holds all the possible values that can returned by a sandbox
+// This class holds all the possible values that can be returned by a sandbox
 // policy.
 // We can either wrap a symbolic ErrorCode (i.e. ERR_XXX enum values), an
-// errno value (in the range 1..4095), a pointer to a TrapFnc callback
+// errno value (in the range 0..4095), a pointer to a TrapFnc callback
 // handling a SECCOMP_RET_TRAP trap, or a complex constraint.
 // All of the commonly used values are stored in the "err_" field. So, code
 // that is using the ErrorCode class typically operates on a single 32bit
@@ -20,36 +23,85 @@
 class ErrorCode {
  public:
   enum {
-    // Allow this system call.
-    ERR_ALLOWED   = 0x0000,
+    // Allow this system call. The value of ERR_ALLOWED is pretty much
+    // completely arbitrary. But we want to pick it so that is is unlikely
+    // to be passed in accidentally, when the user intended to return an
+    // "errno" (see below) value instead.
+    ERR_ALLOWED   = 0x04000000,
 
     // Deny the system call with a particular "errno" value.
-    ERR_MIN_ERRNO = 1,
+    // N.B.: It is also possible to return "0" here. That would normally
+    //       indicate success, but it won't actually run the system call.
+    //       This is very different from return ERR_ALLOWED.
+    ERR_MIN_ERRNO = 0,
+    // TODO(markus): Android only supports errno up to 255
+    // (crbug.com/181647).
     ERR_MAX_ERRNO = 4095,
-
-    // This code should never be used directly, it is used internally only.
-    ERR_INVALID   = -1,
   };
 
-  // TrapFnc is a pointer to a function that handles Seccomp traps in
-  // user-space. The seccomp policy can request that a trap handler gets
-  // installed; it does so by returning a suitable ErrorCode() from the
-  // syscallEvaluator. See the ErrorCode() constructor for how to pass in
-  // the function pointer.
-  // Please note that TrapFnc is executed from signal context and must be
-  // async-signal safe:
-  // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
-  typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
-
+  // While BPF filter programs always operate on 32bit quantities, the kernel
+  // always sees system call arguments as 64bit values. This statement is true
+  // no matter whether the host system is natively operating in 32bit or 64bit.
+  // The BPF compiler hides the fact that BPF instructions cannot directly
+  // access 64bit quantities. But policies are still advised to specify whether
+  // a system call expects a 32bit or a 64bit quantity.
   enum ArgType {
-    TP_32BIT, TP_64BIT,
+    // When passed as an argument to Sandbox::Cond(), TP_32BIT requests that
+    // the conditional test should operate on the 32bit part of the system call
+    // argument.
+    // On 64bit architectures, this verifies that user space did not pass
+    // a 64bit value as an argument to the system call. If it did, that will be
+    // interpreted as an attempt at breaking the sandbox and results in the
+    // program getting terminated.
+    // In other words, only perform a 32bit test, if you are sure this
+    // particular system call would never legitimately take a 64bit
+    // argument.
+    // Implementation detail: TP_32BIT does two things. 1) it restricts the
+    // conditional test to operating on the LSB only, and 2) it adds code to
+    // the BPF filter program verifying that the MSB  the kernel received from
+    // user space is either 0, or 0xFFFFFFFF; the latter is acceptable, iff bit
+    // 31 was set in the system call argument. It deals with 32bit arguments
+    // having been sign extended.
+    TP_32BIT,
+
+    // When passed as an argument to Sandbox::Cond(), TP_64BIT requests that
+    // the conditional test should operate on the full 64bit argument. It is
+    // generally harmless to perform a 64bit test on 32bit systems, as the
+    // kernel will always see the top 32 bits of all arguments as zero'd out.
+    // This approach has the desirable property that for tests of pointer
+    // values, we can always use TP_64BIT no matter the host architecture.
+    // But of course, that also means, it is possible to write conditional
+    // policies that turn into no-ops on 32bit systems; this is by design.
+    TP_64BIT,
   };
 
   enum Operation {
-    OP_EQUAL, OP_GREATER, OP_GREATER_EQUAL, OP_HAS_BITS,
+    // Test whether the system call argument is equal to the operand.
+    OP_EQUAL,
+
+    // Test whether the system call argument is greater (or equal) to the
+    // operand. Please note that all tests always operate on unsigned
+    // values. You can generally emulate signed tests, if that's what you
+    // need.
+    // TODO(markus): Check whether we should automatically emulate signed
+    //               operations.
+    OP_GREATER_UNSIGNED, OP_GREATER_EQUAL_UNSIGNED,
+
+    // Tests a system call argument against a bit mask.
+    // The "ALL_BITS" variant performs this test: "arg & mask == mask"
+    // This implies that a mask of zero always results in a passing test.
+    // The "ANY_BITS" variant performs this test: "arg & mask != 0"
+    // This implies that a mask of zero always results in a failing test.
+    OP_HAS_ALL_BITS, OP_HAS_ANY_BITS,
+
+    // Total number of operations.
     OP_NUM_OPS,
   };
 
+  enum ErrorType {
+    ET_INVALID, ET_SIMPLE, ET_TRAP, ET_COND,
+  };
+
   // We allow the default constructor, as it makes the ErrorCode class
   // much easier to use. But if we ever encounter an invalid ErrorCode
   // when compiling a BPF filter, we deliberately generate an invalid
@@ -75,6 +127,16 @@
   bool LessThan(const ErrorCode& err) const;
 
   uint32_t err() const { return err_; }
+  ErrorType error_type() const { return error_type_; }
+
+  bool safe() const { return safe_; }
+
+  uint64_t value() const { return value_; }
+  int argno() const { return argno_; }
+  ArgType width() const { return width_; }
+  Operation op() const { return op_; }
+  const ErrorCode *passed() const { return passed_; }
+  const ErrorCode *failed() const { return failed_; }
 
   struct LessThan {
     bool operator()(const ErrorCode& a, const ErrorCode& b) const {
@@ -85,16 +147,12 @@
  private:
   friend class CodeGen;
   friend class Sandbox;
-  friend class Verifier;
-
-  enum ErrorType {
-    ET_INVALID, ET_SIMPLE, ET_TRAP, ET_COND,
-  };
+  friend class Trap;
 
   // If we are wrapping a callback, we must assign a unique id. This id is
   // how the kernel tells us which one of our different SECCOMP_RET_TRAP
   // cases has been triggered.
-  ErrorCode(TrapFnc fnc, const void *aux, uint16_t id);
+  ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe, uint16_t id);
 
   // Some system calls require inspection of arguments. This constructor
   // allows us to specify additional constraints.
@@ -106,8 +164,9 @@
   union {
     // Fields needed for SECCOMP_RET_TRAP callbacks
     struct {
-      TrapFnc fnc_;              // Callback function and arg, if trap was
-      void    *aux_;             //   triggered by the kernel's BPF filter.
+      Trap::TrapFnc fnc_;        // Callback function and arg, if trap was
+      void          *aux_;       //   triggered by the kernel's BPF filter.
+      bool          safe_;       // Keep sandbox active while calling fnc_()
     };
 
     // Fields needed when inspecting additional arguments.
diff --git a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
index 21f889e..10f2132 100644
--- a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -19,17 +19,19 @@
   ErrorCode e2(EPERM);
   SANDBOX_ASSERT(e2.err() == SECCOMP_RET_ERRNO + EPERM);
 
-  ErrorCode e3 = Sandbox::Trap(NULL, NULL);
+  Sandbox sandbox;
+  ErrorCode e3 = sandbox.Trap(NULL, NULL);
   SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION)  == SECCOMP_RET_TRAP);
 }
 
 SANDBOX_TEST(ErrorCode, Trap) {
-  ErrorCode e0 = Sandbox::Trap(NULL, "a");
-  ErrorCode e1 = Sandbox::Trap(NULL, "b");
+  Sandbox sandbox;
+  ErrorCode e0 = sandbox.Trap(NULL, "a");
+  ErrorCode e1 = sandbox.Trap(NULL, "b");
   SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 ==
                  (e1.err() & SECCOMP_RET_DATA));
 
-  ErrorCode e2 = Sandbox::Trap(NULL, "a");
+  ErrorCode e2 = sandbox.Trap(NULL, "a");
   SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) ==
                  (e2.err() & SECCOMP_RET_DATA));
 }
@@ -44,9 +46,10 @@
   ErrorCode e3(EPERM);
   SANDBOX_ASSERT(!e1.Equals(e3));
 
-  ErrorCode e4 = Sandbox::Trap(NULL, "a");
-  ErrorCode e5 = Sandbox::Trap(NULL, "b");
-  ErrorCode e6 = Sandbox::Trap(NULL, "a");
+  Sandbox sandbox;
+  ErrorCode e4 = sandbox.Trap(NULL, "a");
+  ErrorCode e5 = sandbox.Trap(NULL, "b");
+  ErrorCode e6 = sandbox.Trap(NULL, "a");
   SANDBOX_ASSERT(!e1.Equals(e4));
   SANDBOX_ASSERT(!e3.Equals(e4));
   SANDBOX_ASSERT(!e5.Equals(e4));
@@ -64,9 +67,10 @@
   SANDBOX_ASSERT(!e1.LessThan(e3));
   SANDBOX_ASSERT( e3.LessThan(e1));
 
-  ErrorCode e4 = Sandbox::Trap(NULL, "a");
-  ErrorCode e5 = Sandbox::Trap(NULL, "b");
-  ErrorCode e6 = Sandbox::Trap(NULL, "a");
+  Sandbox sandbox;
+  ErrorCode e4 = sandbox.Trap(NULL, "a");
+  ErrorCode e5 = sandbox.Trap(NULL, "b");
+  ErrorCode e6 = sandbox.Trap(NULL, "a");
   SANDBOX_ASSERT(e1.LessThan(e4));
   SANDBOX_ASSERT(e3.LessThan(e4));
   SANDBOX_ASSERT(e4.LessThan(e5));
diff --git a/sandbox/linux/seccomp-bpf/linux_seccomp.h b/sandbox/linux/seccomp-bpf/linux_seccomp.h
new file mode 100644
index 0000000..0de0259
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/linux_seccomp.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 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_LINUX_SECCOMP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
+
+// The Seccomp2 kernel ABI is not part of older versions of glibc.
+// As we can't break compilation with these versions of the library,
+// we explicitly define all missing symbols.
+// If we ever decide that we can now rely on system headers, the following
+// include files should be enabled:
+// #include <linux/audit.h>
+// #include <linux/seccomp.h>
+
+#include <asm/unistd.h>
+#include <linux/filter.h>
+
+// For audit.h
+#ifndef EM_ARM
+#define EM_ARM    40
+#endif
+#ifndef EM_386
+#define EM_386    3
+#endif
+#ifndef EM_X86_64
+#define EM_X86_64 62
+#endif
+
+#ifndef __AUDIT_ARCH_64BIT
+#define __AUDIT_ARCH_64BIT 0x80000000
+#endif
+#ifndef __AUDIT_ARCH_LE
+#define __AUDIT_ARCH_LE    0x40000000
+#endif
+#ifndef AUDIT_ARCH_ARM
+#define AUDIT_ARCH_ARM    (EM_ARM|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_I386
+#define AUDIT_ARCH_I386   (EM_386|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_X86_64
+#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#endif
+
+// For prctl.h
+#ifndef PR_SET_SECCOMP
+#define PR_SET_SECCOMP               22
+#define PR_GET_SECCOMP               21
+#endif
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS          38
+#define PR_GET_NO_NEW_PRIVS          39
+#endif
+#ifndef IPC_64
+#define IPC_64                   0x0100
+#endif
+
+#ifndef BPF_MOD
+#define BPF_MOD                    0x90
+#endif
+#ifndef BPF_XOR
+#define BPF_XOR                    0xA0
+#endif
+
+// In order to build will older tool chains, we currently have to avoid
+// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
+// our own definitions of the seccomp kernel ABI.
+#ifndef SECCOMP_MODE_FILTER
+#define SECCOMP_MODE_DISABLED         0
+#define SECCOMP_MODE_STRICT           1
+#define SECCOMP_MODE_FILTER           2  // User user-supplied filter
+#endif
+
+#ifndef SECCOMP_RET_KILL
+// Return values supported for BPF filter programs. Please note that the
+// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
+// ever be used internally, and would result in the kernel killing our process.
+#define SECCOMP_RET_KILL    0x00000000U  // Kill the task immediately
+#define SECCOMP_RET_INVALID 0x00010000U  // Illegal return value
+#define SECCOMP_RET_TRAP    0x00030000U  // Disallow and force a SIGSYS
+#define SECCOMP_RET_ERRNO   0x00050000U  // Returns an errno
+#define SECCOMP_RET_TRACE   0x7ff00000U  // Pass to a tracer or disallow
+#define SECCOMP_RET_ALLOW   0x7fff0000U  // Allow
+#define SECCOMP_RET_ACTION  0xffff0000U  // Masks for the return value
+#define SECCOMP_RET_DATA    0x0000ffffU  //   sections
+#else
+#define SECCOMP_RET_INVALID 0x00010000U  // Illegal return value
+#endif
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP                   1
+#endif
+
+// Impose some reasonable maximum BPF program size. Realistically, the
+// kernel probably has much lower limits. But by limiting to less than
+// 30 bits, we can ease requirements on some of our data types.
+#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
+
+#if defined(__i386__)
+#define MIN_SYSCALL         0u
+#define MAX_PUBLIC_SYSCALL  1024u
+#define MAX_SYSCALL         MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH        AUDIT_ARCH_I386
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx)    SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_SYSCALL(_ctx)   SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_IP(_ctx)        SECCOMP_REG(_ctx, REG_EIP)
+#define SECCOMP_PARM1(_ctx)     SECCOMP_REG(_ctx, REG_EBX)
+#define SECCOMP_PARM2(_ctx)     SECCOMP_REG(_ctx, REG_ECX)
+#define SECCOMP_PARM3(_ctx)     SECCOMP_REG(_ctx, REG_EDX)
+#define SECCOMP_PARM4(_ctx)     SECCOMP_REG(_ctx, REG_ESI)
+#define SECCOMP_PARM5(_ctx)     SECCOMP_REG(_ctx, REG_EDI)
+#define SECCOMP_PARM6(_ctx)     SECCOMP_REG(_ctx, REG_EBP)
+#define SECCOMP_NR_IDX          (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX        (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX      (offsetof(struct arch_seccomp_data,           \
+                                          instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX      (offsetof(struct arch_seccomp_data,           \
+                                          instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) +   \
+                                 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) +   \
+                                 8*(nr) + 0)
+
+#elif defined(__x86_64__)
+#define MIN_SYSCALL         0u
+#define MAX_PUBLIC_SYSCALL  1024u
+#define MAX_SYSCALL         MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH        AUDIT_ARCH_X86_64
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx)    SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_SYSCALL(_ctx)   SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_IP(_ctx)        SECCOMP_REG(_ctx, REG_RIP)
+#define SECCOMP_PARM1(_ctx)     SECCOMP_REG(_ctx, REG_RDI)
+#define SECCOMP_PARM2(_ctx)     SECCOMP_REG(_ctx, REG_RSI)
+#define SECCOMP_PARM3(_ctx)     SECCOMP_REG(_ctx, REG_RDX)
+#define SECCOMP_PARM4(_ctx)     SECCOMP_REG(_ctx, REG_R10)
+#define SECCOMP_PARM5(_ctx)     SECCOMP_REG(_ctx, REG_R8)
+#define SECCOMP_PARM6(_ctx)     SECCOMP_REG(_ctx, REG_R9)
+#define SECCOMP_NR_IDX          (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX        (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX      (offsetof(struct arch_seccomp_data,           \
+                                          instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX      (offsetof(struct arch_seccomp_data,           \
+                                          instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) +   \
+                                 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) +   \
+                                 8*(nr) + 0)
+
+#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
+// and a "ghost syscall private to the kernel", cmpxchg,
+// at |__ARM_NR_BASE+0x00fff0|.
+// See </arch/arm/include/asm/unistd.h> in the Linux kernel.
+#define MIN_SYSCALL         ((unsigned int)__NR_SYSCALL_BASE)
+#define MAX_PUBLIC_SYSCALL  (MIN_SYSCALL + 1024u)
+#define MIN_PRIVATE_SYSCALL ((unsigned int)__ARM_NR_BASE)
+#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
+#define MIN_GHOST_SYSCALL   ((unsigned int)__ARM_NR_BASE + 0xfff0u)
+#define MAX_SYSCALL         (MIN_GHOST_SYSCALL + 4u)
+
+#define SECCOMP_ARCH AUDIT_ARCH_ARM
+
+// ARM sigcontext_t is different from i386/x86_64.
+// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
+// ARM EABI syscall convention.
+#define SECCOMP_RESULT(_ctx)    SECCOMP_REG(_ctx, r0)
+#define SECCOMP_SYSCALL(_ctx)   SECCOMP_REG(_ctx, r7)
+#define SECCOMP_IP(_ctx)        SECCOMP_REG(_ctx, pc)
+#define SECCOMP_PARM1(_ctx)     SECCOMP_REG(_ctx, r0)
+#define SECCOMP_PARM2(_ctx)     SECCOMP_REG(_ctx, r1)
+#define SECCOMP_PARM3(_ctx)     SECCOMP_REG(_ctx, r2)
+#define SECCOMP_PARM4(_ctx)     SECCOMP_REG(_ctx, r3)
+#define SECCOMP_PARM5(_ctx)     SECCOMP_REG(_ctx, r4)
+#define SECCOMP_PARM6(_ctx)     SECCOMP_REG(_ctx, r5)
+#define SECCOMP_NR_IDX          (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX        (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX      (offsetof(struct arch_seccomp_data,           \
+                                          instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX      (offsetof(struct arch_seccomp_data,           \
+                                          instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) +   \
+                                 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) +   \
+                                 8*(nr) + 0)
+
+#else
+#error Unsupported target platform
+
+#endif
+
+#endif  // SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
diff --git a/sandbox/linux/seccomp-bpf/port.h b/sandbox/linux/seccomp-bpf/port.h
new file mode 100644
index 0000000..f10b148
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/port.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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.
+
+// Commonly used macro definitions to make the code build in different
+// target environments (e.g. as part of Chrome vs. stand-alone)
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
+
+#if !defined(SECCOMP_BPF_STANDALONE)
+  #include "base/basictypes.h"
+  #include "base/logging.h"
+  #include "base/posix/eintr_wrapper.h"
+#else
+  #define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+  #define HANDLE_EINTR TEMP_FAILURE_RETRY
+
+  #define DISALLOW_COPY_AND_ASSIGN(TypeName)       \
+    TypeName(const TypeName&);                     \
+    void operator=(const TypeName&)
+
+  #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+    TypeName();                                    \
+    DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+  template <bool>
+  struct CompileAssert {
+  };
+
+  #define COMPILE_ASSERT(expr, msg) \
+    typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+#endif
+
+#endif  // SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index eb03995..1dc5eae 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -2,34 +2,69 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// Some headers on Android are missing cdefs: crbug.com/172337.
+// (We can't use OS_ANDROID here since build_config.h is not included).
+#if defined(ANDROID)
+#include <sys/cdefs.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef SECCOMP_BPF_STANDALONE
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#endif
+
 #include "sandbox/linux/seccomp-bpf/codegen.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
 #include "sandbox/linux/seccomp-bpf/verifier.h"
 
 namespace {
 
+using playground2::ErrorCode;
+using playground2::Instruction;
+using playground2::Sandbox;
+using playground2::Trap;
+using playground2::arch_seccomp_data;
+
+const int kExpectedExitCode = 100;
+
+template<class T> int popcount(T x);
+template<> int popcount<unsigned int>(unsigned int x) {
+  return __builtin_popcount(x);
+}
+template<> int popcount<unsigned long>(unsigned long x) {
+  return __builtin_popcountl(x);
+}
+template<> int popcount<unsigned long long>(unsigned long long x) {
+  return __builtin_popcountll(x);
+}
+
 void WriteFailedStderrSetupMessage(int out_fd) {
   const char* error_string = strerror(errno);
-  static const char msg[] = "Failed to set up stderr: ";
+  static const char msg[] = "You have reproduced a puzzling issue.\n"
+                            "Please, report to crbug.com/152530!\n"
+                            "Failed to set up stderr: ";
   if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg)-1)) > 0 && error_string &&
       HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 &&
       HANDLE_EINTR(write(out_fd, "\n", 1))) {
   }
 }
 
-}  // namespace
-
-// The kernel gives us a sandbox, we turn it into a playground :-)
-// This is version 2 of the playground; version 1 was built on top of
-// pre-BPF seccomp mode.
-namespace playground2 {
-
-const int kExpectedExitCode = 100;
-
 // We define a really simple sandbox policy. It is just good enough for us
 // to tell that the sandbox has actually been activated.
-ErrorCode Sandbox::probeEvaluator(int sysnum, void *) {
+ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) __attribute__((const));
+ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) {
   switch (sysnum) {
   case __NR_getpid:
     // Return EPERM so that we can check that the filter actually ran.
@@ -43,24 +78,20 @@
   }
 }
 
-void Sandbox::probeProcess(void) {
+void ProbeProcess(void) {
   if (syscall(__NR_getpid) < 0 && errno == EPERM) {
     syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
   }
 }
 
-bool Sandbox::isValidSyscallNumber(int sysnum) {
-  return SyscallIterator::IsValid(sysnum);
-}
-
-ErrorCode Sandbox::allowAllEvaluator(int sysnum, void *) {
-  if (!isValidSyscallNumber(sysnum)) {
+ErrorCode AllowAllEvaluator(Sandbox *, int sysnum, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysnum)) {
     return ErrorCode(ENOSYS);
   }
   return ErrorCode(ErrorCode::ERR_ALLOWED);
 }
 
-void Sandbox::tryVsyscallProcess(void) {
+void TryVsyscallProcess(void) {
   time_t current_time;
   // time() is implemented as a vsyscall. With an older glibc, with
   // vsyscall=emulate and some versions of the seccomp BPF patch
@@ -70,15 +101,156 @@
   }
 }
 
-bool Sandbox::RunFunctionInPolicy(void (*CodeInSandbox)(),
-                                  EvaluateSyscall syscallEvaluator,
-                                  void *aux,
-                                  int proc_fd) {
+bool IsSingleThreaded(int proc_fd) {
+  if (proc_fd < 0) {
+    // Cannot determine whether program is single-threaded. Hope for
+    // the best...
+    return true;
+  }
+
+  struct stat sb;
+  int task = -1;
+  if ((task = openat(proc_fd, "self/task", O_RDONLY|O_DIRECTORY)) < 0 ||
+      fstat(task, &sb) != 0 ||
+      sb.st_nlink != 3 ||
+      HANDLE_EINTR(close(task))) {
+    if (task >= 0) {
+      if (HANDLE_EINTR(close(task))) { }
+    }
+    return false;
+  }
+  return true;
+}
+
+bool IsDenied(const ErrorCode& code) {
+  return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
+         (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
+          code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
+}
+
+// Function that can be passed as a callback function to CodeGen::Traverse().
+// Checks whether the "insn" returns an UnsafeTrap() ErrorCode. If so, it
+// sets the "bool" variable pointed to by "aux".
+void CheckForUnsafeErrorCodes(Instruction *insn, void *aux) {
+  bool *is_unsafe = static_cast<bool *>(aux);
+  if (!*is_unsafe) {
+    if (BPF_CLASS(insn->code) == BPF_RET &&
+        insn->k > SECCOMP_RET_TRAP &&
+        insn->k - SECCOMP_RET_TRAP <= SECCOMP_RET_DATA) {
+      const ErrorCode& err =
+        Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA);
+      if (err.error_type() != ErrorCode::ET_INVALID && !err.safe()) {
+        *is_unsafe = true;
+      }
+    }
+  }
+}
+
+// A Trap() handler that returns an "errno" value. The value is encoded
+// in the "aux" parameter.
+intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux) {
+  // TrapFnc functions report error by following the native kernel convention
+  // of returning an exit code in the range of -1..-4096. They do not try to
+  // set errno themselves. The glibc wrapper that triggered the SIGSYS will
+  // ultimately do so for us.
+  int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
+  return -err;
+}
+
+// Function that can be passed as a callback function to CodeGen::Traverse().
+// Checks whether the "insn" returns an errno value from a BPF filter. If so,
+// it rewrites the instruction to instead call a Trap() handler that does
+// the same thing. "aux" is ignored.
+void RedirectToUserspace(Instruction *insn, void *aux) {
+  // When inside an UnsafeTrap() callback, we want to allow all system calls.
+  // This means, we must conditionally disable the sandbox -- and that's not
+  // something that kernel-side BPF filters can do, as they cannot inspect
+  // any state other than the syscall arguments.
+  // But if we redirect all error handlers to user-space, then we can easily
+  // make this decision.
+  // The performance penalty for this extra round-trip to user-space is not
+  // actually that bad, as we only ever pay it for denied system calls; and a
+  // typical program has very few of these.
+  Sandbox *sandbox = static_cast<Sandbox *>(aux);
+  if (BPF_CLASS(insn->code) == BPF_RET &&
+      (insn->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+    insn->k = sandbox->Trap(ReturnErrno,
+                   reinterpret_cast<void *>(insn->k & SECCOMP_RET_DATA)).err();
+  }
+}
+
+// Stackable wrapper around an Evaluators handler. Changes ErrorCodes
+// returned by a system call evaluator to match the changes made by
+// RedirectToUserspace(). "aux" should be pointer to wrapped system call
+// evaluator.
+ErrorCode RedirectToUserspaceEvalWrapper(Sandbox *sandbox, int sysnum,
+                                         void *aux) {
+  // We need to replicate the behavior of RedirectToUserspace(), so that our
+  // Verifier can still work correctly.
+  Sandbox::Evaluators *evaluators =
+    reinterpret_cast<Sandbox::Evaluators *>(aux);
+  const std::pair<Sandbox::EvaluateSyscall, void *>& evaluator =
+    *evaluators->begin();
+
+  ErrorCode err = evaluator.first(sandbox, sysnum, evaluator.second);
+  if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+    return sandbox->Trap(ReturnErrno,
+                       reinterpret_cast<void *>(err.err() & SECCOMP_RET_DATA));
+  }
+  return err;
+}
+
+intptr_t BpfFailure(const struct arch_seccomp_data&, void *aux) {
+  SANDBOX_DIE(static_cast<char *>(aux));
+}
+
+}  // namespace
+
+// The kernel gives us a sandbox, we turn it into a playground :-)
+// This is version 2 of the playground; version 1 was built on top of
+// pre-BPF seccomp mode.
+namespace playground2 {
+
+Sandbox::Sandbox()
+    : quiet_(false),
+      proc_fd_(-1),
+      evaluators_(new Evaluators),
+      conds_(new Conds) {
+}
+
+Sandbox::~Sandbox() {
+  // It is generally unsafe to call any memory allocator operations or to even
+  // call arbitrary destructors after having installed a new policy. We just
+  // have no way to tell whether this policy would allow the system calls that
+  // the constructors can trigger.
+  // So, we normally destroy all of our complex state prior to starting the
+  // sandbox. But this won't happen, if the Sandbox object was created and
+  // never actually used to set up a sandbox. So, just in case, we are
+  // destroying any remaining state.
+  // The "if ()" statements are technically superfluous. But let's be explicit
+  // that we really don't want to run any code, when we already destroyed
+  // objects before setting up the sandbox.
+  if (evaluators_) {
+    delete evaluators_;
+  }
+  if (conds_) {
+    delete conds_;
+  }
+}
+
+bool Sandbox::IsValidSyscallNumber(int sysnum) {
+  return SyscallIterator::IsValid(sysnum);
+}
+
+
+bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(),
+                                  Sandbox::EvaluateSyscall syscall_evaluator,
+                                  void *aux) {
   // Block all signals before forking a child process. This prevents an
   // attacker from manipulating our test by sending us an unexpected signal.
-  sigset_t oldMask, newMask;
-  if (sigfillset(&newMask) ||
-      sigprocmask(SIG_BLOCK, &newMask, &oldMask)) {
+  sigset_t old_mask, new_mask;
+  if (sigfillset(&new_mask) ||
+      sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
     SANDBOX_DIE("sigprocmask() failed");
   }
   int fds[2];
@@ -98,7 +270,7 @@
     // But what we don't want to do is return "false", as a crafty
     // attacker might cause fork() to fail at will and could trick us
     // into running without a sandbox.
-    sigprocmask(SIG_SETMASK, &oldMask, NULL);  // OK, if it fails
+    sigprocmask(SIG_SETMASK, &old_mask, NULL);  // OK, if it fails
     SANDBOX_DIE("fork() failed unexpectedly");
   }
 
@@ -108,9 +280,14 @@
     // successfully turn on sandboxing.
     Die::EnableSimpleExit();
 
+    errno = 0;
     if (HANDLE_EINTR(close(fds[0]))) {
+      // This call to close() has been failing in strange ways. See
+      // crbug.com/152530. So we only fail in debug mode now.
+#if !defined(NDEBUG)
       WriteFailedStderrSetupMessage(fds[1]);
       SANDBOX_DIE(NULL);
+#endif
     }
     if (HANDLE_EINTR(dup2(fds[1], 2)) != 2) {
       // Stderr could very well be a file descriptor to .xsession-errors, or
@@ -120,25 +297,28 @@
       // If dup2 fails here, we will continue normally, this means that our
       // parent won't cause a fatal failure if something writes to stderr in
       // this child.
-    }
-    if (HANDLE_EINTR(close(fds[1]))) {
+#if !defined(NDEBUG)
+      // In DEBUG builds, we still want to get a report.
       WriteFailedStderrSetupMessage(fds[1]);
       SANDBOX_DIE(NULL);
+#endif
+    }
+    if (HANDLE_EINTR(close(fds[1]))) {
+      // This call to close() has been failing in strange ways. See
+      // crbug.com/152530. So we only fail in debug mode now.
+#if !defined(NDEBUG)
+      WriteFailedStderrSetupMessage(fds[1]);
+      SANDBOX_DIE(NULL);
+#endif
     }
 
-    evaluators_.clear();
-    setSandboxPolicy(syscallEvaluator, aux);
-    setProcFd(proc_fd);
-
-    // By passing "quiet=true" to "startSandboxInternal()" we suppress
-    // messages for expected and benign failures (e.g. if the current
-    // kernel lacks support for BPF filters).
-    startSandboxInternal(true);
+    SetSandboxPolicy(syscall_evaluator, aux);
+    StartSandbox();
 
     // Run our code in the sandbox.
-    CodeInSandbox();
+    code_in_sandbox();
 
-    // CodeInSandbox() is not supposed to return here.
+    // code_in_sandbox() is not supposed to return here.
     SANDBOX_DIE(NULL);
   }
 
@@ -146,7 +326,7 @@
   if (HANDLE_EINTR(close(fds[1]))) {
     SANDBOX_DIE("close() failed");
   }
-  if (sigprocmask(SIG_SETMASK, &oldMask, NULL)) {
+  if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) {
     SANDBOX_DIE("sigprocmask() failed");
   }
   int status;
@@ -178,23 +358,13 @@
   return rc;
 }
 
-bool Sandbox::kernelSupportSeccompBPF(int proc_fd) {
-#if defined(SECCOMP_BPF_VALGRIND_HACKS)
-  if (RUNNING_ON_VALGRIND) {
-    // Valgrind doesn't like our run-time test. Disable testing and assume we
-    // always support sandboxing. This feature should only ever be enabled when
-    // debugging.
-    return true;
-  }
-#endif
-
+bool Sandbox::KernelSupportSeccompBPF() {
   return
-    RunFunctionInPolicy(probeProcess, Sandbox::probeEvaluator, 0, proc_fd) &&
-    RunFunctionInPolicy(tryVsyscallProcess, Sandbox::allowAllEvaluator, 0,
-                        proc_fd);
+    RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) &&
+    RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0);
 }
 
-Sandbox::SandboxStatus Sandbox::supportsSeccompSandbox(int proc_fd) {
+Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) {
   // It the sandbox is currently active, we clearly must have support for
   // sandboxing.
   if (status_ == STATUS_ENABLED) {
@@ -204,13 +374,13 @@
   // Even if the sandbox was previously available, something might have
   // changed in our run-time environment. Check one more time.
   if (status_ == STATUS_AVAILABLE) {
-    if (!isSingleThreaded(proc_fd)) {
+    if (!IsSingleThreaded(proc_fd)) {
       status_ = STATUS_UNAVAILABLE;
     }
     return status_;
   }
 
-  if (status_ == STATUS_UNAVAILABLE && isSingleThreaded(proc_fd)) {
+  if (status_ == STATUS_UNAVAILABLE && IsSingleThreaded(proc_fd)) {
     // All state transitions resulting in STATUS_UNAVAILABLE are immediately
     // preceded by STATUS_AVAILABLE. Furthermore, these transitions all
     // happen, if and only if they are triggered by the process being multi-
@@ -226,31 +396,40 @@
   // we otherwise don't believe to have a good cached value, we have to
   // perform a thorough check now.
   if (status_ == STATUS_UNKNOWN) {
-    status_ = kernelSupportSeccompBPF(proc_fd)
+    // We create our own private copy of a "Sandbox" object. This ensures that
+    // the object does not have any policies configured, that might interfere
+    // with the tests done by "KernelSupportSeccompBPF()".
+    Sandbox sandbox;
+
+    // By setting "quiet_ = true" we suppress messages for expected and benign
+    // failures (e.g. if the current kernel lacks support for BPF filters).
+    sandbox.quiet_ = true;
+    sandbox.set_proc_fd(proc_fd);
+    status_ = sandbox.KernelSupportSeccompBPF()
       ? STATUS_AVAILABLE : STATUS_UNSUPPORTED;
 
     // As we are performing our tests from a child process, the run-time
     // environment that is visible to the sandbox is always guaranteed to be
     // single-threaded. Let's check here whether the caller is single-
     // threaded. Otherwise, we mark the sandbox as temporarily unavailable.
-    if (status_ == STATUS_AVAILABLE && !isSingleThreaded(proc_fd)) {
+    if (status_ == STATUS_AVAILABLE && !IsSingleThreaded(proc_fd)) {
       status_ = STATUS_UNAVAILABLE;
     }
   }
   return status_;
 }
 
-void Sandbox::setProcFd(int proc_fd) {
+void Sandbox::set_proc_fd(int proc_fd) {
   proc_fd_ = proc_fd;
 }
 
-void Sandbox::startSandboxInternal(bool quiet) {
+void Sandbox::StartSandbox() {
   if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
     SANDBOX_DIE("Trying to start sandbox, even though it is known to be "
                 "unavailable");
-  } else if (status_ == STATUS_ENABLED) {
-    SANDBOX_DIE("Cannot start sandbox recursively. Use multiple calls to "
-                "setSandboxPolicy() to stack policies instead");
+  } else if (!evaluators_ || !conds_) {
+    SANDBOX_DIE("Cannot repeatedly start sandbox. Create a separate Sandbox "
+                "object instead.");
   }
   if (proc_fd_ < 0) {
     proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY);
@@ -259,7 +438,7 @@
     // For now, continue in degraded mode, if we can't access /proc.
     // In the future, we might want to tighten this requirement.
   }
-  if (!isSingleThreaded(proc_fd_)) {
+  if (!IsSingleThreaded(proc_fd_)) {
     SANDBOX_DIE("Cannot start sandbox, if process is already multi-threaded");
   }
 
@@ -274,44 +453,17 @@
   }
 
   // Install the filters.
-  installFilter(quiet);
+  InstallFilter();
 
   // We are now inside the sandbox.
   status_ = STATUS_ENABLED;
 }
 
-bool Sandbox::isSingleThreaded(int proc_fd) {
-  if (proc_fd < 0) {
-    // Cannot determine whether program is single-threaded. Hope for
-    // the best...
-    return true;
-  }
-
-  struct stat sb;
-  int task = -1;
-  if ((task = openat(proc_fd, "self/task", O_RDONLY|O_DIRECTORY)) < 0 ||
-      fstat(task, &sb) != 0 ||
-      sb.st_nlink != 3 ||
-      HANDLE_EINTR(close(task))) {
-    if (task >= 0) {
-      if (HANDLE_EINTR(close(task))) { }
-    }
-    return false;
-  }
-  return true;
-}
-
-bool Sandbox::isDenied(const ErrorCode& code) {
-  return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
-         (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
-          code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
-}
-
-void Sandbox::policySanityChecks(EvaluateSyscall syscallEvaluator,
+void Sandbox::PolicySanityChecks(EvaluateSyscall syscall_evaluator,
                                  void *aux) {
   for (SyscallIterator iter(true); !iter.Done(); ) {
     uint32_t sysnum = iter.Next();
-    if (!isDenied(syscallEvaluator(sysnum, aux))) {
+    if (!IsDenied(syscall_evaluator(this, sysnum, aux))) {
       SANDBOX_DIE("Policies should deny system calls that are outside the "
                   "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
     }
@@ -319,41 +471,65 @@
   return;
 }
 
-void Sandbox::setSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux) {
-  if (status_ == STATUS_ENABLED) {
+void Sandbox::SetSandboxPolicy(EvaluateSyscall syscall_evaluator, void *aux) {
+  if (!evaluators_ || !conds_) {
     SANDBOX_DIE("Cannot change policy after sandbox has started");
   }
-  policySanityChecks(syscallEvaluator, aux);
-  evaluators_.push_back(std::make_pair(syscallEvaluator, aux));
+  PolicySanityChecks(syscall_evaluator, aux);
+  evaluators_->push_back(std::make_pair(syscall_evaluator, aux));
 }
 
-void Sandbox::installFilter(bool quiet) {
+void Sandbox::InstallFilter() {
+  // We want to be very careful in not imposing any requirements on the
+  // policies that are set with SetSandboxPolicy(). This means, as soon as
+  // the sandbox is active, we shouldn't be relying on libraries that could
+  // be making system calls. This, for example, means we should avoid
+  // using the heap and we should avoid using STL functions.
+  // Temporarily copy the contents of the "program" vector into a
+  // stack-allocated array; and then explicitly destroy that object.
+  // This makes sure we don't ex- or implicitly call new/delete after we
+  // installed the BPF filter program in the kernel. Depending on the
+  // system memory allocator that is in effect, these operators can result
+  // in system calls to things like munmap() or brk().
+  Program *program = AssembleFilter(false /* force_verification */);
+
+  struct sock_filter bpf[program->size()];
+  const struct sock_fprog prog = {
+    static_cast<unsigned short>(program->size()), bpf };
+  memcpy(bpf, &(*program)[0], sizeof(bpf));
+  delete program;
+
+  // Release memory that is no longer needed
+  delete evaluators_;
+  delete conds_;
+  evaluators_ = NULL;
+  conds_      = NULL;
+
+  // Install BPF filter program
+  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+    SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs");
+  } else {
+    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+      SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters");
+    }
+  }
+
+  return;
+}
+
+Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) {
+#if !defined(NDEBUG)
+  force_verification = true;
+#endif
+
   // Verify that the user pushed a policy.
-  if (evaluators_.empty()) {
-  filter_failed:
+  if (evaluators_->empty()) {
     SANDBOX_DIE("Failed to configure system call filters");
   }
 
-  // Set new SIGSYS handler
-  struct sigaction sa;
-  memset(&sa, 0, sizeof(sa));
-  sa.sa_sigaction = &sigSys;
-  sa.sa_flags = SA_SIGINFO;
-  if (sigaction(SIGSYS, &sa, NULL) < 0) {
-    goto filter_failed;
-  }
-
-  // Unmask SIGSYS
-  sigset_t mask;
-  if (sigemptyset(&mask) ||
-      sigaddset(&mask, SIGSYS) ||
-      sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
-    goto filter_failed;
-  }
-
   // We can't handle stacked evaluators, yet. We'll get there eventually
   // though. Hang tight.
-  if (evaluators_.size() != 1) {
+  if (evaluators_->size() != 1) {
     SANDBOX_DIE("Not implemented");
   }
 
@@ -367,44 +543,124 @@
   // system call.
   Instruction *tail;
   Instruction *head =
-    gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
-                         offsetof(struct arch_seccomp_data, arch),
-    gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH,
+    gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_ARCH_IDX,
   tail =
-    // Grab the system call number, so that we can implement jump tables.
-    gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
-                         offsetof(struct arch_seccomp_data, nr)),
+    gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH,
+                         NULL,
     gen->MakeInstruction(BPF_RET+BPF_K,
-                         Kill(
-                           "Invalid audit architecture in BPF filter").err_)));
+                         Kill("Invalid audit architecture in BPF filter"))));
 
-  // On Intel architectures, verify that system call numbers are in the
-  // expected number range. The older i386 and x86-64 APIs clear bit 30
-  // on all system calls. The newer x32 API always sets bit 30.
-#if defined(__i386__) || defined(__x86_64__)
-  Instruction *invalidX32 =
-    gen->MakeInstruction(BPF_RET+BPF_K,
-                         Kill("Illegal mixing of system call ABIs").err_);
-  Instruction *checkX32 =
-#if defined(__x86_64__) && defined(__ILP32__)
-    gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 0, invalidX32);
-#else
-    gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, invalidX32, 0);
-#endif
-    gen->JoinInstructions(tail, checkX32);
-    tail = checkX32;
-#endif
-
-
+  bool has_unsafe_traps = false;
   {
     // Evaluate all possible system calls and group their ErrorCodes into
     // ranges of identical codes.
     Ranges ranges;
-    findRanges(&ranges);
+    FindRanges(&ranges);
 
     // Compile the system call ranges to an optimized BPF jumptable
     Instruction *jumptable =
-      assembleJumpTable(gen, ranges.begin(), ranges.end());
+      AssembleJumpTable(gen, ranges.begin(), ranges.end());
+
+    // If there is at least one UnsafeTrap() in our program, the entire sandbox
+    // is unsafe. We need to modify the program so that all non-
+    // SECCOMP_RET_ALLOW ErrorCodes are handled in user-space. This will then
+    // allow us to temporarily disable sandboxing rules inside of callbacks to
+    // UnsafeTrap().
+    gen->Traverse(jumptable, CheckForUnsafeErrorCodes, &has_unsafe_traps);
+
+    // Grab the system call number, so that we can implement jump tables.
+    Instruction *load_nr =
+      gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_NR_IDX);
+
+    // If our BPF program has unsafe jumps, enable support for them. This
+    // test happens very early in the BPF filter program. Even before we
+    // consider looking at system call numbers.
+    // As support for unsafe jumps essentially defeats all the security
+    // measures that the sandbox provides, we print a big warning message --
+    // and of course, we make sure to only ever enable this feature if it
+    // is actually requested by the sandbox policy.
+    if (has_unsafe_traps) {
+      if (SandboxSyscall(-1) == -1 && errno == ENOSYS) {
+        SANDBOX_DIE("Support for UnsafeTrap() has not yet been ported to this "
+                    "architecture");
+      }
+
+      EvaluateSyscall evaluateSyscall = evaluators_->begin()->first;
+      void *aux                       = evaluators_->begin()->second;
+      if (!evaluateSyscall(this, __NR_rt_sigprocmask, aux).
+            Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) ||
+          !evaluateSyscall(this, __NR_rt_sigreturn, aux).
+            Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+#if defined(__NR_sigprocmask)
+       || !evaluateSyscall(this, __NR_sigprocmask, aux).
+            Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+#endif
+#if defined(__NR_sigreturn)
+       || !evaluateSyscall(this, __NR_sigreturn, aux).
+            Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+#endif
+          ) {
+        SANDBOX_DIE("Invalid seccomp policy; if using UnsafeTrap(), you must "
+                    "unconditionally allow sigreturn() and sigprocmask()");
+      }
+
+      if (!Trap::EnableUnsafeTrapsInSigSysHandler()) {
+        // We should never be able to get here, as UnsafeTrap() should never
+        // actually return a valid ErrorCode object unless the user set the
+        // CHROME_SANDBOX_DEBUGGING environment variable; and therefore,
+        // "has_unsafe_traps" would always be false. But better double-check
+        // than enabling dangerous code.
+        SANDBOX_DIE("We'd rather die than enable unsafe traps");
+      }
+      gen->Traverse(jumptable, RedirectToUserspace, this);
+
+      // Allow system calls, if they originate from our magic return address
+      // (which we can query by calling SandboxSyscall(-1)).
+      uintptr_t syscall_entry_point =
+        static_cast<uintptr_t>(SandboxSyscall(-1));
+      uint32_t low = static_cast<uint32_t>(syscall_entry_point);
+#if __SIZEOF_POINTER__ > 4
+      uint32_t hi  = static_cast<uint32_t>(syscall_entry_point >> 32);
+#endif
+
+      // BPF cannot do native 64bit comparisons. On 64bit architectures, we
+      // have to compare both 32bit halves of the instruction pointer. If they
+      // match what we expect, we return ERR_ALLOWED. If either or both don't
+      // match, we continue evalutating the rest of the sandbox policy.
+      Instruction *escape_hatch =
+        gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_LSB_IDX,
+        gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, low,
+#if __SIZEOF_POINTER__ > 4
+        gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_MSB_IDX,
+        gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, hi,
+#endif
+        gen->MakeInstruction(BPF_RET+BPF_K, ErrorCode(ErrorCode::ERR_ALLOWED)),
+#if __SIZEOF_POINTER__ > 4
+                             load_nr)),
+#endif
+                             load_nr));
+      gen->JoinInstructions(tail, escape_hatch);
+    } else {
+      gen->JoinInstructions(tail, load_nr);
+    }
+    tail = load_nr;
+
+    // On Intel architectures, verify that system call numbers are in the
+    // expected number range. The older i386 and x86-64 APIs clear bit 30
+    // on all system calls. The newer x32 API always sets bit 30.
+#if defined(__i386__) || defined(__x86_64__)
+    Instruction *invalidX32 =
+      gen->MakeInstruction(BPF_RET+BPF_K,
+                           Kill("Illegal mixing of system call ABIs").err_);
+    Instruction *checkX32 =
+#if defined(__x86_64__) && defined(__ILP32__)
+      gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 0, invalidX32);
+#else
+      gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, invalidX32, 0);
+#endif
+      gen->JoinInstructions(tail, checkX32);
+      tail = checkX32;
+#endif
 
     // Append jump table to our pre-amble
     gen->JoinInstructions(tail, jumptable);
@@ -418,84 +674,67 @@
   // Make sure compilation resulted in BPF program that executes
   // correctly. Otherwise, there is an internal error in our BPF compiler.
   // There is really nothing the caller can do until the bug is fixed.
-#ifndef NDEBUG
-  const char *err = NULL;
-  if (!Verifier::VerifyBPF(*program, evaluators_, &err)) {
-    SANDBOX_DIE(err);
-  }
-#endif
-
-  // We want to be very careful in not imposing any requirements on the
-  // policies that are set with setSandboxPolicy(). This means, as soon as
-  // the sandbox is active, we shouldn't be relying on libraries that could
-  // be making system calls. This, for example, means we should avoid
-  // using the heap and we should avoid using STL functions.
-  // Temporarily copy the contents of the "program" vector into a
-  // stack-allocated array; and then explicitly destroy that object.
-  // This makes sure we don't ex- or implicitly call new/delete after we
-  // installed the BPF filter program in the kernel. Depending on the
-  // system memory allocator that is in effect, these operators can result
-  // in system calls to things like munmap() or brk().
-  struct sock_filter bpf[program->size()];
-  const struct sock_fprog prog = {
-    static_cast<unsigned short>(program->size()), bpf };
-  memcpy(bpf, &(*program)[0], sizeof(bpf));
-  delete program;
-
-  // Release memory that is no longer needed
-  evaluators_.clear();
-  errMap_.clear();
-
-#if defined(SECCOMP_BPF_VALGRIND_HACKS)
-  // Valgrind is really not happy about our sandbox. Disable it when running
-  // in Valgrind. This feature is dangerous and should never be enabled by
-  // default. We protect it behind a pre-processor option.
-  if (!RUNNING_ON_VALGRIND)
-#endif
-  {
-    // Install BPF filter program
-    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
-      SANDBOX_DIE(quiet ? NULL : "Kernel refuses to enable no-new-privs");
-    } else {
-      if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
-        SANDBOX_DIE(quiet ? NULL : "Kernel refuses to turn on BPF filters");
-      }
-    }
+  if (force_verification) {
+    // Verification is expensive. We only perform this step, if we are
+    // compiled in debug mode, or if the caller explicitly requested
+    // verification.
+    VerifyProgram(*program, has_unsafe_traps);
   }
 
-  return;
+  return program;
 }
 
-void Sandbox::findRanges(Ranges *ranges) {
+void Sandbox::VerifyProgram(const Program& program, bool has_unsafe_traps) {
+  // If we previously rewrote the BPF program so that it calls user-space
+  // whenever we return an "errno" value from the filter, then we have to
+  // wrap our system call evaluator to perform the same operation. Otherwise,
+  // the verifier would also report a mismatch in return codes.
+  Evaluators redirected_evaluators;
+  redirected_evaluators.push_back(
+      std::make_pair(RedirectToUserspaceEvalWrapper, evaluators_));
+
+  const char *err = NULL;
+  if (!Verifier::VerifyBPF(
+                       this,
+                       program,
+                       has_unsafe_traps ? redirected_evaluators : *evaluators_,
+                       &err)) {
+    CodeGen::PrintProgram(program);
+    SANDBOX_DIE(err);
+  }
+}
+
+void Sandbox::FindRanges(Ranges *ranges) {
   // Please note that "struct seccomp_data" defines system calls as a signed
   // int32_t, but BPF instructions always operate on unsigned quantities. We
   // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
   // and then verifying that the rest of the number range (both positive and
   // negative) all return the same ErrorCode.
-  EvaluateSyscall evaluateSyscall = evaluators_.begin()->first;
-  void *aux                       = evaluators_.begin()->second;
-  uint32_t oldSysnum              = 0;
-  ErrorCode oldErr                = evaluateSyscall(oldSysnum, aux);
-  ErrorCode invalidErr            = evaluateSyscall(MIN_SYSCALL - 1, aux);
+  EvaluateSyscall evaluate_syscall = evaluators_->begin()->first;
+  void *aux                        = evaluators_->begin()->second;
+  uint32_t old_sysnum              = 0;
+  ErrorCode old_err                = evaluate_syscall(this, old_sysnum, aux);
+  ErrorCode invalid_err            = evaluate_syscall(this, MIN_SYSCALL - 1,
+                                                      aux);
   for (SyscallIterator iter(false); !iter.Done(); ) {
     uint32_t sysnum = iter.Next();
-    ErrorCode err = evaluateSyscall(static_cast<int>(sysnum), aux);
-    if (!iter.IsValid(sysnum) && !invalidErr.Equals(err)) {
+    ErrorCode err = evaluate_syscall(this, static_cast<int>(sysnum), aux);
+    if (!iter.IsValid(sysnum) && !invalid_err.Equals(err)) {
       // A proper sandbox policy should always treat system calls outside of
       // the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns
       // "false" for SyscallIterator::IsValid()) identically. Typically, all
       // of these system calls would be denied with the same ErrorCode.
       SANDBOX_DIE("Invalid seccomp policy");
     }
-    if (!err.Equals(oldErr) || iter.Done()) {
-      ranges->push_back(Range(oldSysnum, sysnum - 1, oldErr));
-      oldSysnum = sysnum;
-      oldErr    = err;
+    if (!err.Equals(old_err) || iter.Done()) {
+      ranges->push_back(Range(old_sysnum, sysnum - 1, old_err));
+      old_sysnum = sysnum;
+      old_err    = err;
     }
   }
 }
 
-Instruction *Sandbox::assembleJumpTable(CodeGen *gen,
+Instruction *Sandbox::AssembleJumpTable(CodeGen *gen,
                                         Ranges::const_iterator start,
                                         Ranges::const_iterator stop) {
   // We convert the list of system call ranges into jump table that performs
@@ -507,7 +746,7 @@
   } else if (stop - start == 1) {
     // If we have narrowed things down to a single range object, we can
     // return from the BPF filter program.
-    return gen->MakeInstruction(BPF_RET+BPF_K, start->err);
+    return RetExpression(gen, start->err);
   }
 
   // Pick the range object that is located at the mid point of our list.
@@ -517,139 +756,230 @@
   Ranges::const_iterator mid = start + (stop - start)/2;
 
   // Sub-divide the list of ranges and continue recursively.
-  Instruction *jf = assembleJumpTable(gen, start, mid);
-  Instruction *jt = assembleJumpTable(gen, mid, stop);
+  Instruction *jf = AssembleJumpTable(gen, start, mid);
+  Instruction *jt = AssembleJumpTable(gen, mid, stop);
   return gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, mid->from, jt, jf);
 }
 
-void Sandbox::sigSys(int nr, siginfo_t *info, void *void_context) {
-  // Various sanity checks to make sure we actually received a signal
-  // triggered by a BPF filter. If something else triggered SIGSYS
-  // (e.g. kill()), there is really nothing we can do with this signal.
-  if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context ||
-      info->si_errno <= 0 ||
-      static_cast<size_t>(info->si_errno) > trapArraySize_) {
-    // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
-    // safe and can lead to bugs. We should eventually implement a different
-    // logging and reporting mechanism that is safe to be called from
-    // the sigSys() handler.
-    // TODO: If we feel confident that our code otherwise works correctly, we
-    //       could actually make an argument that spurious SIGSYS should
-    //       just get silently ignored. TBD
-  sigsys_err:
-    SANDBOX_DIE("Unexpected SIGSYS received");
-  }
-
-  // Signal handlers should always preserve "errno". Otherwise, we could
-  // trigger really subtle bugs.
-  int old_errno   = errno;
-
-  // Obtain the signal context. This, most notably, gives us access to
-  // all CPU registers at the time of the signal.
-  ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context);
-
-  // Obtain the siginfo information that is specific to SIGSYS. Unfortunately,
-  // most versions of glibc don't include this information in siginfo_t. So,
-  // we need to explicitly copy it into a arch_sigsys structure.
-  struct arch_sigsys sigsys;
-  memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
-
-  // Some more sanity checks.
-  if (sigsys.ip != reinterpret_cast<void *>(SECCOMP_IP(ctx)) ||
-      sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) ||
-      sigsys.arch != SECCOMP_ARCH) {
-    goto sigsys_err;
-  }
-
-  // Copy the seccomp-specific data into a arch_seccomp_data structure. This
-  // is what we are showing to TrapFnc callbacks that the system call evaluator
-  // registered with the sandbox.
-  struct arch_seccomp_data data = {
-    sigsys.nr,
-    SECCOMP_ARCH,
-    reinterpret_cast<uint64_t>(sigsys.ip),
-    {
-      static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
-      static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
-      static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
-      static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
-      static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
-      static_cast<uint64_t>(SECCOMP_PARM6(ctx))
-    }
-  };
-
-  // Now call the TrapFnc callback associated with this particular instance
-  // of SECCOMP_RET_TRAP.
-  const ErrorCode& err = trapArray_[info->si_errno - 1];
-  intptr_t rc          = err.fnc_(data, err.aux_);
-
-  // Update the CPU register that stores the return code of the system call
-  // that we just handled, and restore "errno" to the value that it had
-  // before entering the signal handler.
-  SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc);
-  errno               = old_errno;
-
-  return;
-}
-
-ErrorCode Sandbox::Trap(ErrorCode::TrapFnc fnc, const void *aux) {
-  // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
-  // of a SECCOMP_RET_TRAP.
-  std::pair<ErrorCode::TrapFnc, const void *> key(fnc, aux);
-  TrapIds::const_iterator iter = trapIds_.find(key);
-  uint16_t id;
-  if (iter != trapIds_.end()) {
-    // We have seen this pair before. Return the same id that we assigned
-    // earlier.
-    id = iter->second;
+Instruction *Sandbox::RetExpression(CodeGen *gen, const ErrorCode& err) {
+  if (err.error_type_ == ErrorCode::ET_COND) {
+    return CondExpression(gen, err);
   } else {
-    // This is a new pair. Remember it and assign a new id.
-    // Please note that we have to store traps in memory that doesn't get
-    // deallocated when the program is shutting down. A memory leak is
-    // intentional, because we might otherwise not be able to execute
-    // system calls part way through the program shutting down
-    if (!traps_) {
-      traps_ = new Traps();
-    }
-    if (traps_->size() >= SECCOMP_RET_DATA) {
-      // In practice, this is pretty much impossible to trigger, as there
-      // are other kernel limitations that restrict overall BPF program sizes.
-      SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
-    }
-    id = traps_->size() + 1;
-
-    traps_->push_back(ErrorCode(fnc, aux, id));
-    trapIds_[key] = id;
-
-    // We want to access the traps_ vector from our signal handler. But
-    // we are not assured that doing so is async-signal safe. On the other
-    // hand, C++ guarantees that the contents of a vector is stored in a
-    // contiguous C-style array.
-    // So, we look up the address and size of this array outside of the
-    // signal handler, where we can safely do so.
-    trapArray_     = &(*traps_)[0];
-    trapArraySize_ = id;
+    return gen->MakeInstruction(BPF_RET+BPF_K, err);
   }
-
-  ErrorCode err = ErrorCode(fnc, aux, id);
-  return errMap_[err.err()] = err;
 }
 
-intptr_t Sandbox::bpfFailure(const struct arch_seccomp_data&, void *aux) {
-  SANDBOX_DIE(static_cast<char *>(aux));
+Instruction *Sandbox::CondExpression(CodeGen *gen, const ErrorCode& cond) {
+  // We can only inspect the six system call arguments that are passed in
+  // CPU registers.
+  if (cond.argno_ < 0 || cond.argno_ >= 6) {
+    SANDBOX_DIE("Internal compiler error; invalid argument number "
+                "encountered");
+  }
+
+  // BPF programs operate on 32bit entities. Load both halfs of the 64bit
+  // system call argument and then generate suitable conditional statements.
+  Instruction *msb_head =
+    gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+                         SECCOMP_ARG_MSB_IDX(cond.argno_));
+  Instruction *msb_tail = msb_head;
+  Instruction *lsb_head =
+    gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+                         SECCOMP_ARG_LSB_IDX(cond.argno_));
+  Instruction *lsb_tail = lsb_head;
+
+  // Emit a suitable comparison statement.
+  switch (cond.op_) {
+  case ErrorCode::OP_EQUAL:
+    // Compare the least significant bits for equality
+    lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+                                    static_cast<uint32_t>(cond.value_),
+                                    RetExpression(gen, *cond.passed_),
+                                    RetExpression(gen, *cond.failed_));
+    gen->JoinInstructions(lsb_head, lsb_tail);
+
+    // If we are looking at a 64bit argument, we need to also compare the
+    // most significant bits.
+    if (cond.width_ == ErrorCode::TP_64BIT) {
+      msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+                                      static_cast<uint32_t>(cond.value_ >> 32),
+                                      lsb_head,
+                                      RetExpression(gen, *cond.failed_));
+      gen->JoinInstructions(msb_head, msb_tail);
+    }
+    break;
+  case ErrorCode::OP_HAS_ALL_BITS:
+    // Check the bits in the LSB half of the system call argument. Our
+    // OP_HAS_ALL_BITS operator passes, iff all of the bits are set. This is
+    // different from the kernel's BPF_JSET operation which passes, if any of
+    // the bits are set.
+    // Of course, if there is only a single set bit (or none at all), then
+    // things get easier.
+    {
+      uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+      int lsb_bit_count = popcount(lsb_bits);
+      if (lsb_bit_count == 0) {
+        // No bits are set in the LSB half. The test will always pass.
+        lsb_head = RetExpression(gen, *cond.passed_);
+        lsb_tail = NULL;
+      } else if (lsb_bit_count == 1) {
+        // Exactly one bit is set in the LSB half. We can use the BPF_JSET
+        // operator.
+        lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+                                        lsb_bits,
+                                        RetExpression(gen, *cond.passed_),
+                                        RetExpression(gen, *cond.failed_));
+        gen->JoinInstructions(lsb_head, lsb_tail);
+      } else {
+        // More than one bit is set in the LSB half. We need to combine
+        // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+        // set in the system call argument.
+        gen->JoinInstructions(lsb_head,
+                     gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
+                                          lsb_bits,
+          lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+                                          lsb_bits,
+                                          RetExpression(gen, *cond.passed_),
+                                          RetExpression(gen, *cond.failed_))));
+      }
+    }
+
+    // If we are looking at a 64bit argument, we need to also check the bits
+    // in the MSB half of the system call argument.
+    if (cond.width_ == ErrorCode::TP_64BIT) {
+      uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+      int msb_bit_count = popcount(msb_bits);
+      if (msb_bit_count == 0) {
+        // No bits are set in the MSB half. The test will always pass.
+        msb_head = lsb_head;
+      } else if (msb_bit_count == 1) {
+        // Exactly one bit is set in the MSB half. We can use the BPF_JSET
+        // operator.
+        msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+                                        msb_bits,
+                                        lsb_head,
+                                        RetExpression(gen, *cond.failed_));
+        gen->JoinInstructions(msb_head, msb_tail);
+      } else {
+        // More than one bit is set in the MSB half. We need to combine
+        // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+        // set in the system call argument.
+        gen->JoinInstructions(msb_head,
+          gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
+                               msb_bits,
+          gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+                               msb_bits,
+                               lsb_head,
+                               RetExpression(gen, *cond.failed_))));
+      }
+    }
+    break;
+  case ErrorCode::OP_HAS_ANY_BITS:
+    // Check the bits in the LSB half of the system call argument. Our
+    // OP_HAS_ANY_BITS operator passes, iff any of the bits are set. This maps
+    // nicely to the kernel's BPF_JSET operation.
+    {
+      uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+      if (!lsb_bits) {
+        // No bits are set in the LSB half. The test will always fail.
+        lsb_head = RetExpression(gen, *cond.failed_);
+        lsb_tail = NULL;
+      } else {
+        lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+                                        lsb_bits,
+                                        RetExpression(gen, *cond.passed_),
+                                        RetExpression(gen, *cond.failed_));
+        gen->JoinInstructions(lsb_head, lsb_tail);
+      }
+    }
+
+    // If we are looking at a 64bit argument, we need to also check the bits
+    // in the MSB half of the system call argument.
+    if (cond.width_ == ErrorCode::TP_64BIT) {
+      uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+      if (!msb_bits) {
+        // No bits are set in the MSB half. The test will always fail.
+        msb_head = lsb_head;
+      } else {
+        msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+                                        msb_bits,
+                                        RetExpression(gen, *cond.passed_),
+                                        lsb_head);
+        gen->JoinInstructions(msb_head, msb_tail);
+      }
+    }
+    break;
+  default:
+    // TODO(markus): Need to add support for OP_GREATER
+    SANDBOX_DIE("Not implemented");
+    break;
+  }
+
+  // Ensure that we never pass a 64bit value, when we only expect a 32bit
+  // value. This is somewhat complicated by the fact that on 64bit systems,
+  // callers could legitimately pass in a non-zero value in the MSB, iff the
+  // LSB has been sign-extended into the MSB.
+  if (cond.width_ == ErrorCode::TP_32BIT) {
+    if (cond.value_ >> 32) {
+      SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
+                  "against a 64bit constant; this test is always false.");
+    }
+
+    Instruction *invalid_64bit = RetExpression(gen, Unexpected64bitArgument());
+    #if __SIZEOF_POINTER__ > 4
+    invalid_64bit =
+      gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0xFFFFFFFF,
+      gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+                           SECCOMP_ARG_LSB_IDX(cond.argno_),
+      gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, 0x80000000,
+                           lsb_head,
+                           invalid_64bit)),
+                           invalid_64bit);
+    #endif
+    gen->JoinInstructions(
+      msb_tail,
+      gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0,
+                           lsb_head,
+                           invalid_64bit));
+  }
+
+  return msb_head;
+}
+
+ErrorCode Sandbox::Unexpected64bitArgument() {
+  return Kill("Unexpected 64bit argument detected");
+}
+
+ErrorCode Sandbox::Trap(Trap::TrapFnc fnc, const void *aux) {
+  return Trap::MakeTrap(fnc, aux, true /* Safe Trap */);
+}
+
+ErrorCode Sandbox::UnsafeTrap(Trap::TrapFnc fnc, const void *aux) {
+  return Trap::MakeTrap(fnc, aux, false /* Unsafe Trap */);
+}
+
+intptr_t Sandbox::ForwardSyscall(const struct arch_seccomp_data& args) {
+  return SandboxSyscall(args.nr,
+                        static_cast<intptr_t>(args.args[0]),
+                        static_cast<intptr_t>(args.args[1]),
+                        static_cast<intptr_t>(args.args[2]),
+                        static_cast<intptr_t>(args.args[3]),
+                        static_cast<intptr_t>(args.args[4]),
+                        static_cast<intptr_t>(args.args[5]));
+}
+
+ErrorCode Sandbox::Cond(int argno, ErrorCode::ArgType width,
+                        ErrorCode::Operation op, uint64_t value,
+                        const ErrorCode& passed, const ErrorCode& failed) {
+  return ErrorCode(argno, width, op, value,
+                   &*conds_->insert(passed).first,
+                   &*conds_->insert(failed).first);
 }
 
 ErrorCode Sandbox::Kill(const char *msg) {
-  return Trap(bpfFailure, const_cast<char *>(msg));
+  return Trap(BpfFailure, const_cast<char *>(msg));
 }
 
 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
-int    Sandbox::proc_fd_                = -1;
-Sandbox::Evaluators Sandbox::evaluators_;
-Sandbox::ErrMap Sandbox::errMap_;
-Sandbox::Traps *Sandbox::traps_         = NULL;
-Sandbox::TrapIds Sandbox::trapIds_;
-ErrorCode *Sandbox::trapArray_          = NULL;
-size_t Sandbox::trapArraySize_          = 0;
 
 }  // namespace
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index a50ddb3..3d26991 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -5,170 +5,22 @@
 #ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
 #define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
 
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/audit.h>
-#include <linux/filter.h>
-// #include <linux/seccomp.h>
-#include <linux/unistd.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
-#include <sched.h>
-#include <signal.h>
 #include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/ipc.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/shm.h>
-#include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/uio.h>
 #include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
 
 #include <algorithm>
 #include <limits>
 #include <map>
+#include <set>
 #include <utility>
 #include <vector>
 
-#ifndef SECCOMP_BPF_STANDALONE
-#include "base/basictypes.h"
-#include "base/eintr_wrapper.h"
-#include "base/logging.h"
-#endif
-
-#if defined(SECCOMP_BPF_VALGRIND_HACKS)
-#ifndef SECCOMP_BPF_STANDALONE
-#include "base/third_party/valgrind/valgrind.h"
-#endif
-#endif
-
-
-// The Seccomp2 kernel ABI is not part of older versions of glibc.
-// As we can't break compilation with these versions of the library,
-// we explicitly define all missing symbols.
-
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS          38
-#define PR_GET_NO_NEW_PRIVS          39
-#endif
-#ifndef IPC_64
-#define IPC_64                   0x0100
-#endif
-#ifndef SECCOMP_MODE_FILTER
-#define SECCOMP_MODE_DISABLED         0
-#define SECCOMP_MODE_STRICT           1
-#define SECCOMP_MODE_FILTER           2  // User user-supplied filter
-#define SECCOMP_RET_KILL    0x00000000U  // Kill the task immediately
-#define SECCOMP_RET_TRAP    0x00030000U  // Disallow and force a SIGSYS
-#define SECCOMP_RET_ERRNO   0x00050000U  // Returns an errno
-#define SECCOMP_RET_TRACE   0x7ff00000U  // Pass to a tracer or disallow
-#define SECCOMP_RET_ALLOW   0x7fff0000U  // Allow
-#define SECCOMP_RET_INVALID 0x8f8f8f8fU  // Illegal return value
-#define SECCOMP_RET_ACTION  0xffff0000U  // Masks for the return value
-#define SECCOMP_RET_DATA    0x0000ffffU  //   sections
-#endif
-#define SECCOMP_DENY_ERRNO  EPERM
-#ifndef SYS_SECCOMP
-#define SYS_SECCOMP                   1
-#endif
-
-// Impose some reasonable maximum BPF program size. Realistically, the
-// kernel probably has much lower limits. But by limiting to less than
-// 30 bits, we can ease requirements on some of our data types.
-#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
-
-#if defined(__i386__)
-#define MIN_SYSCALL         0u
-#define MAX_PUBLIC_SYSCALL  1024u
-#define MAX_SYSCALL         MAX_PUBLIC_SYSCALL
-#define SECCOMP_ARCH AUDIT_ARCH_I386
-
-#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
-#define SECCOMP_RESULT(_ctx)    SECCOMP_REG(_ctx, REG_EAX)
-#define SECCOMP_SYSCALL(_ctx)   SECCOMP_REG(_ctx, REG_EAX)
-#define SECCOMP_IP(_ctx)        SECCOMP_REG(_ctx, REG_EIP)
-#define SECCOMP_PARM1(_ctx)     SECCOMP_REG(_ctx, REG_EBX)
-#define SECCOMP_PARM2(_ctx)     SECCOMP_REG(_ctx, REG_ECX)
-#define SECCOMP_PARM3(_ctx)     SECCOMP_REG(_ctx, REG_EDX)
-#define SECCOMP_PARM4(_ctx)     SECCOMP_REG(_ctx, REG_ESI)
-#define SECCOMP_PARM5(_ctx)     SECCOMP_REG(_ctx, REG_EDI)
-#define SECCOMP_PARM6(_ctx)     SECCOMP_REG(_ctx, REG_EBP)
-
-#elif defined(__x86_64__)
-#define MIN_SYSCALL         0u
-#define MAX_PUBLIC_SYSCALL  1024u
-#define MAX_SYSCALL         MAX_PUBLIC_SYSCALL
-#define SECCOMP_ARCH AUDIT_ARCH_X86_64
-
-#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
-#define SECCOMP_RESULT(_ctx)    SECCOMP_REG(_ctx, REG_RAX)
-#define SECCOMP_SYSCALL(_ctx)   SECCOMP_REG(_ctx, REG_RAX)
-#define SECCOMP_IP(_ctx)        SECCOMP_REG(_ctx, REG_RIP)
-#define SECCOMP_PARM1(_ctx)     SECCOMP_REG(_ctx, REG_RDI)
-#define SECCOMP_PARM2(_ctx)     SECCOMP_REG(_ctx, REG_RSI)
-#define SECCOMP_PARM3(_ctx)     SECCOMP_REG(_ctx, REG_RDX)
-#define SECCOMP_PARM4(_ctx)     SECCOMP_REG(_ctx, REG_R10)
-#define SECCOMP_PARM5(_ctx)     SECCOMP_REG(_ctx, REG_R8)
-#define SECCOMP_PARM6(_ctx)     SECCOMP_REG(_ctx, REG_R9)
-
-#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
-// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
-// and a "ghost syscall private to the kernel", cmpxchg,
-// at |__ARM_NR_BASE+0x00fff0|.
-// See </arch/arm/include/asm/unistd.h> in the Linux kernel.
-#define MIN_SYSCALL         ((unsigned int)__NR_SYSCALL_BASE)
-#define MAX_PUBLIC_SYSCALL  (MIN_SYSCALL + 1024u)
-#define MIN_PRIVATE_SYSCALL ((unsigned int)__ARM_NR_BASE)
-#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
-#define MIN_GHOST_SYSCALL   ((unsigned int)__ARM_NR_BASE + 0xfff0u)
-#define MAX_SYSCALL         (MIN_GHOST_SYSCALL + 4u)
-// <linux/audit.h> includes <linux/elf-em.h>, which does not define EM_ARM.
-// <linux/elf.h> only includes <asm/elf.h> if we're in the kernel.
-# if !defined(EM_ARM)
-# define EM_ARM 40
-# endif
-#define SECCOMP_ARCH AUDIT_ARCH_ARM
-
-// ARM sigcontext_t is different from i386/x86_64.
-// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
-#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
-// ARM EABI syscall convention.
-#define SECCOMP_RESULT(_ctx)     SECCOMP_REG(_ctx, r0)
-#define SECCOMP_SYSCALL(_ctx)    SECCOMP_REG(_ctx, r7)
-#define SECCOMP_IP(_ctx)         SECCOMP_REG(_ctx, pc)
-#define SECCOMP_PARM1(_ctx)      SECCOMP_REG(_ctx, r0)
-#define SECCOMP_PARM2(_ctx)      SECCOMP_REG(_ctx, r1)
-#define SECCOMP_PARM3(_ctx)      SECCOMP_REG(_ctx, r2)
-#define SECCOMP_PARM4(_ctx)      SECCOMP_REG(_ctx, r3)
-#define SECCOMP_PARM5(_ctx)      SECCOMP_REG(_ctx, r4)
-#define SECCOMP_PARM6(_ctx)      SECCOMP_REG(_ctx, r5)
-
-#else
-#error Unsupported target platform
-
-#endif
-
-#if defined(SECCOMP_BPF_STANDALONE)
-#define arraysize(x) (sizeof(x)/sizeof(*(x)))
-#define HANDLE_EINTR TEMP_FAILURE_RETRY
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
-  TypeName();                                    \
-  TypeName(const TypeName&);                     \
-  void operator=(const TypeName&)
-#endif
-
 #include "sandbox/linux/seccomp-bpf/die.h"
 #include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
+
 
 namespace playground2 {
 
@@ -199,56 +51,49 @@
     STATUS_ENABLED       // The sandbox is now active
   };
 
-  // TrapFnc is a pointer to a function that handles Seccomp traps in
-  // user-space. The seccomp policy can request that a trap handler gets
-  // installed; it does so by returning a suitable ErrorCode() from the
-  // syscallEvaluator. See the ErrorCode() constructor for how to pass in
-  // the function pointer.
-  // Please note that TrapFnc is executed from signal context and must be
-  // async-signal safe:
-  // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
-  typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
-
-  enum Operation {
-    OP_NOP, OP_EQUAL, OP_NOTEQUAL, OP_LESS,
-    OP_LESS_EQUAL, OP_GREATER, OP_GREATER_EQUAL,
-    OP_HAS_BITS, OP_DOES_NOT_HAVE_BITS
-  };
-
-  struct Constraint {
-    bool      is32bit;
-    Operation op;
-    uint32_t  value;
-    ErrorCode passed;
-    ErrorCode failed;
-  };
-
   // When calling setSandboxPolicy(), the caller can provide an arbitrary
   // pointer. This pointer will then be forwarded to the sandbox policy
   // each time a call is made through an EvaluateSyscall function pointer.
   // One common use case would be to pass the "aux" pointer as an argument
   // to Trap() functions.
-  typedef ErrorCode (*EvaluateSyscall)(int sysnum, void *aux);
+  typedef ErrorCode (*EvaluateSyscall)(Sandbox *sb, int sysnum, 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;
+
+  // Constructors and destructors.
+  // NOTE: Setting a policy and starting the sandbox is a one-way operation.
+  //       The kernel does not provide any option for unloading a loaded
+  //       sandbox. Strictly speaking, that means we should disallow calling
+  //       the destructor, if StartSandbox() has ever been called. In practice,
+  //       this makes it needlessly complicated to operate on "Sandbox"
+  //       objects. So, we instead opted to allow object destruction. But it
+  //       should be noted that during its lifetime, the object probably made
+  //       irreversible state changes to the runtime environment. These changes
+  //       stay in effect even after the destructor has been run.
+  Sandbox();
+  ~Sandbox();
+
   // Checks whether a particular system call number is valid on the current
   // architecture. E.g. on ARM there's a non-contiguous range of private
   // system calls.
-  static bool isValidSyscallNumber(int sysnum);
+  static bool IsValidSyscallNumber(int sysnum);
 
   // There are a lot of reasons why the Seccomp sandbox might not be available.
   // This could be because the kernel does not support Seccomp mode, or it
   // could be because another sandbox is already active.
   // "proc_fd" should be a file descriptor for "/proc", or -1 if not
   // provided by the caller.
-  static SandboxStatus supportsSeccompSandbox(int proc_fd);
+  static SandboxStatus SupportsSeccompSandbox(int proc_fd);
 
   // The sandbox needs to be able to access files in "/proc/self". If this
   // directory is not accessible when "startSandbox()" gets called, the caller
-  // can provide an already opened file descriptor by calling "setProcFd()".
+  // can provide an already opened file descriptor by calling "set_proc_fd()".
   // The sandbox becomes the new owner of this file descriptor and will
-  // eventually close it when "startSandbox()" executes.
-  static void setProcFd(int proc_fd);
+  // eventually close it when "StartSandbox()" executes.
+  void set_proc_fd(int proc_fd);
 
   // The system call evaluator function is called with the system
   // call number. It can decide to allow the system call unconditionally
@@ -261,83 +106,162 @@
   // handler. In this case, of course, the data that is pointed to must remain
   // valid for the entire time that Trap() handlers can be called; typically,
   // this would be the lifetime of the program.
-  static void setSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
+  void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
 
   // We can use ErrorCode to request calling of a trap handler. This method
   // performs the required wrapping of the callback function into an
   // ErrorCode object.
   // The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
-  // for a description of how to pass data from setSandboxPolicy() to a Trap()
+  // for a description of how to pass data from SetSandboxPolicy() to a Trap()
   // handler.
-  static ErrorCode Trap(ErrorCode::TrapFnc fnc, const void *aux);
+  ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
+
+  // Calls a user-space trap handler and disables all sandboxing for system
+  // calls made from this trap handler.
+  // This feature is available only if explicitly enabled by the user having
+  // set the CHROME_SANDBOX_DEBUGGING environment variable.
+  // Returns an ET_INVALID ErrorCode, if called when not enabled.
+  // NOTE: This feature, by definition, disables all security features of
+  //   the sandbox. It should never be used in production, but it can be
+  //   very useful to diagnose code that is incompatible with the sandbox.
+  //   If even a single system call returns "UnsafeTrap", the security of
+  //   entire sandbox should be considered compromised.
+  ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
+
+  // From within an UnsafeTrap() it is often useful to be able to execute
+  // the system call that triggered the trap. The ForwardSyscall() method
+  // makes this easy. It is more efficient than calling glibc's syscall()
+  // function, as it avoid the extra round-trip to the signal handler. And
+  // it automatically does the correct thing to report kernel-style error
+  // conditions, rather than setting errno. See the comments for TrapFnc for
+  // details. In other words, the return value from ForwardSyscall() is
+  // directly suitable as a return value for a trap handler.
+  static intptr_t ForwardSyscall(const struct arch_seccomp_data& args);
+
+  // We can also use ErrorCode to request evaluation of a conditional
+  // statement based on inspection of system call parameters.
+  // This method wrap an ErrorCode object around the conditional statement.
+  // Argument "argno" (1..6) will be compared to "value" using comparator
+  // "op". If the condition is true "passed" will be returned, otherwise
+  // "failed".
+  // If "is32bit" is set, the argument must in the range of 0x0..(1u << 32 - 1)
+  // If it is outside this range, the sandbox treats the system call just
+  // the same as any other ABI violation (i.e. it aborts with an error
+  // message).
+  ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
+                 ErrorCode::Operation op,
+                 uint64_t value, const ErrorCode& passed,
+                 const ErrorCode& failed);
 
   // Kill the program and print an error message.
-  static ErrorCode Kill(const char *msg);
+  ErrorCode Kill(const char *msg);
 
   // This is the main public entry point. It finds all system calls that
   // need rewriting, sets up the resources needed by the sandbox, and
   // enters Seccomp mode.
-  static void startSandbox() { startSandboxInternal(false); }
+  // It is possible to stack multiple sandboxes by creating separate "Sandbox"
+  // objects and calling "StartSandbox()" on each of them. Please note, that
+  // this requires special care, though, as newly stacked sandboxes can never
+  // relax restrictions imposed by earlier sandboxes. Furthermore, installing
+  // a new policy requires making system calls, that might already be
+  // disallowed.
+  // Finally, stacking does add more kernel overhead than having a single
+  // combined policy. So, it should only be used if there are no alternatives.
+  void StartSandbox();
+
+  // Assembles a BPF filter program from the current policy. After calling this
+  // function, you must not call any other sandboxing function.
+  // Typically, AssembleFilter() is only used by unit tests and by sandbox
+  // internals. It should not be used by production code.
+  // For performance reasons, we normally only run the assembled BPF program
+  // through the verifier, iff the program was built in debug mode.
+  // But by setting "force_verification", the caller can request that the
+  // verifier is run unconditionally. This is useful for unittests.
+  Program *AssembleFilter(bool force_verification);
+
+  // Returns the fatal ErrorCode that is used to indicate that somebody
+  // attempted to pass a 64bit value in a 32bit system call argument.
+  // This method is primarily needed for testing purposes.
+  ErrorCode Unexpected64bitArgument();
 
  private:
-  friend class ErrorCode;
   friend class CodeGen;
   friend class SandboxUnittestHelper;
-  friend class Util;
-  friend class Verifier;
-
-  typedef std::vector<struct sock_filter> Program;
+  friend class ErrorCode;
 
   struct Range {
-    Range(uint32_t f, uint32_t t, const ErrorCode& e) :
-      from(f),
-      to(t),
-      err(e) {
+    Range(uint32_t f, uint32_t t, const ErrorCode& e)
+        : from(f),
+          to(t),
+          err(e) {
     }
     uint32_t  from, to;
     ErrorCode err;
   };
   typedef std::vector<Range> Ranges;
   typedef std::map<uint32_t, ErrorCode> ErrMap;
-  typedef std::vector<ErrorCode> Traps;
-  typedef std::map<std::pair<TrapFnc, const void *>, int> TrapIds;
+  typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
 
   // Get a file descriptor pointing to "/proc", if currently available.
-  static int proc_fd() { return proc_fd_; }
+  int proc_fd() { return proc_fd_; }
 
-  static ErrorCode probeEvaluator(int sysnum, void *) __attribute__((const));
-  static void      probeProcess(void);
-  static ErrorCode allowAllEvaluator(int sysnum, void *aux);
-  static void      tryVsyscallProcess(void);
-  static bool      kernelSupportSeccompBPF(int proc_fd);
-  static bool      RunFunctionInPolicy(void (*function)(),
-                                       EvaluateSyscall syscallEvaluator,
-                                       void *aux,
-                                       int proc_fd);
-  static void      startSandboxInternal(bool quiet);
-  static bool      isSingleThreaded(int proc_fd);
-  static bool      isDenied(const ErrorCode& code);
-  static bool      disableFilesystem();
-  static void      policySanityChecks(EvaluateSyscall syscallEvaluator,
-                                      void *aux);
-  static void      installFilter(bool quiet);
-  static void      findRanges(Ranges *ranges);
-  static Instruction *assembleJumpTable(CodeGen *gen,
-                                        Ranges::const_iterator start,
-                                        Ranges::const_iterator stop);
-  static void      sigSys(int nr, siginfo_t *info, void *void_context);
-  static intptr_t  bpfFailure(const struct arch_seccomp_data& data, void *aux);
-  static int       getTrapId(TrapFnc fnc, const void *aux);
+  // Creates a subprocess and runs "code_in_sandbox" inside of the specified
+  // policy. The caller has to make sure that "this" has not yet been
+  // initialized with any other policies.
+  bool RunFunctionInPolicy(void (*code_in_sandbox)(),
+                           EvaluateSyscall syscall_evaluator, void *aux);
+
+  // Performs a couple of sanity checks to verify that the kernel supports the
+  // features that we need for successful sandboxing.
+  // The caller has to make sure that "this" has not yet been initialized with
+  // any other policies.
+  bool KernelSupportSeccompBPF();
+
+  // Verify that the current policy passes some basic sanity checks.
+  void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux);
+
+  // Assembles and installs a filter based on the policy that has previously
+  // been configured with SetSandboxPolicy().
+  void InstallFilter();
+
+  // Verify the correctness of a compiled program by comparing it against the
+  // current policy. This function should only ever be called by unit tests and
+  // by the sandbox internals. It should not be used by production code.
+  void VerifyProgram(const Program& program, bool has_unsafe_traps);
+
+  // Finds all the ranges of system calls that need to be handled. Ranges are
+  // sorted in ascending order of system call numbers. There are no gaps in the
+  // ranges. System calls with identical ErrorCodes are coalesced into a single
+  // range.
+  void FindRanges(Ranges *ranges);
+
+  // Returns a BPF program snippet that implements a jump table for the
+  // given range of system call numbers. This function runs recursively.
+  Instruction *AssembleJumpTable(CodeGen *gen,
+                                 Ranges::const_iterator start,
+                                 Ranges::const_iterator stop);
+
+  // Returns a BPF program snippet that makes the BPF filter program exit
+  // with the given ErrorCode "err". N.B. the ErrorCode may very well be a
+  // conditional expression; if so, this function will recursively call
+  // CondExpression() and possibly RetExpression() to build a complex set of
+  // instructions.
+  Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
+
+  // Returns a BPF program that evaluates the conditional expression in
+  // "cond" and returns the appropriate value from the BPF filter program.
+  // This function recursively calls RetExpression(); it should only ever be
+  // called from RetExpression().
+  Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
 
   static SandboxStatus status_;
-  static int           proc_fd_;
-  static Evaluators    evaluators_;
-  static ErrMap        errMap_;
-  static Traps         *traps_;
-  static TrapIds       trapIds_;
-  static ErrorCode     *trapArray_;
-  static size_t        trapArraySize_;
-  DISALLOW_IMPLICIT_CONSTRUCTORS(Sandbox);
+
+  bool       quiet_;
+  int        proc_fd_;
+  Evaluators *evaluators_;
+  Conds      *conds_;
+
+  DISALLOW_COPY_AND_ASSIGN(Sandbox);
 };
 
 }  // namespace
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 8ea23d9..2d775f4 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -2,17 +2,58 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#if defined(ANDROID)
+// Work-around for buggy headers in Android's NDK
+#define __user
+#endif
+#include <linux/futex.h>
+
 #include <ostream>
 
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
 #include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
 #include "sandbox/linux/seccomp-bpf/verifier.h"
+#include "sandbox/linux/services/broker_process.h"
+#include "sandbox/linux/services/linux_syscalls.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+// Workaround for Android's prctl.h file.
+#ifndef PR_GET_ENDIAN
+#define PR_GET_ENDIAN   19
+#endif
+#ifndef PR_CAPBSET_READ
+#define PR_CAPBSET_READ 23
+#define PR_CAPBSET_DROP 24
+#endif
+
 using namespace playground2;
+using sandbox::BrokerProcess;
 
 namespace {
 
-const int kExpectedReturnValue = 42;
+const int  kExpectedReturnValue   = 42;
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+inline bool IsAndroid() {
+#if defined(OS_ANDROID)
+  return true;
+#else
+  return false;
+#endif
+}
 
 // This test should execute no matter whether we have kernel support. So,
 // we make it a TEST() instead of a BPF_TEST().
@@ -20,7 +61,7 @@
   // We check that we don't crash, but it's ok if the kernel doesn't
   // support it.
   bool seccomp_bpf_supported =
-      Sandbox::supportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE;
+      Sandbox::SupportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE;
   // We want to log whether or not seccomp BPF is actually supported
   // since actual test coverage depends on it.
   RecordProperty("SeccompBPFSupported",
@@ -28,11 +69,13 @@
   std::cout << "Seccomp BPF supported: "
             << (seccomp_bpf_supported ? "true." : "false.")
             << "\n";
+  RecordProperty("PointerSize", sizeof(void*));
+  std::cout << "Pointer size: " << sizeof(void*) << "\n";
 }
 
 SANDBOX_TEST(SandboxBpf, CallSupportsTwice) {
-  Sandbox::supportsSeccompSandbox(-1);
-  Sandbox::supportsSeccompSandbox(-1);
+  Sandbox::SupportsSeccompSandbox(-1);
+  Sandbox::SupportsSeccompSandbox(-1);
 }
 
 // BPF_TEST does a lot of the boiler-plate code around setting up a
@@ -48,22 +91,23 @@
   return (*pid_ptr)++;
 }
 
-ErrorCode VerboseAPITestingPolicy(int sysno, void *aux) {
-  if (!Sandbox::isValidSyscallNumber(sysno)) {
+ErrorCode VerboseAPITestingPolicy(Sandbox *sandbox, int sysno, void *aux) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
     return ErrorCode(ENOSYS);
   } else if (sysno == __NR_getpid) {
-    return Sandbox::Trap(FakeGetPid, aux);
+    return sandbox->Trap(FakeGetPid, aux);
   } else {
     return ErrorCode(ErrorCode::ERR_ALLOWED);
   }
 }
 
 SANDBOX_TEST(SandboxBpf, VerboseAPITesting) {
-  if (Sandbox::supportsSeccompSandbox(-1) ==
+  if (Sandbox::SupportsSeccompSandbox(-1) ==
       playground2::Sandbox::STATUS_AVAILABLE) {
     pid_t test_var = 0;
-    playground2::Sandbox::setSandboxPolicy(VerboseAPITestingPolicy, &test_var);
-    playground2::Sandbox::startSandbox();
+    Sandbox sandbox;
+    sandbox.SetSandboxPolicy(VerboseAPITestingPolicy, &test_var);
+    sandbox.StartSandbox();
 
     BPF_ASSERT(test_var == 0);
     BPF_ASSERT(syscall(__NR_getpid) == 0);
@@ -79,8 +123,8 @@
 
 // A simple blacklist test
 
-ErrorCode BlacklistNanosleepPolicy(int sysno, void *) {
-  if (!Sandbox::isValidSyscallNumber(sysno)) {
+ErrorCode BlacklistNanosleepPolicy(Sandbox *, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
     // FIXME: we should really not have to do that in a trivial policy
     return ErrorCode(ENOSYS);
   }
@@ -103,7 +147,7 @@
 
 // Now do a simple whitelist test
 
-ErrorCode WhitelistGetpidPolicy(int sysno, void *) {
+ErrorCode WhitelistGetpidPolicy(Sandbox *, int sysno, void *) {
   switch (sysno) {
     case __NR_getpid:
     case __NR_exit_group:
@@ -133,15 +177,16 @@
   return -ENOMEM;
 }
 
-ErrorCode BlacklistNanosleepPolicySigsys(int sysno, void *aux) {
-  if (!Sandbox::isValidSyscallNumber(sysno)) {
+ErrorCode BlacklistNanosleepPolicySigsys(Sandbox *sandbox, int sysno,
+                                         void *aux) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
     // FIXME: we should really not have to do that in a trivial policy
     return ErrorCode(ENOSYS);
   }
 
   switch (sysno) {
     case __NR_nanosleep:
-      return Sandbox::Trap(EnomemHandler, aux);
+      return sandbox->Trap(EnomemHandler, aux);
     default:
       return ErrorCode(ErrorCode::ERR_ALLOWED);
   }
@@ -164,6 +209,132 @@
   BPF_ASSERT(BPF_AUX == kExpectedReturnValue);
 }
 
+// A simple test that verifies we can return arbitrary errno values.
+
+ErrorCode ErrnoTestPolicy(Sandbox *, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  }
+
+  switch (sysno) {
+  case __NR_dup2:
+    // Pretend that dup2() worked, but don't actually do anything.
+    return ErrorCode(0);
+  case __NR_setuid:
+#if defined(__NR_setuid32)
+  case __NR_setuid32:
+#endif
+    // Return errno = 1.
+    return ErrorCode(1);
+  case __NR_setgid:
+#if defined(__NR_setgid32)
+  case __NR_setgid32:
+#endif
+    // Return maximum errno value (typically 4095).
+    return ErrorCode(ErrorCode::ERR_MAX_ERRNO);
+  case __NR_uname:
+    // Return errno = 42;
+    return ErrorCode(42);
+  default:
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+BPF_TEST(SandboxBpf, ErrnoTest, ErrnoTestPolicy) {
+  // Verify that dup2() returns success, but doesn't actually run.
+  int fds[4];
+  BPF_ASSERT(pipe(fds) == 0);
+  BPF_ASSERT(pipe(fds+2) == 0);
+  BPF_ASSERT(dup2(fds[2], fds[0]) == 0);
+  char buf[1] = { };
+  BPF_ASSERT(write(fds[1], "\x55", 1) == 1);
+  BPF_ASSERT(write(fds[3], "\xAA", 1) == 1);
+  BPF_ASSERT(read(fds[0], buf, 1) == 1);
+
+  // If dup2() executed, we will read \xAA, but it dup2() has been turned
+  // into a no-op by our policy, then we will read \x55.
+  BPF_ASSERT(buf[0] == '\x55');
+
+  // Verify that we can return the minimum and maximum errno values.
+  errno = 0;
+  BPF_ASSERT(setuid(0) == -1);
+  BPF_ASSERT(errno == 1);
+
+  // On Android, errno is only supported up to 255, otherwise errno
+  // processing is skipped.
+  // We work around this (crbug.com/181647).
+  if (IsAndroid() && setgid(0) != -1) {
+    errno = 0;
+    BPF_ASSERT(setgid(0) == -ErrorCode::ERR_MAX_ERRNO);
+    BPF_ASSERT(errno == 0);
+  } else {
+    errno = 0;
+    BPF_ASSERT(setgid(0) == -1);
+    BPF_ASSERT(errno == ErrorCode::ERR_MAX_ERRNO);
+  }
+
+  // Finally, test an errno in between the minimum and maximum.
+  errno = 0;
+  struct utsname uts_buf;
+  BPF_ASSERT(uname(&uts_buf) == -1);
+  BPF_ASSERT(errno == 42);
+}
+
+// Testing the stacking of two sandboxes
+
+ErrorCode StackingPolicyPartOne(Sandbox *sandbox, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    return ErrorCode(ENOSYS);
+  }
+
+  switch (sysno) {
+    case __NR_getppid:
+      return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+                           ErrorCode(ErrorCode::ERR_ALLOWED),
+                           ErrorCode(EPERM));
+    default:
+      return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+ErrorCode StackingPolicyPartTwo(Sandbox *sandbox, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    return ErrorCode(ENOSYS);
+  }
+
+  switch (sysno) {
+    case __NR_getppid:
+      return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+                           ErrorCode(EINVAL),
+                           ErrorCode(ErrorCode::ERR_ALLOWED));
+    default:
+      return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+BPF_TEST(SandboxBpf, StackingPolicy, StackingPolicyPartOne) {
+  errno = 0;
+  BPF_ASSERT(syscall(__NR_getppid, 0) > 0);
+  BPF_ASSERT(errno == 0);
+
+  BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+  BPF_ASSERT(errno == EPERM);
+
+  // Stack a second sandbox with its own policy. Verify that we can further
+  // restrict filters, but we cannot relax existing filters.
+  Sandbox sandbox;
+  sandbox.SetSandboxPolicy(StackingPolicyPartTwo, NULL);
+  sandbox.StartSandbox();
+
+  errno = 0;
+  BPF_ASSERT(syscall(__NR_getppid, 0) == -1);
+  BPF_ASSERT(errno == EINVAL);
+
+  BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+  BPF_ASSERT(errno == EPERM);
+}
+
 // A more complex, but synthetic policy. This tests the correctness of the BPF
 // program by iterating through all syscalls and checking for an errno that
 // depends on the syscall number. Unlike the Verifier, this exercises the BPF
@@ -179,8 +350,8 @@
   return ((sysno & ~3) >> 2) % 29 + 1;
 }
 
-ErrorCode SyntheticPolicy(int sysno, void *) {
-  if (!Sandbox::isValidSyscallNumber(sysno)) {
+ErrorCode SyntheticPolicy(Sandbox *, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
     // FIXME: we should really not have to do that in a trivial policy
     return ErrorCode(ENOSYS);
   }
@@ -237,8 +408,8 @@
   }
 }
 
-ErrorCode ArmPrivatePolicy(int sysno, void *) {
-  if (!Sandbox::isValidSyscallNumber(sysno)) {
+ErrorCode ArmPrivatePolicy(Sandbox *, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
     // FIXME: we should really not have to do that in a trivial policy.
     return ErrorCode(ENOSYS);
   }
@@ -264,4 +435,1288 @@
 }
 #endif  // defined(__arm__)
 
+intptr_t CountSyscalls(const struct arch_seccomp_data& args, void *aux) {
+  // Count all invocations of our callback function.
+  ++*reinterpret_cast<int *>(aux);
+
+  // Verify that within the callback function all filtering is temporarily
+  // disabled.
+  BPF_ASSERT(syscall(__NR_getpid) > 1);
+
+  // Verify that we can now call the underlying system call without causing
+  // infinite recursion.
+  return Sandbox::ForwardSyscall(args);
+}
+
+ErrorCode GreyListedPolicy(Sandbox *sandbox, int sysno, void *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
+  // BPF_TEST() is guaranteed to turn messages back on, after the policy
+  // function has completed.
+  setenv(kSandboxDebuggingEnv, "t", 0);
+  Die::SuppressInfoMessages(true);
+
+  // Some system calls must always be allowed, if our policy wants to make
+  // use of UnsafeTrap()
+  if (sysno == __NR_rt_sigprocmask ||
+      sysno == __NR_rt_sigreturn
+#if defined(__NR_sigprocmask)
+   || sysno == __NR_sigprocmask
+#endif
+#if defined(__NR_sigreturn)
+   || sysno == __NR_sigreturn
+#endif
+      ) {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  } else if (sysno == __NR_getpid) {
+    // Disallow getpid()
+    return ErrorCode(EPERM);
+  } else if (Sandbox::IsValidSyscallNumber(sysno)) {
+    // Allow (and count) all other system calls.
+      return sandbox->UnsafeTrap(CountSyscalls, aux);
+  } else {
+    return ErrorCode(ENOSYS);
+  }
+}
+
+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(syscall(__NR_geteuid) == syscall(__NR_getuid));
+  BPF_ASSERT(BPF_AUX == 2);
+  char name[17] = { };
+  BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
+                      (void *)NULL, (void *)NULL));
+  BPF_ASSERT(BPF_AUX == 3);
+  BPF_ASSERT(*name);
+}
+
+SANDBOX_TEST(SandboxBpf, EnableUnsafeTrapsInSigSysHandler) {
+  // Disabling warning messages that could confuse our test framework.
+  setenv(kSandboxDebuggingEnv, "t", 0);
+  Die::SuppressInfoMessages(true);
+
+  unsetenv(kSandboxDebuggingEnv);
+  SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == false);
+  setenv(kSandboxDebuggingEnv, "", 1);
+  SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == false);
+  setenv(kSandboxDebuggingEnv, "t", 1);
+  SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == true);
+}
+
+intptr_t PrctlHandler(const struct arch_seccomp_data& args, void *) {
+  if (args.args[0] == PR_CAPBSET_DROP &&
+      static_cast<int>(args.args[1]) == -1) {
+    // prctl(PR_CAPBSET_DROP, -1) is never valid. The kernel will always
+    // return an error. But our handler allows this call.
+    return 0;
+  } else {
+    return Sandbox::ForwardSyscall(args);
+  }
+}
+
+ErrorCode PrctlPolicy(Sandbox *sandbox, int sysno, void *aux) {
+  setenv(kSandboxDebuggingEnv, "t", 0);
+  Die::SuppressInfoMessages(true);
+
+  if (sysno == __NR_prctl) {
+    // Handle prctl() inside an UnsafeTrap()
+    return sandbox->UnsafeTrap(PrctlHandler, NULL);
+  } else if (Sandbox::IsValidSyscallNumber(sysno)) {
+    // Allow all other system calls.
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  } else {
+    return ErrorCode(ENOSYS);
+  }
+}
+
+BPF_TEST(SandboxBpf, ForwardSyscall, PrctlPolicy) {
+  // This call should never be allowed. But our policy will intercept it and
+  // let it pass successfully.
+  BPF_ASSERT(!prctl(PR_CAPBSET_DROP, -1, (void *)NULL, (void *)NULL,
+                    (void *)NULL));
+
+  // Verify that the call will fail, if it makes it all the way to the kernel.
+  BPF_ASSERT(prctl(PR_CAPBSET_DROP, -2, (void *)NULL, (void *)NULL,
+                   (void *)NULL) == -1);
+
+  // And verify that other uses of prctl() work just fine.
+  char name[17] = { };
+  BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
+                      (void *)NULL, (void *)NULL));
+  BPF_ASSERT(*name);
+
+  // Finally, verify that system calls other than prctl() are completely
+  // unaffected by our policy.
+  struct utsname uts = { };
+  BPF_ASSERT(!uname(&uts));
+  BPF_ASSERT(!strcmp(uts.sysname, "Linux"));
+}
+
+intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void *) {
+  return Sandbox::ForwardSyscall(args);
+}
+
+ErrorCode RedirectAllSyscallsPolicy(Sandbox *sandbox, int sysno, void *aux) {
+  setenv(kSandboxDebuggingEnv, "t", 0);
+  Die::SuppressInfoMessages(true);
+
+  // Some system calls must always be allowed, if our policy wants to make
+  // use of UnsafeTrap()
+  if (sysno == __NR_rt_sigprocmask ||
+      sysno == __NR_rt_sigreturn
+#if defined(__NR_sigprocmask)
+   || sysno == __NR_sigprocmask
+#endif
+#if defined(__NR_sigreturn)
+   || sysno == __NR_sigreturn
+#endif
+      ) {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  } else if (Sandbox::IsValidSyscallNumber(sysno)) {
+    return sandbox->UnsafeTrap(AllowRedirectedSyscall, aux);
+  } else {
+    return ErrorCode(ENOSYS);
+  }
+}
+
+int bus_handler_fd_ = -1;
+
+void SigBusHandler(int, siginfo_t *info, void *void_context) {
+  BPF_ASSERT(write(bus_handler_fd_, "\x55", 1) == 1);
+}
+
+BPF_TEST(SandboxBpf, SigBus, RedirectAllSyscallsPolicy) {
+  // We use the SIGBUS bit in the signal mask as a thread-local boolean
+  // value in the implementation of UnsafeTrap(). This is obviously a bit
+  // of a hack that could conceivably interfere with code that uses SIGBUS
+  // in more traditional ways. This test verifies that basic functionality
+  // of SIGBUS is not impacted, but it is certainly possibly to construe
+  // more complex uses of signals where our use of the SIGBUS mask is not
+  // 100% transparent. This is expected behavior.
+  int fds[2];
+  BPF_ASSERT(pipe(fds) == 0);
+  bus_handler_fd_ = fds[1];
+  struct sigaction sa = { };
+  sa.sa_sigaction = SigBusHandler;
+  sa.sa_flags = SA_SIGINFO;
+  BPF_ASSERT(sigaction(SIGBUS, &sa, NULL) == 0);
+  raise(SIGBUS);
+  char c = '\000';
+  BPF_ASSERT(read(fds[0], &c, 1) == 1);
+  BPF_ASSERT(close(fds[0]) == 0);
+  BPF_ASSERT(close(fds[1]) == 0);
+  BPF_ASSERT(c == 0x55);
+}
+
+BPF_TEST(SandboxBpf, SigMask, RedirectAllSyscallsPolicy) {
+  // Signal masks are potentially tricky to handle. For instance, if we
+  // ever tried to update them from inside a Trap() or UnsafeTrap() handler,
+  // the call to sigreturn() at the end of the signal handler would undo
+  // all of our efforts. So, it makes sense to test that sigprocmask()
+  // works, even if we have a policy in place that makes use of UnsafeTrap().
+  // In practice, this works because we force sigprocmask() to be handled
+  // entirely in the kernel.
+  sigset_t mask0, mask1, mask2;
+
+  // Call sigprocmask() to verify that SIGUSR2 wasn't blocked, if we didn't
+  // change the mask (it shouldn't have been, as it isn't blocked by default
+  // in POSIX).
+  //
+  // Use SIGUSR2 because Android seems to use SIGUSR1 for some purpose.
+  sigemptyset(&mask0);
+  BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, &mask1));
+  BPF_ASSERT(!sigismember(&mask1, SIGUSR2));
+
+  // Try again, and this time we verify that we can block it. This
+  // requires a second call to sigprocmask().
+  sigaddset(&mask0, SIGUSR2);
+  BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, NULL));
+  BPF_ASSERT(!sigprocmask(SIG_BLOCK, NULL, &mask2));
+  BPF_ASSERT( sigismember(&mask2, SIGUSR2));
+}
+
+BPF_TEST(SandboxBpf, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
+  // An UnsafeTrap() (or for that matter, a Trap()) has to report error
+  // conditions by returning an exit code in the range -1..-4096. This
+  // should happen automatically if using ForwardSyscall(). If the TrapFnc()
+  // uses some other method to make system calls, then it is responsible
+  // for computing the correct return code.
+  // This test verifies that ForwardSyscall() does the correct thing.
+
+  // The glibc system wrapper will ultimately set errno for us. So, from normal
+  // userspace, all of this should be completely transparent.
+  errno = 0;
+  BPF_ASSERT(close(-1) == -1);
+  BPF_ASSERT(errno == EBADF);
+
+  // Explicitly avoid the glibc wrapper. This is not normally the way anybody
+  // would make system calls, but it allows us to verify that we don't
+  // accidentally mess with errno, when we shouldn't.
+  errno = 0;
+  struct arch_seccomp_data args = { };
+  args.nr      = __NR_close;
+  args.args[0] = -1;
+  BPF_ASSERT(Sandbox::ForwardSyscall(args) == -EBADF);
+  BPF_ASSERT(errno == 0);
+}
+
+// Test a trap handler that makes use of a broker process to open().
+
+class InitializedOpenBroker {
+ public:
+  InitializedOpenBroker() : initialized_(false) {
+    std::vector<std::string> allowed_files;
+    allowed_files.push_back("/proc/allowed");
+    allowed_files.push_back("/proc/cpuinfo");
+
+    broker_process_.reset(new BrokerProcess(allowed_files,
+                                            std::vector<std::string>()));
+    BPF_ASSERT(broker_process() != NULL);
+    BPF_ASSERT(broker_process_->Init(NULL));
+
+    initialized_ = true;
+  }
+  bool initialized() { return initialized_; }
+  class BrokerProcess* broker_process() { return broker_process_.get(); }
+ private:
+  bool initialized_;
+  scoped_ptr<class BrokerProcess> broker_process_;
+  DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker);
+};
+
+intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
+                               void *aux) {
+  BPF_ASSERT(aux);
+  BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
+  switch(args.nr) {
+    case __NR_open:
+      return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
+          static_cast<int>(args.args[1]));
+    case __NR_openat:
+      // We only call open() so if we arrive here, it's because glibc uses
+      // the openat() system call.
+      BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
+      return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
+          static_cast<int>(args.args[2]));
+    default:
+      BPF_ASSERT(false);
+      return -ENOSYS;
+  }
+}
+
+ErrorCode DenyOpenPolicy(Sandbox *sandbox, int sysno, void *aux) {
+  InitializedOpenBroker* iob = static_cast<InitializedOpenBroker*>(aux);
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    return ErrorCode(ENOSYS);
+  }
+
+  switch (sysno) {
+    case __NR_open:
+    case __NR_openat:
+      // We get a InitializedOpenBroker class, but our trap handler wants
+      // the BrokerProcess object.
+      return ErrorCode(sandbox->Trap(BrokerOpenTrapHandler,
+                                     iob->broker_process()));
+    default:
+      return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+// We use a InitializedOpenBroker class, so that we can run unsandboxed
+// code in its constructor, which is the only way to do so in a BPF_TEST.
+BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy,
+         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"
+  BPF_ASSERT(broker_process->Open("/proc/denied", O_RDONLY) == -EPERM);
+  BPF_ASSERT(broker_process->Open("/proc/allowed", O_RDONLY) == -ENOENT);
+
+  // Now use glibc's open() as an external library would.
+  BPF_ASSERT(open("/proc/denied", O_RDONLY) == -1);
+  BPF_ASSERT(errno == EPERM);
+
+  BPF_ASSERT(open("/proc/allowed", O_RDONLY) == -1);
+  BPF_ASSERT(errno == ENOENT);
+
+  // Also test glibc's openat(), some versions of libc use it transparently
+  // instead of open().
+  BPF_ASSERT(openat(AT_FDCWD, "/proc/denied", O_RDONLY) == -1);
+  BPF_ASSERT(errno == EPERM);
+
+  BPF_ASSERT(openat(AT_FDCWD, "/proc/allowed", O_RDONLY) == -1);
+  BPF_ASSERT(errno == ENOENT);
+
+
+  // This is also white listed and does exist.
+  int cpu_info_fd = open("/proc/cpuinfo", O_RDONLY);
+  BPF_ASSERT(cpu_info_fd >= 0);
+  char buf[1024];
+  BPF_ASSERT(read(cpu_info_fd, buf, sizeof(buf)) > 0);
+}
+
+// Simple test demonstrating how to use Sandbox::Cond()
+
+ErrorCode SimpleCondTestPolicy(Sandbox *sandbox, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  }
+
+  // We deliberately return unusual errno values upon failure, so that we
+  // can uniquely test for these values. In a "real" policy, you would want
+  // to return more traditional values.
+  switch (sysno) {
+    case __NR_open:
+      // Allow opening files for reading, but don't allow writing.
+      COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
+      return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+                           O_ACCMODE /* 0x3 */,
+                           ErrorCode(EROFS),
+                           ErrorCode(ErrorCode::ERR_ALLOWED));
+    case __NR_prctl:
+      // Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
+      // disallow everything else.
+      return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+                           PR_SET_DUMPABLE,
+                           ErrorCode(ErrorCode::ERR_ALLOWED),
+             sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+                           PR_GET_DUMPABLE,
+                           ErrorCode(ErrorCode::ERR_ALLOWED),
+                           ErrorCode(ENOMEM)));
+    default:
+      return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+BPF_TEST(SandboxBpf, SimpleCondTest, SimpleCondTestPolicy) {
+  int fd;
+  BPF_ASSERT((fd = open("/proc/self/comm", O_RDWR)) == -1);
+  BPF_ASSERT(errno == EROFS);
+  BPF_ASSERT((fd = open("/proc/self/comm", O_RDONLY)) >= 0);
+  close(fd);
+
+  int ret;
+  BPF_ASSERT((ret = prctl(PR_GET_DUMPABLE)) >= 0);
+  BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1-ret) == 0);
+  BPF_ASSERT(prctl(PR_GET_ENDIAN, &ret) == -1);
+  BPF_ASSERT(errno == ENOMEM);
+}
+
+// This test exercises the Sandbox::Cond() method by building a complex
+// tree of conditional equality operations. It then makes system calls and
+// verifies that they return the values that we expected from our BPF
+// program.
+class EqualityStressTest {
+ public:
+  EqualityStressTest() {
+    // We want a deterministic test
+    srand(0);
+
+    // Iterates over system call numbers and builds a random tree of
+    // equality tests.
+    // We are actually constructing a graph of ArgValue objects. This
+    // graph will later be used to a) compute our sandbox policy, and
+    // b) drive the code that verifies the output from the BPF program.
+    COMPILE_ASSERT(kNumTestCases < (int)(MAX_PUBLIC_SYSCALL-MIN_SYSCALL-10),
+           num_test_cases_must_be_significantly_smaller_than_num_system_calls);
+    for (int sysno = MIN_SYSCALL, end = kNumTestCases; sysno < end; ++sysno) {
+      if (IsReservedSyscall(sysno)) {
+        // Skip reserved system calls. This ensures that our test frame
+        // work isn't impacted by the fact that we are overriding
+        // a lot of different system calls.
+        ++end;
+        arg_values_.push_back(NULL);
+      } else {
+        arg_values_.push_back(RandomArgValue(rand() % kMaxArgs, 0,
+                                             rand() % kMaxArgs));
+      }
+    }
+  }
+
+  ~EqualityStressTest() {
+    for (std::vector<ArgValue *>::iterator iter = arg_values_.begin();
+         iter != arg_values_.end();
+         ++iter) {
+      DeleteArgValue(*iter);
+    }
+  }
+
+  ErrorCode Policy(Sandbox *sandbox, int sysno) {
+    if (!Sandbox::IsValidSyscallNumber(sysno)) {
+      // FIXME: we should really not have to do that in a trivial policy
+      return ErrorCode(ENOSYS);
+    } else if (sysno < 0 || sysno >= (int)arg_values_.size() ||
+               IsReservedSyscall(sysno)) {
+      // We only return ErrorCode values for the system calls that
+      // are part of our test data. Every other system call remains
+      // allowed.
+      return ErrorCode(ErrorCode::ERR_ALLOWED);
+    } else {
+      // ToErrorCode() turns an ArgValue object into an ErrorCode that is
+      // suitable for use by a sandbox policy.
+      return ToErrorCode(sandbox, arg_values_[sysno]);
+    }
+  }
+
+  void VerifyFilter() {
+    // Iterate over all system calls. Skip the system calls that have
+    // previously been determined as being reserved.
+    for (int sysno = 0; sysno < (int)arg_values_.size(); ++sysno) {
+      if (!arg_values_[sysno]) {
+        // Skip reserved system calls.
+        continue;
+      }
+      // Verify that system calls return the values that we expect them to
+      // return. This involves passing different combinations of system call
+      // parameters in order to exercise all possible code paths through the
+      // BPF filter program.
+      // We arbitrarily start by setting all six system call arguments to
+      // zero. And we then recursive traverse our tree of ArgValues to
+      // determine the necessary combinations of parameters.
+      intptr_t args[6] = { };
+      Verify(sysno, args, *arg_values_[sysno]);
+    }
+  }
+
+ private:
+  struct ArgValue {
+    int argno;                     // Argument number to inspect.
+    int size;                      // Number of test cases (must be > 0).
+    struct Tests {
+      uint32_t k_value;            // Value to compare syscall arg against.
+      int      err;                // If non-zero, errno value to return.
+      struct ArgValue *arg_value;  // Otherwise, more args needs inspecting.
+    } *tests;
+    int err;                       // If none of the tests passed, this is what
+    struct ArgValue *arg_value;    // we'll return (this is the "else" branch).
+  };
+
+  bool IsReservedSyscall(int sysno) {
+    // There are a handful of system calls that we should never use in our
+    // test cases. These system calls are needed to allow the test framework
+    // to run properly.
+    // If we wanted to write fully generic code, there are more system calls
+    // that could be listed here, and it is quite difficult to come up with a
+    // truly comprehensive list. After all, we are deliberately making system
+    // calls unavailable. In practice, we have a pretty good idea of the system
+    // calls that will be made by this particular test. So, this small list is
+    // sufficient. But if anybody copy'n'pasted this code for other uses, they
+    // would have to review that the list.
+    return sysno == __NR_read       ||
+           sysno == __NR_write      ||
+           sysno == __NR_exit       ||
+           sysno == __NR_exit_group ||
+           sysno == __NR_restart_syscall;
+  }
+
+  ArgValue *RandomArgValue(int argno, int args_mask, int remaining_args) {
+    // Create a new ArgValue and fill it with random data. We use as bit mask
+    // to keep track of the system call parameters that have previously been
+    // set; this ensures that we won't accidentally define a contradictory
+    // set of equality tests.
+    struct ArgValue *arg_value = new ArgValue();
+    args_mask        |= 1 << argno;
+    arg_value->argno  = argno;
+
+    // Apply some restrictions on just how complex our tests can be.
+    // Otherwise, we end up with a BPF program that is too complicated for
+    // the kernel to load.
+    int fan_out       = kMaxFanOut;
+    if (remaining_args > 3) {
+      fan_out         = 1;
+    } else if (remaining_args > 2) {
+      fan_out         = 2;
+    }
+
+    // Create a couple of different test cases with randomized values that
+    // we want to use when comparing system call parameter number "argno".
+    arg_value->size   = rand() % fan_out + 1;
+    arg_value->tests  = new ArgValue::Tests[arg_value->size];
+
+    uint32_t k_value  = rand();
+    for (int n = 0; n < arg_value->size; ++n) {
+      // Ensure that we have unique values
+      k_value += rand() % (RAND_MAX/(kMaxFanOut+1)) + 1;
+
+      // There are two possible types of nodes. Either this is a leaf node;
+      // in that case, we have completed all the equality tests that we
+      // wanted to perform, and we can now compute a random "errno" value that
+      // we should return. Or this is part of a more complex boolean
+      // expression; in that case, we have to recursively add tests for some
+      // of system call parameters that we have not yet included in our
+      // tests.
+      arg_value->tests[n].k_value = k_value;
+      if (!remaining_args || (rand() & 1)) {
+        arg_value->tests[n].err = (rand() % 1000) + 1;
+        arg_value->tests[n].arg_value = NULL;
+      } else {
+        arg_value->tests[n].err = 0;
+        arg_value->tests[n].arg_value =
+          RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+      }
+    }
+    // Finally, we have to define what we should return if none of the
+    // previous equality tests pass. Again, we can either deal with a leaf
+    // node, or we can randomly add another couple of tests.
+    if (!remaining_args || (rand() & 1)) {
+      arg_value->err = (rand() % 1000) + 1;
+      arg_value->arg_value = NULL;
+    } else {
+      arg_value->err = 0;
+      arg_value->arg_value =
+        RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+    }
+    // We have now built a new (sub-)tree of ArgValues defining a set of
+    // boolean expressions for testing random system call arguments against
+    // random values. Return this tree to our caller.
+    return arg_value;
+  }
+
+  int RandomArg(int args_mask) {
+    // Compute a random system call parameter number.
+    int argno = rand() % kMaxArgs;
+
+    // Make sure that this same parameter number has not previously been
+    // used. Otherwise, we could end up with a test that is impossible to
+    // satisfy (e.g. args[0] == 1 && args[0] == 2).
+    while (args_mask & (1 << argno)) {
+      argno = (argno + 1) % kMaxArgs;
+    }
+    return argno;
+  }
+
+  void DeleteArgValue(ArgValue *arg_value) {
+    // Delete an ArgValue and all of its child nodes. This requires
+    // recursively descending into the tree.
+    if (arg_value) {
+      if (arg_value->size) {
+        for (int n = 0; n < arg_value->size; ++n) {
+          if (!arg_value->tests[n].err) {
+            DeleteArgValue(arg_value->tests[n].arg_value);
+          }
+        }
+        delete[] arg_value->tests;
+      }
+      if (!arg_value->err) {
+        DeleteArgValue(arg_value->arg_value);
+      }
+      delete arg_value;
+    }
+  }
+
+  ErrorCode ToErrorCode(Sandbox *sandbox, ArgValue *arg_value) {
+    // Compute the ErrorCode that should be returned, if none of our
+    // tests succeed (i.e. the system call parameter doesn't match any
+    // of the values in arg_value->tests[].k_value).
+    ErrorCode err;
+    if (arg_value->err) {
+      // If this was a leaf node, return the errno value that we expect to
+      // return from the BPF filter program.
+      err = ErrorCode(arg_value->err);
+    } else {
+      // If this wasn't a leaf node yet, recursively descend into the rest
+      // of the tree. This will end up adding a few more Sandbox::Cond()
+      // tests to our ErrorCode.
+      err = ToErrorCode(sandbox, arg_value->arg_value);
+    }
+
+    // Now, iterate over all the test cases that we want to compare against.
+    // This builds a chain of Sandbox::Cond() tests
+    // (aka "if ... elif ... elif ... elif ... fi")
+    for (int n = arg_value->size; n-- > 0; ) {
+      ErrorCode matched;
+      // Again, we distinguish between leaf nodes and subtrees.
+      if (arg_value->tests[n].err) {
+        matched = ErrorCode(arg_value->tests[n].err);
+      } else {
+        matched = ToErrorCode(sandbox, arg_value->tests[n].arg_value);
+      }
+      // For now, all of our tests are limited to 32bit.
+      // We have separate tests that check the behavior of 32bit vs. 64bit
+      // conditional expressions.
+      err = sandbox->Cond(arg_value->argno, ErrorCode::TP_32BIT,
+                          ErrorCode::OP_EQUAL, arg_value->tests[n].k_value,
+                          matched, err);
+    }
+    return err;
+  }
+
+  void Verify(int sysno, intptr_t *args, const ArgValue& arg_value) {
+    uint32_t mismatched = 0;
+    // Iterate over all the k_values in arg_value.tests[] and verify that
+    // we see the expected return values from system calls, when we pass
+    // the k_value as a parameter in a system call.
+    for (int n = arg_value.size; n-- > 0; ) {
+      mismatched += arg_value.tests[n].k_value;
+      args[arg_value.argno] = arg_value.tests[n].k_value;
+      if (arg_value.tests[n].err) {
+        VerifyErrno(sysno, args, arg_value.tests[n].err);
+      } else {
+        Verify(sysno, args, *arg_value.tests[n].arg_value);
+      }
+    }
+    // Find a k_value that doesn't match any of the k_values in
+    // arg_value.tests[]. In most cases, the current value of "mismatched"
+    // would fit this requirement. But on the off-chance that it happens
+    // to collide, we double-check.
+  try_again:
+    for (int n = arg_value.size; n-- > 0; ) {
+      if (mismatched == arg_value.tests[n].k_value) {
+        ++mismatched;
+        goto try_again;
+      }
+    }
+    // Now verify that we see the expected return value from system calls,
+    // if we pass a value that doesn't match any of the conditions (i.e. this
+    // is testing the "else" clause of the conditions).
+    args[arg_value.argno] = mismatched;
+    if (arg_value.err) {
+      VerifyErrno(sysno, args, arg_value.err);
+    } else {
+      Verify(sysno, args, *arg_value.arg_value);
+    }
+    // Reset args[arg_value.argno]. This is not technically needed, but it
+    // makes it easier to reason about the correctness of our tests.
+    args[arg_value.argno] = 0;
+  }
+
+  void VerifyErrno(int sysno, intptr_t *args, int err) {
+    // We installed BPF filters that return different errno values
+    // based on the system call number and the parameters that we decided
+    // to pass in. Verify that this condition holds true.
+    BPF_ASSERT(SandboxSyscall(sysno,
+                              args[0], args[1], args[2],
+                              args[3], args[4], args[5]) == -err);
+  }
+
+  // Vector of ArgValue trees. These trees define all the possible boolean
+  // expressions that we want to turn into a BPF filter program.
+  std::vector<ArgValue *> arg_values_;
+
+  // Don't increase these values. We are pushing the limits of the maximum
+  // BPF program that the kernel will allow us to load. If the values are
+  // increased too much, the test will start failing.
+  static const int kNumIterations = 3;
+  static const int kNumTestCases = 40;
+  static const int kMaxFanOut = 3;
+  static const int kMaxArgs = 6;
+};
+
+ErrorCode EqualityStressTestPolicy(Sandbox *sandbox, int sysno, void *aux) {
+  return reinterpret_cast<EqualityStressTest *>(aux)->Policy(sandbox, sysno);
+}
+
+BPF_TEST(SandboxBpf, EqualityTests, EqualityStressTestPolicy,
+         EqualityStressTest /* BPF_AUX */) {
+  BPF_AUX.VerifyFilter();
+}
+
+ErrorCode EqualityArgumentWidthPolicy(Sandbox *sandbox, int sysno, void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  } else if (sysno == __NR_uname) {
+    return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+                         0x55555555, ErrorCode(1), ErrorCode(2)),
+           // The BPF compiler and the BPF interpreter in the kernel are
+           // (mostly) agnostic of the host platform's word size. The compiler
+           // will happily generate code that tests a 64bit value, and the
+           // interpreter will happily perform this test.
+           // But unless there is a kernel bug, there is no way for us to pass
+           // in a 64bit quantity on a 32bit platform. The upper 32bits should
+           // always be zero. So, this test should always evaluate as false on
+           // 32bit systems.
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_EQUAL,
+                         0x55555555AAAAAAAAULL, ErrorCode(1), ErrorCode(2)));
+  } else {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+BPF_TEST(SandboxBpf, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 0, 0x55555555) == -1);
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 0, 0xAAAAAAAA) == -2);
+#if __SIZEOF_POINTER__ > 4
+  // On 32bit machines, there is no way to pass a 64bit argument through the
+  // syscall interface. So, we have to skip the part of the test that requires
+  // 64bit arguments.
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x55555555AAAAAAAAULL) == -1);
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555500000000ULL) == -2);
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x5555555511111111ULL) == -2);
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x11111111AAAAAAAAULL) == -2);
+#else
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x55555555) == -2);
+#endif
+}
+
+#if __SIZEOF_POINTER__ > 4
+// On 32bit machines, there is no way to pass a 64bit argument through the
+// syscall interface. So, we have to skip the part of the test that requires
+// 64bit arguments.
+BPF_DEATH_TEST(SandboxBpf, EqualityArgumentUnallowed64bit,
+               DEATH_MESSAGE("Unexpected 64bit argument detected"),
+               EqualityArgumentWidthPolicy) {
+  SandboxSyscall(__NR_uname, 0, 0x5555555555555555ULL);
+}
+#endif
+
+ErrorCode EqualityWithNegativeArgumentsPolicy(Sandbox *sandbox, int sysno,
+                                              void *) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  } else if (sysno == __NR_uname) {
+    return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+                         0xFFFFFFFF, ErrorCode(1), ErrorCode(2));
+  } else {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+BPF_TEST(SandboxBpf, EqualityWithNegativeArguments,
+         EqualityWithNegativeArgumentsPolicy) {
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF) == -1);
+  BPF_ASSERT(SandboxSyscall(__NR_uname, -1) == -1);
+  BPF_ASSERT(SandboxSyscall(__NR_uname, -1LL) == -1);
+}
+
+#if __SIZEOF_POINTER__ > 4
+BPF_DEATH_TEST(SandboxBpf, EqualityWithNegative64bitArguments,
+               DEATH_MESSAGE("Unexpected 64bit argument detected"),
+               EqualityWithNegativeArgumentsPolicy) {
+  // When expecting a 32bit system call argument, we look at the MSB of the
+  // 64bit value and allow both "0" and "-1". But the latter is allowed only
+  // iff the LSB was negative. So, this death test should error out.
+  BPF_ASSERT(SandboxSyscall(__NR_uname, 0xFFFFFFFF00000000LL) == -1);
+}
+#endif
+
+ErrorCode AllBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
+  // Test the OP_HAS_ALL_BITS conditional test operator with a couple of
+  // different bitmasks. We try to find bitmasks that could conceivably
+  // touch corner cases.
+  // For all of these tests, we override the uname(). We can make use with
+  // a single system call number, as we use the first system call argument to
+  // select the different bit masks that we want to test against.
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  } else if (sysno == __NR_uname) {
+    return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x0,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x1,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x3,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x80000000,
+                         ErrorCode(1), ErrorCode(0)),
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x0,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x1,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x3,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x80000000,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x100000000ULL,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x300000000ULL,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         0x100000001ULL,
+                         ErrorCode(1), ErrorCode(0)),
+
+                         sandbox->Kill("Invalid test case number"))))))))))));
+  } else {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+// Define a macro that performs tests using our test policy.
+// NOTE: Not all of the arguments in this macro are actually used!
+//       They are here just to serve as documentation of the conditions
+//       implemented in the test policy.
+//       Most notably, "op" and "mask" are unused by the macro. If you want
+//       to make changes to these values, you will have to edit the
+//       test policy instead.
+#define BITMASK_TEST(testcase, arg, op, mask, expected_value)                 \
+  BPF_ASSERT(SandboxSyscall(__NR_uname, (testcase), (arg)) == (expected_value))
+
+// Our uname() system call returns ErrorCode(1) for success and
+// ErrorCode(0) for failure. SandboxSyscall() turns this into an
+// exit code of -1 or 0.
+#define EXPECT_FAILURE  0
+#define EXPECT_SUCCESS -1
+
+// A couple of our tests behave differently on 32bit and 64bit systems, as
+// there is no way for a 32bit system call to pass in a 64bit system call
+// argument "arg".
+// We expect these tests to succeed on 64bit systems, but to tail on 32bit
+// systems.
+#define EXPT64_SUCCESS                                                        \
+  (sizeof(void *) > 4 ? EXPECT_SUCCESS : EXPECT_FAILURE)
+
+BPF_TEST(SandboxBpf, AllBitTests, AllBitTestPolicy) {
+  // 32bit test: all of 0x0 (should always be true)
+  BITMASK_TEST( 0,                   0, ALLBITS32,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 0,                   1, ALLBITS32,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 0,                   3, ALLBITS32,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 0,         0xFFFFFFFFU, ALLBITS32,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 0,                -1LL, ALLBITS32,          0, EXPECT_SUCCESS);
+
+  // 32bit test: all of 0x1
+  BITMASK_TEST( 1,                   0, ALLBITS32,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 1,                   1, ALLBITS32,        0x1, EXPECT_SUCCESS);
+  BITMASK_TEST( 1,                   2, ALLBITS32,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 1,                   3, ALLBITS32,        0x1, EXPECT_SUCCESS);
+
+  // 32bit test: all of 0x3
+  BITMASK_TEST( 2,                   0, ALLBITS32,        0x3, EXPECT_FAILURE);
+  BITMASK_TEST( 2,                   1, ALLBITS32,        0x3, EXPECT_FAILURE);
+  BITMASK_TEST( 2,                   2, ALLBITS32,        0x3, EXPECT_FAILURE);
+  BITMASK_TEST( 2,                   3, ALLBITS32,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 2,                   7, ALLBITS32,        0x3, EXPECT_SUCCESS);
+
+  // 32bit test: all of 0x80000000
+  BITMASK_TEST( 3,                   0, ALLBITS32, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 3,         0x40000000U, ALLBITS32, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 3,         0x80000000U, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 3,         0xC0000000U, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 3,       -0x80000000LL, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+
+  // 64bit test: all of 0x0 (should always be true)
+  BITMASK_TEST( 4,                   0, ALLBITS64,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 4,                   1, ALLBITS64,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 4,                   3, ALLBITS64,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 4,         0xFFFFFFFFU, ALLBITS64,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 4,       0x100000000LL, ALLBITS64,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 4,       0x300000000LL, ALLBITS64,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 4,0x8000000000000000LL, ALLBITS64,          0, EXPECT_SUCCESS);
+  BITMASK_TEST( 4,                -1LL, ALLBITS64,          0, EXPECT_SUCCESS);
+
+  // 64bit test: all of 0x1
+  BITMASK_TEST( 5,                   0, ALLBITS64,          1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,                   1, ALLBITS64,          1, EXPECT_SUCCESS);
+  BITMASK_TEST( 5,                   2, ALLBITS64,          1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,                   3, ALLBITS64,          1, EXPECT_SUCCESS);
+  BITMASK_TEST( 5,       0x100000000LL, ALLBITS64,          1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,       0x100000001LL, ALLBITS64,          1, EXPECT_SUCCESS);
+  BITMASK_TEST( 5,       0x100000002LL, ALLBITS64,          1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,       0x100000003LL, ALLBITS64,          1, EXPECT_SUCCESS);
+
+  // 64bit test: all of 0x3
+  BITMASK_TEST( 6,                   0, ALLBITS64,          3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,                   1, ALLBITS64,          3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,                   2, ALLBITS64,          3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,                   3, ALLBITS64,          3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,                   7, ALLBITS64,          3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,       0x100000000LL, ALLBITS64,          3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,       0x100000001LL, ALLBITS64,          3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,       0x100000002LL, ALLBITS64,          3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,       0x100000003LL, ALLBITS64,          3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,       0x100000007LL, ALLBITS64,          3, EXPECT_SUCCESS);
+
+  // 64bit test: all of 0x80000000
+  BITMASK_TEST( 7,                   0, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,         0x40000000U, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,         0x80000000U, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,         0xC0000000U, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,       -0x80000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,       0x100000000LL, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,       0x140000000LL, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,       0x180000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,       0x1C0000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,      -0x180000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+
+  // 64bit test: all of 0x100000000
+  BITMASK_TEST( 8,       0x000000000LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x100000000LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 8,       0x200000000LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x300000000LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 8,       0x000000001LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x100000001LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 8,       0x200000001LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x300000001LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+
+  // 64bit test: all of 0x300000000
+  BITMASK_TEST( 9,       0x000000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x100000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x200000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x300000000LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x700000000LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x000000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x100000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x200000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x300000001LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x700000001LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+
+  // 64bit test: all of 0x100000001
+  BITMASK_TEST(10,       0x000000000LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+  BITMASK_TEST(10,       0x000000001LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+  BITMASK_TEST(10,       0x100000000LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+  BITMASK_TEST(10,       0x100000001LL, ALLBITS64,0x100000001, EXPT64_SUCCESS);
+  BITMASK_TEST(10,         0xFFFFFFFFU, ALLBITS64,0x100000001, EXPECT_FAILURE);
+  BITMASK_TEST(10,                 -1L, ALLBITS64,0x100000001, EXPT64_SUCCESS);
+}
+
+ErrorCode AnyBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
+  // Test the OP_HAS_ANY_BITS conditional test operator with a couple of
+  // different bitmasks. We try to find bitmasks that could conceivably
+  // touch corner cases.
+  // For all of these tests, we override the uname(). We can make use with
+  // a single system call number, as we use the first system call argument to
+  // select the different bit masks that we want to test against.
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  } else if (sysno == __NR_uname) {
+    return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x0,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x1,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x3,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+           sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x80000000,
+                         ErrorCode(1), ErrorCode(0)),
+
+           // All the following tests don't really make much sense on 32bit
+           // systems. They will always evaluate as false.
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x0,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x1,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x3,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x80000000,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x100000000ULL,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x300000000ULL,
+                         ErrorCode(1), ErrorCode(0)),
+
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+           sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         0x100000001ULL,
+                         ErrorCode(1), ErrorCode(0)),
+
+                         sandbox->Kill("Invalid test case number"))))))))))));
+  } else {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+BPF_TEST(SandboxBpf, AnyBitTests, AnyBitTestPolicy) {
+  // 32bit test: any of 0x0 (should always be false)
+  BITMASK_TEST( 0,                   0, ANYBITS32,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 0,                   1, ANYBITS32,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 0,                   3, ANYBITS32,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 0,         0xFFFFFFFFU, ANYBITS32,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 0,                -1LL, ANYBITS32,        0x0, EXPECT_FAILURE);
+
+  // 32bit test: any of 0x1
+  BITMASK_TEST( 1,                   0, ANYBITS32,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 1,                   1, ANYBITS32,        0x1, EXPECT_SUCCESS);
+  BITMASK_TEST( 1,                   2, ANYBITS32,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 1,                   3, ANYBITS32,        0x1, EXPECT_SUCCESS);
+
+  // 32bit test: any of 0x3
+  BITMASK_TEST( 2,                   0, ANYBITS32,        0x3, EXPECT_FAILURE);
+  BITMASK_TEST( 2,                   1, ANYBITS32,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 2,                   2, ANYBITS32,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 2,                   3, ANYBITS32,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 2,                   7, ANYBITS32,        0x3, EXPECT_SUCCESS);
+
+  // 32bit test: any of 0x80000000
+  BITMASK_TEST( 3,                   0, ANYBITS32, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 3,         0x40000000U, ANYBITS32, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 3,         0x80000000U, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 3,         0xC0000000U, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 3,       -0x80000000LL, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+
+  // 64bit test: any of 0x0 (should always be false)
+  BITMASK_TEST( 4,                   0, ANYBITS64,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 4,                   1, ANYBITS64,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 4,                   3, ANYBITS64,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 4,         0xFFFFFFFFU, ANYBITS64,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 4,       0x100000000LL, ANYBITS64,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 4,       0x300000000LL, ANYBITS64,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 4,0x8000000000000000LL, ANYBITS64,        0x0, EXPECT_FAILURE);
+  BITMASK_TEST( 4,                -1LL, ANYBITS64,        0x0, EXPECT_FAILURE);
+
+  // 64bit test: any of 0x1
+  BITMASK_TEST( 5,                   0, ANYBITS64,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,                   1, ANYBITS64,        0x1, EXPECT_SUCCESS);
+  BITMASK_TEST( 5,                   2, ANYBITS64,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,                   3, ANYBITS64,        0x1, EXPECT_SUCCESS);
+  BITMASK_TEST( 5,       0x100000001LL, ANYBITS64,        0x1, EXPECT_SUCCESS);
+  BITMASK_TEST( 5,       0x100000000LL, ANYBITS64,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,       0x100000002LL, ANYBITS64,        0x1, EXPECT_FAILURE);
+  BITMASK_TEST( 5,       0x100000003LL, ANYBITS64,        0x1, EXPECT_SUCCESS);
+
+  // 64bit test: any of 0x3
+  BITMASK_TEST( 6,                   0, ANYBITS64,        0x3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,                   1, ANYBITS64,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,                   2, ANYBITS64,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,                   3, ANYBITS64,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,                   7, ANYBITS64,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,       0x100000000LL, ANYBITS64,        0x3, EXPECT_FAILURE);
+  BITMASK_TEST( 6,       0x100000001LL, ANYBITS64,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,       0x100000002LL, ANYBITS64,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,       0x100000003LL, ANYBITS64,        0x3, EXPECT_SUCCESS);
+  BITMASK_TEST( 6,       0x100000007LL, ANYBITS64,        0x3, EXPECT_SUCCESS);
+
+  // 64bit test: any of 0x80000000
+  BITMASK_TEST( 7,                   0, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,         0x40000000U, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,         0x80000000U, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,         0xC0000000U, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,       -0x80000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,       0x100000000LL, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,       0x140000000LL, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+  BITMASK_TEST( 7,       0x180000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,       0x1C0000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+  BITMASK_TEST( 7,      -0x180000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+
+  // 64bit test: any of 0x100000000
+  BITMASK_TEST( 8,       0x000000000LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x100000000LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 8,       0x200000000LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x300000000LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 8,       0x000000001LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x100000001LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 8,       0x200000001LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+  BITMASK_TEST( 8,       0x300000001LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+
+  // 64bit test: any of 0x300000000
+  BITMASK_TEST( 9,       0x000000000LL, ANYBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x100000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x200000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x300000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x700000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x000000001LL, ANYBITS64,0x300000000, EXPECT_FAILURE);
+  BITMASK_TEST( 9,       0x100000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x200000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x300000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+  BITMASK_TEST( 9,       0x700000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+
+  // 64bit test: any of 0x100000001
+  BITMASK_TEST( 10,      0x000000000LL, ANYBITS64,0x100000001, EXPECT_FAILURE);
+  BITMASK_TEST( 10,      0x000000001LL, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+  BITMASK_TEST( 10,      0x100000000LL, ANYBITS64,0x100000001, EXPT64_SUCCESS);
+  BITMASK_TEST( 10,      0x100000001LL, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+  BITMASK_TEST( 10,        0xFFFFFFFFU, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+  BITMASK_TEST( 10,                -1L, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+}
+
+intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void *aux) {
+  if (args.args[0] != (CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD)) {
+    // We expect to get called for an attempt to fork(). No need to log that
+    // call. But if we ever get called for anything else, we want to verbosely
+    // print as much information as possible.
+    const char *msg = (const char *)aux;
+    printf("Clone() was called with unexpected arguments\n"
+           "  nr: %d\n"
+           "  1: 0x%llX\n"
+           "  2: 0x%llX\n"
+           "  3: 0x%llX\n"
+           "  4: 0x%llX\n"
+           "  5: 0x%llX\n"
+           "  6: 0x%llX\n"
+           "%s\n",
+           args.nr,
+           (long long)args.args[0],  (long long)args.args[1],
+           (long long)args.args[2],  (long long)args.args[3],
+           (long long)args.args[4],  (long long)args.args[5],
+           msg);
+  }
+  return -EPERM;
+}
+
+ErrorCode PthreadPolicyEquality(Sandbox *sandbox, int sysno, void *aux) {
+  // This policy allows creating threads with pthread_create(). But it
+  // doesn't allow any other uses of clone(). Most notably, it does not
+  // allow callers to implement fork() or vfork() by passing suitable flags
+  // to the clone() system call.
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  } else if (sysno == __NR_clone) {
+    // We have seen two different valid combinations of flags. Glibc
+    // uses the more modern flags, sets the TLS from the call to clone(), and
+    // uses futexes to monitor threads. Android's C run-time library, doesn't
+    // do any of this, but it sets the obsolete (and no-op) CLONE_DETACHED.
+    // The following policy is very strict. It only allows the exact masks
+    // that we have seen in known implementations. It is probably somewhat
+    // stricter than what we would want to do.
+    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_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
+                         CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED,
+                         ErrorCode(ErrorCode::ERR_ALLOWED),
+                         sandbox->Trap(PthreadTrapHandler, aux)));
+  } else {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+ErrorCode PthreadPolicyBitMask(Sandbox *sandbox, int sysno, void *aux) {
+  // This policy allows creating threads with pthread_create(). But it
+  // doesn't allow any other uses of clone(). Most notably, it does not
+  // allow callers to implement fork() or vfork() by passing suitable flags
+  // to the clone() system call.
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    // FIXME: we should really not have to do that in a trivial policy
+    return ErrorCode(ENOSYS);
+  } else if (sysno == __NR_clone) {
+    // We have seen two different valid combinations of flags. Glibc
+    // uses the more modern flags, sets the TLS from the call to clone(), and
+    // uses futexes to monitor threads. Android's C run-time library, doesn't
+    // do any of this, but it sets the obsolete (and no-op) CLONE_DETACHED.
+    // The following policy allows for either combination of flags, but it
+    // is generally a little more conservative than strictly necessary. We
+    // err on the side of rather safe than sorry.
+    // Very noticeably though, we disallow fork() (which is often just a
+    // wrapper around clone()).
+    return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         ~uint32(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
+                                 CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
+                                 CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID|
+                                 CLONE_DETACHED),
+                         sandbox->Trap(PthreadTrapHandler,
+                                       "Unexpected CLONE_XXX flag found"),
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
+                         CLONE_THREAD|CLONE_SYSVSEM,
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+                         CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
+                         ErrorCode(ErrorCode::ERR_ALLOWED),
+           sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+                         CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
+                         sandbox->Trap(PthreadTrapHandler,
+                                       "Must set either all or none of the TLS"
+                                       " and futex bits in call to clone()"),
+                         ErrorCode(ErrorCode::ERR_ALLOWED))),
+                         sandbox->Trap(PthreadTrapHandler,
+                                       "Missing mandatory CLONE_XXX flags "
+                                       "when creating new thread")));
+  } else {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+static void *ThreadFnc(void *arg) {
+  ++*reinterpret_cast<int *>(arg);
+  SandboxSyscall(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0);
+  return NULL;
+}
+
+static void PthreadTest() {
+  // Attempt to start a joinable thread. This should succeed.
+  pthread_t thread;
+  int thread_ran = 0;
+  BPF_ASSERT(!pthread_create(&thread, NULL, ThreadFnc, &thread_ran));
+  BPF_ASSERT(!pthread_join(thread, NULL));
+  BPF_ASSERT(thread_ran);
+
+  // Attempt to start a detached thread. This should succeed.
+  thread_ran = 0;
+  pthread_attr_t attr;
+  BPF_ASSERT(!pthread_attr_init(&attr));
+  BPF_ASSERT(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
+  BPF_ASSERT(!pthread_create(&thread, &attr, ThreadFnc, &thread_ran));
+  BPF_ASSERT(!pthread_attr_destroy(&attr));
+  while (SandboxSyscall(__NR_futex, &thread_ran, FUTEX_WAIT,
+                        0, 0, 0, 0) == -EINTR) {
+  }
+  BPF_ASSERT(thread_ran);
+
+  // Attempt to fork() a process using clone(). This should fail. We use the
+  // same flags that glibc uses when calling fork(). But we don't actually
+  // try calling the fork() implementation in the C run-time library, as
+  // run-time libraries other than glibc might call __NR_fork instead of
+  // __NR_clone, and that would introduce a bogus test failure.
+  int pid;
+  BPF_ASSERT(SandboxSyscall(__NR_clone,
+                            CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,
+                            0, 0, &pid) == -EPERM);
+}
+
+BPF_TEST(SandboxBpf, PthreadEquality, PthreadPolicyEquality) {
+  PthreadTest();
+}
+
+BPF_TEST(SandboxBpf, PthreadBitMask, PthreadPolicyBitMask) {
+  PthreadTest();
+}
+
 } // namespace
diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
new file mode 100644
index 0000000..8b09a68
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2012 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 <asm/unistd.h>
+#include <errno.h>
+
+#include "sandbox/linux/seccomp-bpf/port.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+
+namespace playground2 {
+
+  asm(      // We need to be able to tell the kernel exactly where we made a
+            // system call. The C++ compiler likes to sometimes clone or
+            // inline code, which would inadvertently end up duplicating
+            // the entry point.
+            // "gcc" can suppress code duplication with suitable function
+            // attributes, but "clang" doesn't have this ability.
+            // The "clang" developer mailing list suggested that the correct
+            // and portable solution is a file-scope assembly block.
+            // N.B. We do mark our code as a proper function so that backtraces
+            // work correctly. But we make absolutely no attempt to use the
+            // ABI's calling conventions for passing arguments. We will only
+            // ever be called from assembly code and thus can pick more
+            // suitable calling conventions.
+#if defined(__i386__)
+            ".text\n"
+            ".align 16, 0x90\n"
+            ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+            // Check if "%eax" is negative. If so, do not attempt to make a
+            // system call. Instead, compute the return address that is visible
+            // to the kernel after we execute "int $0x80". This address can be
+            // used as a marker that BPF code inspects.
+            "test %eax, %eax\n"
+            "jge  1f\n"
+            // Always, make sure that our code is position-independent, or
+            // address space randomization might not work on i386. This means,
+            // we can't use "lea", but instead have to rely on "call/pop".
+            "call 0f;   .cfi_adjust_cfa_offset  4\n"
+          "0:pop  %eax; .cfi_adjust_cfa_offset -4\n"
+            "addl $2f-0b, %eax\n"
+            "ret\n"
+            // Save register that we don't want to clobber. On i386, we need to
+            // save relatively aggressively, as there are a couple or registers
+            // that are used internally (e.g. %ebx for position-independent
+            // code, and %ebp for the frame pointer), and as we need to keep at
+            // least a few registers available for the register allocator.
+          "1:push %esi; .cfi_adjust_cfa_offset 4\n"
+            "push %edi; .cfi_adjust_cfa_offset 4\n"
+            "push %ebx; .cfi_adjust_cfa_offset 4\n"
+            "push %ebp; .cfi_adjust_cfa_offset 4\n"
+            // Copy entries from the array holding the arguments into the
+            // correct CPU registers.
+            "movl  0(%edi), %ebx\n"
+            "movl  4(%edi), %ecx\n"
+            "movl  8(%edi), %edx\n"
+            "movl 12(%edi), %esi\n"
+            "movl 20(%edi), %ebp\n"
+            "movl 16(%edi), %edi\n"
+            // Enter the kernel.
+            "int  $0x80\n"
+            // This is our "magic" return address that the BPF filter sees.
+          "2:"
+            // Restore any clobbered registers that we didn't declare to the
+            // compiler.
+            "pop  %ebp; .cfi_adjust_cfa_offset -4\n"
+            "pop  %ebx; .cfi_adjust_cfa_offset -4\n"
+            "pop  %edi; .cfi_adjust_cfa_offset -4\n"
+            "pop  %esi; .cfi_adjust_cfa_offset -4\n"
+            "ret\n"
+            ".cfi_endproc\n"
+          "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__x86_64__)
+            ".text\n"
+            ".align 16, 0x90\n"
+            ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+            // Check if "%rax" is negative. If so, do not attempt to make a
+            // system call. Instead, compute the return address that is visible
+            // to the kernel after we execute "syscall". This address can be
+            // used as a marker that BPF code inspects.
+            "test %rax, %rax\n"
+            "jge  1f\n"
+            // Always make sure that our code is position-independent, or the
+            // linker will throw a hissy fit on x86-64.
+            "call 0f;   .cfi_adjust_cfa_offset  8\n"
+          "0:pop  %rax; .cfi_adjust_cfa_offset -8\n"
+            "addq $2f-0b, %rax\n"
+            "ret\n"
+            // We declared all clobbered registers to the compiler. On x86-64,
+            // there really isn't much of a problem with register pressure. So,
+            // we can go ahead and directly copy the entries from the arguments
+            // array into the appropriate CPU registers.
+          "1:movq  0(%r12), %rdi\n"
+            "movq  8(%r12), %rsi\n"
+            "movq 16(%r12), %rdx\n"
+            "movq 24(%r12), %r10\n"
+            "movq 32(%r12), %r8\n"
+            "movq 40(%r12), %r9\n"
+            // Enter the kernel.
+            "syscall\n"
+            // This is our "magic" return address that the BPF filter sees.
+          "2:ret\n"
+            ".cfi_endproc\n"
+          "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__arm__)
+            // Throughout this file, we use the same mode (ARM vs. thumb)
+            // that the C++ compiler uses. This means, when transfering control
+            // from C++ to assembly code, we do not need to switch modes (e.g.
+            // by using the "bx" instruction). It also means that our assembly
+            // code should not be invoked directly from code that lives in
+            // other compilation units, as we don't bother implementing thumb
+            // interworking. That's OK, as we don't make any of the assembly
+            // symbols public. They are all local to this file.
+            ".text\n"
+            ".align 2\n"
+            ".type SyscallAsm, %function\n"
+#if defined(__thumb__)
+            ".thumb_func\n"
+#else
+            ".arm\n"
+#endif
+ "SyscallAsm:.fnstart\n"
+            "@ args = 0, pretend = 0, frame = 8\n"
+            "@ frame_needed = 1, uses_anonymous_args = 0\n"
+#if defined(__thumb__)
+            ".cfi_startproc\n"
+            "push {r7, lr}\n"
+            ".cfi_offset 14, -4\n"
+            ".cfi_offset  7, -8\n"
+            "mov r7, sp\n"
+            ".cfi_def_cfa_register 7\n"
+            ".cfi_def_cfa_offset 8\n"
+#else
+            "stmfd sp!, {fp, lr}\n"
+            "add fp, sp, #4\n"
+#endif
+            // Check if "r0" is negative. If so, do not attempt to make a
+            // system call. Instead, compute the return address that is visible
+            // to the kernel after we execute "swi 0". This address can be
+            // used as a marker that BPF code inspects.
+            "cmp r0, #0\n"
+            "bge 1f\n"
+            "ldr r0, =2f\n"
+            "b   2f\n"
+            // We declared (almost) all clobbered registers to the compiler. On
+            // ARM there is no particular register pressure. So, we can go
+            // ahead and directly copy the entries from the arguments array
+            // into the appropriate CPU registers.
+          "1:ldr r5, [r6, #20]\n"
+            "ldr r4, [r6, #16]\n"
+            "ldr r3, [r6, #12]\n"
+            "ldr r2, [r6, #8]\n"
+            "ldr r1, [r6, #4]\n"
+            "mov r7, r0\n"
+            "ldr r0, [r6, #0]\n"
+            // Enter the kernel
+            "swi 0\n"
+            // Restore the frame pointer. Also restore the program counter from
+            // the link register; this makes us return to the caller.
+#if defined(__thumb__)
+          "2:pop {r7, pc}\n"
+            ".cfi_endproc\n"
+#else
+          "2:ldmfd sp!, {fp, pc}\n"
+#endif
+            ".fnend\n"
+          "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#endif
+  );  // asm
+
+intptr_t SandboxSyscall(int nr,
+                        intptr_t p0, intptr_t p1, intptr_t p2,
+                        intptr_t p3, intptr_t p4, intptr_t p5) {
+  // We rely on "intptr_t" to be the exact size as a "void *". This is
+  // typically true, but just in case, we add a check. The language
+  // specification allows platforms some leeway in cases, where
+  // "sizeof(void *)" is not the same as "sizeof(void (*)())". We expect
+  // that this would only be an issue for IA64, which we are currently not
+  // planning on supporting. And it is even possible that this would work
+  // on IA64, but for lack of actual hardware, I cannot test.
+  COMPILE_ASSERT(sizeof(void *) == sizeof(intptr_t),
+                 pointer_types_and_intptr_must_be_exactly_the_same_size);
+
+  const intptr_t args[6] = { p0, p1, p2, p3, p4, p5 };
+
+  // Invoke our file-scope assembly code. The constraints have been picked
+  // carefully to match what the rest of the assembly code expects in input,
+  // output, and clobbered registers.
+#if defined(__i386__)
+  intptr_t ret = nr;
+  asm volatile(
+    "call SyscallAsm\n"
+    // N.B. These are not the calling conventions normally used by the ABI.
+    : "=a"(ret)
+    : "0"(ret), "D"(args)
+    : "esp", "memory", "ecx", "edx");
+#elif defined(__x86_64__)
+  intptr_t ret = nr;
+  {
+    register const intptr_t *data __asm__("r12") = args;
+    asm volatile(
+      "lea  -128(%%rsp), %%rsp\n"  // Avoid red zone.
+      "call SyscallAsm\n"
+      "lea  128(%%rsp), %%rsp\n"
+      // N.B. These are not the calling conventions normally used by the ABI.
+      : "=a"(ret)
+      : "0"(ret), "r"(data)
+      : "rsp", "memory",
+        "rcx", "rdi", "rsi", "rdx", "r8", "r9", "r10", "r11");
+  }
+#elif defined(__arm__)
+  intptr_t ret;
+  {
+    register intptr_t inout __asm__("r0") = nr;
+    register const intptr_t *data __asm__("r6") = args;
+    asm volatile(
+      "bl SyscallAsm\n"
+      // N.B. These are not the calling conventions normally used by the ABI.
+      : "=r"(inout)
+      : "0"(inout), "r"(data)
+      : "lr", "memory", "r1", "r2", "r3", "r4", "r5"
+#if !defined(__arm__)
+      // In thumb mode, we cannot use "r7" as a general purpose register, as
+      // it is our frame pointer. We have to manually manage and preserve it.
+      // In ARM mode, we have a dedicated frame pointer register and "r7" is
+      // thus available as a general purpose register. We don't preserve it,
+      // but instead mark it as clobbered.
+        , "r7"
+#endif
+      );
+    ret = inout;
+  }
+#else
+  errno = ENOSYS;
+  intptr_t ret = -1;
+#endif
+  return ret;
+}
+
+}  // namespace
diff --git a/sandbox/linux/seccomp-bpf/syscall.h b/sandbox/linux/seccomp-bpf/syscall.h
new file mode 100644
index 0000000..39b1bca
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 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_SYSCALL_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+
+#include <stdint.h>
+
+namespace playground2 {
+
+// We have to make sure that we have a single "magic" return address for
+// our system calls, which we can check from within a BPF filter. This
+// works by writing a little bit of asm() code that a) enters the kernel, and
+// that also b) can be invoked in a way that computes this return address.
+// Passing "nr" as "-1" computes the "magic" return address. Passing any
+// other value invokes the appropriate system call.
+intptr_t SandboxSyscall(int nr,
+                        intptr_t p0, intptr_t p1, intptr_t p2,
+                        intptr_t p3, intptr_t p4, intptr_t p5);
+
+
+// System calls can take up to six parameters. Traditionally, glibc
+// implements this property by using variadic argument lists. This works, but
+// confuses modern tools such as valgrind, because we are nominally passing
+// uninitialized data whenever we call through this function and pass less
+// than the full six arguments.
+// So, instead, we use C++'s template system to achieve a very similar
+// effect. C++ automatically sets the unused parameters to zero for us, and
+// it also does the correct type expansion (e.g. from 32bit to 64bit) where
+// necessary.
+// We have to use C-style cast operators as we want to be able to accept both
+// integer and pointer types.
+// We explicitly mark all functions as inline. This is not necessary in
+// optimized builds, where the compiler automatically figures out that it
+// can inline everything. But it makes stack traces of unoptimized builds
+// easier to read as it hides implementation details.
+#if __cplusplus >= 201103  // C++11
+
+template<class T0 = intptr_t, class T1 = intptr_t, class T2 = intptr_t,
+         class T3 = intptr_t, class T4 = intptr_t, class T5 = intptr_t>
+inline intptr_t SandboxSyscall(int nr,
+                               T0 p0 = 0, T1 p1 = 0, T2 p2 = 0,
+                               T3 p3 = 0, T4 p4 = 0, T5 p5 = 0)
+  __attribute__((always_inline));
+
+template<class T0, class T1, class T2, class T3, class T4, class T5>
+inline intptr_t SandboxSyscall(int nr,
+                               T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+  return SandboxSyscall(nr,
+                        (intptr_t)p0, (intptr_t)p1, (intptr_t)p2,
+                        (intptr_t)p3, (intptr_t)p4, (intptr_t)p5);
+}
+
+#else  // Pre-C++11
+
+// TODO(markus): C++11 has a much more concise and readable solution for
+//   expressing what we are doing here. Delete the fall-back code for older
+//   compilers as soon as we have fully switched to C++11
+
+template<class T0, class T1, class T2, class T3, class T4, class T5>
+inline intptr_t SandboxSyscall(int nr,
+                               T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
+  __attribute__((always_inline));
+template<class T0, class T1, class T2, class T3, class T4, class T5>
+inline intptr_t SandboxSyscall(int nr,
+                               T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+  return SandboxSyscall(nr,
+                        (intptr_t)p0, (intptr_t)p1, (intptr_t)p2,
+                        (intptr_t)p3, (intptr_t)p4, (intptr_t)p5);
+}
+
+template<class T0, class T1, class T2, class T3, class T4>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4)
+  __attribute__((always_inline));
+template<class T0, class T1, class T2, class T3, class T4>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) {
+  return SandboxSyscall(nr, p0, p1, p2, p3, p4, 0);
+}
+
+template<class T0, class T1, class T2, class T3>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3)
+  __attribute__((always_inline));
+template<class T0, class T1, class T2, class T3>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
+  return SandboxSyscall(nr, p0, p1, p2, p3, 0, 0);
+}
+
+template<class T0, class T1, class T2>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2)
+  __attribute__((always_inline));
+template<class T0, class T1, class T2>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1, T2 p2) {
+  return SandboxSyscall(nr, p0, p1, p2, 0, 0, 0);
+}
+
+template<class T0, class T1>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1)
+  __attribute__((always_inline));
+template<class T0, class T1>
+inline intptr_t SandboxSyscall(int nr, T0 p0, T1 p1) {
+  return SandboxSyscall(nr, p0, p1, 0, 0, 0, 0);
+}
+
+template<class T0>
+inline intptr_t SandboxSyscall(int nr, T0 p0)
+  __attribute__((always_inline));
+template<class T0>
+inline intptr_t SandboxSyscall(int nr, T0 p0) {
+  return SandboxSyscall(nr, p0, 0, 0, 0, 0, 0);
+}
+
+inline intptr_t SandboxSyscall(int nr)
+  __attribute__((always_inline));
+inline intptr_t SandboxSyscall(int nr) {
+  return SandboxSyscall(nr, 0, 0, 0, 0, 0, 0);
+}
+
+#endif  // Pre-C++11
+
+}  // namespace
+
+#endif  // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
index 583dcf6..4ea979a 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -2,7 +2,8 @@
 // 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.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
 
 namespace playground2 {
@@ -16,7 +17,8 @@
   do {
     // |num_| has been initialized to 0, which we assume is also MIN_SYSCALL.
     // This true for supported architectures (Intel and ARM EABI).
-    CHECK_EQ(MIN_SYSCALL, 0u);
+    COMPILE_ASSERT(MIN_SYSCALL == 0u,
+                   min_syscall_should_always_be_zero);
     val = num_;
 
     // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
@@ -78,14 +80,16 @@
   return false;
 }
 
-bool SyscallIterator::IsArmPrivate(uint32_t num) {
 #if defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+bool SyscallIterator::IsArmPrivate(uint32_t num) {
   return (num >= MIN_PRIVATE_SYSCALL && num <= MAX_PRIVATE_SYSCALL) ||
          (num >= MIN_GHOST_SYSCALL && num <= MAX_SYSCALL);
-#else
-  return false;
-#endif
 }
+#else
+bool SyscallIterator::IsArmPrivate(uint32_t) {
+  return false;
+}
+#endif
 
 }  // namespace
 
diff --git a/sandbox/linux/seccomp-bpf/syscall_iterator.h b/sandbox/linux/seccomp-bpf/syscall_iterator.h
index 39568d8..e17593d 100644
--- a/sandbox/linux/seccomp-bpf/syscall_iterator.h
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.h
@@ -7,8 +7,6 @@
 
 #include <stdint.h>
 
-#include <base/logging.h>
-
 namespace playground2 {
 
 // Iterates over the entire system call range from 0..0xFFFFFFFFu. This
@@ -49,7 +47,7 @@
   bool     done_;
   uint32_t num_;
 
-  DISALLOW_COPY_AND_ASSIGN(SyscallIterator);
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallIterator);
 };
 
 }  // namespace playground2
diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
new file mode 100644
index 0000000..136deb6
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2012 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 <asm/unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace playground2;
+
+namespace {
+
+// Different platforms use different symbols for the six-argument version
+// of the mmap() system call. Test for the correct symbol at compile time.
+#ifdef __NR_mmap2
+const int kMMapNr = __NR_mmap2;
+#else
+const int kMMapNr = __NR_mmap;
+#endif
+
+TEST(Syscall, WellKnownEntryPoint) {
+  // Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM,
+  // where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
+  // are still testing ARM code in the next set of tests.
+#if !defined(__arm__)
+  EXPECT_NE(SandboxSyscall(-1), syscall(-1));
+#endif
+
+  // If possible, test that SandboxSyscall(-1) returns the address right after
+  // a kernel entry point.
+#if defined(__i386__)
+  EXPECT_EQ(0x80CDu, ((uint16_t *)SandboxSyscall(-1))[-1]);      // INT 0x80
+#elif defined(__x86_64__)
+  EXPECT_EQ(0x050Fu, ((uint16_t *)SandboxSyscall(-1))[-1]);      // SYSCALL
+#elif defined(__arm__)
+#if defined(__thumb__)
+  EXPECT_EQ(0xDF00u, ((uint16_t *)SandboxSyscall(-1))[-1]);      // SWI 0
+#else
+  EXPECT_EQ(0xEF000000u, ((uint32_t *)SandboxSyscall(-1))[-1]);  // SVC 0
+#endif
+#else
+  #warning Incomplete test case; need port for target platform
+#endif
+}
+
+TEST(Syscall, TrivialSyscallNoArgs) {
+  // Test that we can do basic system calls
+  EXPECT_EQ(SandboxSyscall(__NR_getpid), syscall(__NR_getpid));
+}
+
+TEST(Syscall, TrivialSyscallOneArg) {
+  int new_fd;
+  // Duplicate standard error and close it.
+  ASSERT_GE(new_fd = SandboxSyscall(__NR_dup, 2), 0);
+  int close_return_value = HANDLE_EINTR(SandboxSyscall(__NR_close, new_fd));
+  ASSERT_EQ(close_return_value, 0);
+}
+
+// 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.
+  std::vector<uint64_t>* const seen_syscall_args =
+      static_cast<std::vector<uint64_t>*>(aux);
+  BPF_ASSERT(arraysize(args.args) == 6);
+  seen_syscall_args->assign(args.args, args.args + arraysize(args.args));
+  return -ENOMEM;
+}
+
+ErrorCode CopyAllArgsOnUnamePolicy(Sandbox *sandbox, int sysno, void *aux) {
+  if (!Sandbox::IsValidSyscallNumber(sysno)) {
+    return ErrorCode(ENOSYS);
+  }
+  if (sysno == __NR_uname) {
+    return sandbox->Trap(CopySyscallArgsToAux, aux);
+  } else {
+    return ErrorCode(ErrorCode::ERR_ALLOWED);
+  }
+}
+
+// We are testing SandboxSyscall() by making use of a BPF filter that allows us
+// to inspect the system call arguments that the kernel saw.
+BPF_TEST(Syscall, SyntheticSixArgs, CopyAllArgsOnUnamePolicy,
+         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
+  // implementation details of kernel BPF filters and we will need to document
+  // the expected behavior very clearly.
+  int syscall_args[6];
+  for (size_t i = 0; i < arraysize(syscall_args); ++i) {
+    syscall_args[i] = kExpectedValue + i;
+  }
+
+  // We could use pretty much any system call we don't need here. uname() is
+  // nice because it doesn't have any dangerous side effects.
+  BPF_ASSERT(SandboxSyscall(__NR_uname, syscall_args[0],
+                                        syscall_args[1],
+                                        syscall_args[2],
+                                        syscall_args[3],
+                                        syscall_args[4],
+                                        syscall_args[5]) == -ENOMEM);
+
+  // We expect the trap handler to have copied the 6 arguments.
+  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]));
+}
+
+TEST(Syscall, ComplexSyscallSixArgs) {
+  int fd;
+  ASSERT_LE(0, fd = SandboxSyscall(__NR_open, "/dev/null", O_RDWR, 0L));
+
+  // Use mmap() to allocate some read-only memory
+  char *addr0;
+  ASSERT_NE((char *)NULL,
+            addr0 = reinterpret_cast<char *>(
+              SandboxSyscall(kMMapNr, (void *)NULL, 4096, PROT_READ,
+                             MAP_PRIVATE|MAP_ANONYMOUS, fd, 0L)));
+
+  // Try to replace the existing mapping with a read-write mapping
+  char *addr1;
+  ASSERT_EQ(addr0,
+            addr1 = reinterpret_cast<char *>(
+              SandboxSyscall(kMMapNr, addr0, 4096L, PROT_READ|PROT_WRITE,
+                             MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,
+                             fd, 0L)));
+  ++*addr1; // This should not seg fault
+
+  // Clean up
+  EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr1, 4096L));
+  EXPECT_EQ(0, HANDLE_EINTR(SandboxSyscall(__NR_close, fd)));
+
+  // Check that the offset argument (i.e. the sixth argument) is processed
+  // correctly.
+  ASSERT_GE(fd = SandboxSyscall(__NR_open, "/proc/self/exe", O_RDONLY, 0L), 0);
+  char *addr2, *addr3;
+  ASSERT_NE((char *)NULL,
+            addr2 = reinterpret_cast<char *>(
+              SandboxSyscall(kMMapNr, (void *)NULL, 8192L, PROT_READ,
+                             MAP_PRIVATE, fd, 0L)));
+  ASSERT_NE((char *)NULL,
+            addr3 = reinterpret_cast<char *>(
+              SandboxSyscall(kMMapNr, (void *)NULL, 4096L, PROT_READ,
+                             MAP_PRIVATE, fd,
+#if defined(__NR_mmap2)
+                      1L
+#else
+                      4096L
+#endif
+                      )));
+  EXPECT_EQ(0, memcmp(addr2 + 4096, addr3, 4096));
+
+  // Just to be absolutely on the safe side, also verify that the file
+  // contents matches what we are getting from a read() operation.
+  char buf[8192];
+  EXPECT_EQ(8192, SandboxSyscall(__NR_read, fd, buf, 8192L));
+  EXPECT_EQ(0, memcmp(addr2, buf, 8192));
+
+  // Clean up
+  EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr2, 8192L));
+  EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr3, 4096L));
+  EXPECT_EQ(0, HANDLE_EINTR(SandboxSyscall(__NR_close, fd)));
+}
+
+} // namespace
diff --git a/sandbox/linux/seccomp-bpf/trap.cc b/sandbox/linux/seccomp-bpf/trap.cc
new file mode 100644
index 0000000..85d7d36
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2012 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 <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#ifndef SECCOMP_BPF_STANDALONE
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+// Android's signal.h doesn't define ucontext etc.
+#if defined(OS_ANDROID)
+#include "sandbox/linux/services/android_ucontext.h"
+#endif
+
+#include <limits>
+
+
+namespace {
+
+const int kCapacityIncrement = 20;
+
+// Unsafe traps can only be turned on, if the user explicitly allowed them
+// by setting the CHROME_SANDBOX_DEBUGGING environment variable.
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// We need to tell whether we are performing a "normal" callback, or
+// whether we were called recursively from within a UnsafeTrap() callback.
+// This is a little tricky to do, because we need to somehow get access to
+// per-thread data from within a signal context. Normal TLS storage is not
+// safely accessible at this time. We could roll our own, but that involves
+// a lot of complexity. Instead, we co-opt one bit in the signal mask.
+// If BUS is blocked, we assume that we have been called recursively.
+// There is a possibility for collision with other code that needs to do
+// this, but in practice the risks are low.
+// If SIGBUS turns out to be a problem, we could instead co-opt one of the
+// realtime signals. There are plenty of them. Unfortunately, there is no
+// way to mark a signal as allocated. So, the potential for collision is
+// possibly even worse.
+bool GetIsInSigHandler(const ucontext_t *ctx) {
+  // Note: on Android, sigismember does not take a pointer to const.
+  return sigismember(const_cast<sigset_t*>(&ctx->uc_sigmask), SIGBUS);
+}
+
+void SetIsInSigHandler() {
+  sigset_t mask;
+  if (sigemptyset(&mask) ||
+      sigaddset(&mask, SIGBUS) ||
+      sigprocmask(SIG_BLOCK, &mask, NULL)) {
+    SANDBOX_DIE("Failed to block SIGBUS");
+  }
+}
+
+}  // namespace
+
+namespace playground2 {
+
+Trap::Trap()
+    : trap_array_(NULL),
+      trap_array_size_(0),
+      trap_array_capacity_(0),
+      has_unsafe_traps_(false) {
+  // Set new SIGSYS handler
+  struct sigaction sa = { };
+  sa.sa_sigaction = SigSysAction;
+  sa.sa_flags = SA_SIGINFO | SA_NODEFER;
+  if (sigaction(SIGSYS, &sa, NULL) < 0) {
+    SANDBOX_DIE("Failed to configure SIGSYS handler");
+  }
+
+  // Unmask SIGSYS
+  sigset_t mask;
+  if (sigemptyset(&mask) ||
+      sigaddset(&mask, SIGSYS) ||
+      sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
+    SANDBOX_DIE("Failed to configure SIGSYS handler");
+  }
+}
+
+Trap *Trap::GetInstance() {
+  // Note: This class is not thread safe. It is the caller's responsibility
+  // to avoid race conditions. Normally, this is a non-issue as the sandbox
+  // can only be initialized if there are no other threads present.
+  // Also, this is not a normal singleton. Once created, the global trap
+  // object must never be destroyed again.
+  if (!global_trap_) {
+    global_trap_ = new Trap();
+    if (!global_trap_) {
+      SANDBOX_DIE("Failed to allocate global trap handler");
+    }
+  }
+  return global_trap_;
+}
+
+void Trap::SigSysAction(int nr, siginfo_t *info, void *void_context) {
+  if (!global_trap_) {
+    SANDBOX_DIE("This can't happen. Found no global singleton instance "
+                "for Trap() handling.");
+  }
+  global_trap_->SigSys(nr, info, void_context);
+}
+
+void Trap::SigSys(int nr, siginfo_t *info, void *void_context) {
+  // Various sanity checks to make sure we actually received a signal
+  // triggered by a BPF filter. If something else triggered SIGSYS
+  // (e.g. kill()), there is really nothing we can do with this signal.
+  if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context ||
+      info->si_errno <= 0 ||
+      static_cast<size_t>(info->si_errno) > trap_array_size_) {
+    // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
+    // safe and can lead to bugs. We should eventually implement a different
+    // logging and reporting mechanism that is safe to be called from
+    // the sigSys() handler.
+    // TODO: If we feel confident that our code otherwise works correctly, we
+    //       could actually make an argument that spurious SIGSYS should
+    //       just get silently ignored. TBD
+  sigsys_err:
+    SANDBOX_DIE("Unexpected SIGSYS received");
+  }
+
+  // Signal handlers should always preserve "errno". Otherwise, we could
+  // trigger really subtle bugs.
+  int old_errno   = errno;
+
+  // Obtain the signal context. This, most notably, gives us access to
+  // all CPU registers at the time of the signal.
+  ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context);
+
+  // Obtain the siginfo information that is specific to SIGSYS. Unfortunately,
+  // most versions of glibc don't include this information in siginfo_t. So,
+  // we need to explicitly copy it into a arch_sigsys structure.
+  struct arch_sigsys sigsys;
+  memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
+
+  // Some more sanity checks.
+  if (sigsys.ip != reinterpret_cast<void *>(SECCOMP_IP(ctx)) ||
+      sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) ||
+      sigsys.arch != SECCOMP_ARCH) {
+    goto sigsys_err;
+  }
+
+  intptr_t rc;
+  if (has_unsafe_traps_ && GetIsInSigHandler(ctx)) {
+    errno = old_errno;
+    if (sigsys.nr == __NR_clone) {
+      SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler");
+    }
+    rc = SandboxSyscall(sigsys.nr,
+                        SECCOMP_PARM1(ctx), SECCOMP_PARM2(ctx),
+                        SECCOMP_PARM3(ctx), SECCOMP_PARM4(ctx),
+                        SECCOMP_PARM5(ctx), SECCOMP_PARM6(ctx));
+  } else {
+    const ErrorCode& err = trap_array_[info->si_errno - 1];
+    if (!err.safe_) {
+      SetIsInSigHandler();
+    }
+
+    // Copy the seccomp-specific data into a arch_seccomp_data structure. This
+    // is what we are showing to TrapFnc callbacks that the system call
+    // evaluator registered with the sandbox.
+    struct arch_seccomp_data data = {
+      sigsys.nr,
+      SECCOMP_ARCH,
+      reinterpret_cast<uint64_t>(sigsys.ip),
+      {
+        static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
+        static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
+        static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
+        static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
+        static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
+        static_cast<uint64_t>(SECCOMP_PARM6(ctx))
+      }
+    };
+
+    // Now call the TrapFnc callback associated with this particular instance
+    // of SECCOMP_RET_TRAP.
+    rc = err.fnc_(data, err.aux_);
+  }
+
+  // Update the CPU register that stores the return code of the system call
+  // that we just handled, and restore "errno" to the value that it had
+  // before entering the signal handler.
+  SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc);
+  errno               = old_errno;
+
+  return;
+}
+
+bool Trap::TrapKey::operator<(const TrapKey& o) const {
+  if (fnc != o.fnc) {
+    return fnc < o.fnc;
+  } else if (aux != o.aux) {
+    return aux < o.aux;
+  } else {
+    return safe < o.safe;
+  }
+}
+
+ErrorCode Trap::MakeTrap(TrapFnc fnc, const void *aux, bool safe) {
+  return GetInstance()->MakeTrapImpl(fnc, aux, safe);
+}
+
+ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) {
+  if (!safe && !SandboxDebuggingAllowedByUser()) {
+    // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
+    // we never return an ErrorCode that is marked as "unsafe". This also
+    // means, the BPF compiler will never emit code that allow unsafe system
+    // calls to by-pass the filter (because they use the magic return address
+    // from SandboxSyscall(-1)).
+
+    // This SANDBOX_DIE() can optionally be removed. It won't break security,
+    // but it might make error messages from the BPF compiler a little harder
+    // to understand. Removing the SANDBOX_DIE() allows callers to easyly check
+    // whether unsafe traps are supported (by checking whether the returned
+    // ErrorCode is ET_INVALID).
+    SANDBOX_DIE("Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
+                "is enabled");
+
+    return ErrorCode();
+  }
+
+  // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
+  // of a SECCOMP_RET_TRAP.
+  TrapKey key(fnc, aux, safe);
+  TrapIds::const_iterator iter = trap_ids_.find(key);
+
+  // We return unique identifiers together with SECCOMP_RET_TRAP. This allows
+  // us to associate trap with the appropriate handler. The kernel allows us
+  // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to
+  // avoid 0, as it could be confused for a trap without any specific id.
+  // The nice thing about sequentially numbered identifiers is that we can also
+  // trivially look them up from our signal handler without making any system
+  // calls that might be async-signal-unsafe.
+  // In order to do so, we store all of our traps in a C-style trap_array_.
+  uint16_t id;
+  if (iter != trap_ids_.end()) {
+    // We have seen this pair before. Return the same id that we assigned
+    // earlier.
+    id = iter->second;
+  } else {
+    // This is a new pair. Remember it and assign a new id.
+    if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
+        trap_array_size_ >= std::numeric_limits<typeof(id)>::max()) {
+      // In practice, this is pretty much impossible to trigger, as there
+      // are other kernel limitations that restrict overall BPF program sizes.
+      SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
+    }
+    id = trap_array_size_ + 1;
+
+    // Our callers ensure that there are no other threads accessing trap_array_
+    // concurrently (typically this is done by ensuring that we are single-
+    // threaded while the sandbox is being set up). But we nonetheless are
+    // modifying a life data structure that could be accessed any time a
+    // system call is made; as system calls could be triggering SIGSYS.
+    // So, we have to be extra careful that we update trap_array_ atomically.
+    // In particular, this means we shouldn't be using realloc() to resize it.
+    // Instead, we allocate a new array, copy the values, and then switch the
+    // pointer. We only really care about the pointer being updated atomically
+    // and the data that is pointed to being valid, as these are the only
+    // values accessed from the signal handler. It is OK if trap_array_size_
+    // is inconsistent with the pointer, as it is monotonously increasing.
+    // Also, we only care about compiler barriers, as the signal handler is
+    // triggered synchronously from a system call. We don't have to protect
+    // against issues with the memory model or with completely asynchronous
+    // events.
+    if (trap_array_size_ >= trap_array_capacity_) {
+      trap_array_capacity_     += kCapacityIncrement;
+      ErrorCode *old_trap_array = trap_array_;
+      ErrorCode *new_trap_array = new ErrorCode[trap_array_capacity_];
+
+      // Language specs are unclear on whether the compiler is allowed to move
+      // the "delete[]" above our preceding assignments and/or memory moves,
+      // iff the compiler believes that "delete[]" doesn't have any other
+      // global side-effects.
+      // We insert optimization barriers to prevent this from happening.
+      // The first barrier is probably not needed, but better be explicit in
+      // what we want to tell the compiler.
+      // The clang developer mailing list couldn't answer whether this is a
+      // legitimate worry; but they at least thought that the barrier is
+      // sufficient to prevent the (so far hypothetical) problem of re-ordering
+      // of instructions by the compiler.
+      memcpy(new_trap_array, trap_array_, trap_array_size_*sizeof(ErrorCode));
+      asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
+      trap_array_ = new_trap_array;
+      asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
+
+      delete[] old_trap_array;
+    }
+    trap_ids_[key] = id;
+    trap_array_[trap_array_size_] = ErrorCode(fnc, aux, safe, id);
+    return trap_array_[trap_array_size_++];
+  }
+
+  return ErrorCode(fnc, aux, safe, id);
+}
+
+bool Trap::SandboxDebuggingAllowedByUser() const {
+  const char *debug_flag = getenv(kSandboxDebuggingEnv);
+  return debug_flag && *debug_flag;
+}
+
+
+bool Trap::EnableUnsafeTrapsInSigSysHandler() {
+  Trap *trap = GetInstance();
+  if (!trap->has_unsafe_traps_) {
+    // Unsafe traps are a one-way fuse. Once enabled, they can never be turned
+    // off again.
+    // We only allow enabling unsafe traps, if the user explicitly set an
+    // appropriate environment variable. This prevents bugs that accidentally
+    // disable all sandboxing for all users.
+    if (trap->SandboxDebuggingAllowedByUser()) {
+      // We only ever print this message once, when we enable unsafe traps the
+      // first time.
+      SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
+      trap->has_unsafe_traps_ = true;
+    } else {
+      SANDBOX_INFO("Cannot disable sandbox and use unsafe traps unless "
+                   "CHROME_SANDBOX_DEBUGGING is turned on first");
+    }
+  }
+  // Returns the, possibly updated, value of has_unsafe_traps_.
+  return trap->has_unsafe_traps_;
+}
+
+ErrorCode Trap::ErrorCodeFromTrapId(uint16_t id) {
+  if (global_trap_ && id > 0 && id <= global_trap_->trap_array_size_) {
+    return global_trap_->trap_array_[id - 1];
+  } else {
+    return ErrorCode();
+  }
+}
+
+Trap *Trap::global_trap_;
+
+}  // namespace playground2
diff --git a/sandbox/linux/seccomp-bpf/trap.h b/sandbox/linux/seccomp-bpf/trap.h
new file mode 100644
index 0000000..db29757
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 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_TRAP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+
+#include <signal.h>
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/port.h"
+
+
+namespace playground2 {
+
+class ErrorCode;
+
+// The Trap class allows a BPF filter program to branch out to user space by
+// raising a SIGSYS signal.
+// N.B.: This class does not perform any synchronization operations. If
+//   modifications are made to any of the traps, it is the caller's
+//   responsibility to ensure that this happens in a thread-safe fashion.
+//   Preferably, that means that no other threads should be running at that
+//   time. For the purposes of our sandbox, this assertion should always be
+//   true. Threads are incompatible with the seccomp sandbox anyway.
+class Trap {
+ public:
+  // TrapFnc is a pointer to a function that handles Seccomp traps in
+  // user-space. The seccomp policy can request that a trap handler gets
+  // installed; it does so by returning a suitable ErrorCode() from the
+  // syscallEvaluator. See the ErrorCode() constructor for how to pass in
+  // the function pointer.
+  // Please note that TrapFnc is executed from signal context and must be
+  // async-signal safe:
+  // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+  // Also note that it follows the calling convention of native system calls.
+  // In other words, it reports an error by returning an exit code in the
+  // range -1..-4096. It should not set errno when reporting errors; on the
+  // other hand, accidentally modifying errno is harmless and the changes will
+  // be undone afterwards.
+  typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
+
+  // Registers a new trap handler and sets up the appropriate SIGSYS handler
+  // as needed.
+  // N.B.: This makes a permanent state change. Traps cannot be unregistered,
+  //   as that would break existing BPF filters that are still active.
+  static ErrorCode MakeTrap(TrapFnc fnc, const void *aux, bool safe);
+
+  // Enables support for unsafe traps in the SIGSYS signal handler. This is a
+  // one-way fuse. It works in conjunction with the BPF compiler emitting code
+  // that unconditionally allows system calls, if they have a magic return
+  // address (i.e. SandboxSyscall(-1)).
+  // Once unsafe traps are enabled, the sandbox is essentially compromised.
+  // But this is still a very useful feature for debugging purposes. Use with
+  // care. This feature is availably only if enabled by the user (see above).
+  // Returns "true", if unsafe traps were turned on.
+  static bool EnableUnsafeTrapsInSigSysHandler();
+
+  // Returns the ErrorCode associate with a particular trap id.
+  static ErrorCode ErrorCodeFromTrapId(uint16_t id);
+
+ private:
+  // The destructor is unimplemented. Don't ever attempt to destruct this
+  // object. It'll break subsequent system calls that trigger a SIGSYS.
+  ~Trap();
+
+  struct TrapKey {
+    TrapKey(TrapFnc f, const void *a, bool s)
+        : fnc(f),
+          aux(a),
+          safe(s) {
+    }
+    TrapFnc    fnc;
+    const void *aux;
+    bool       safe;
+    bool operator<(const TrapKey&) const;
+  };
+  typedef std::map<TrapKey, uint16_t> TrapIds;
+
+  // We only have a very small number of methods. We opt to make them static
+  // and have them internally call GetInstance(). This is a little more
+  // convenient than having each caller obtain short-lived reference to the
+  // singleton.
+  // It also gracefully deals with methods that should check for the singleton,
+  // but avoid instantiating it, if it doesn't exist yet
+  // (e.g. ErrorCodeFromTrapId()).
+  static Trap *GetInstance();
+  static void SigSysAction(int nr, siginfo_t *info, void *void_context);
+
+  void SigSys(int nr, siginfo_t *info, void *void_context);
+  ErrorCode MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe);
+  bool SandboxDebuggingAllowedByUser() const;
+
+
+
+  // We have a global singleton that handles all of our SIGSYS traps. This
+  // variable must never be deallocated after it has been set up initially, as
+  // there is no way to reset in-kernel BPF filters that generate SIGSYS
+  // events.
+  static Trap *global_trap_;
+
+  TrapIds   trap_ids_;             // Maps from TrapKeys to numeric ids
+  ErrorCode *trap_array_;          // Array of ErrorCodes indexed by ids
+  size_t    trap_array_size_;      // Currently used size of array
+  size_t    trap_array_capacity_;  // Currently allocated capacity of array
+  bool      has_unsafe_traps_;     // Whether unsafe traps have been enabled
+
+  // Our constructor is private. A shared global instance is created
+  // automatically as needed.
+  // Copying and assigning is unimplemented. It doesn't make sense for a
+  // singleton.
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Trap);
+};
+
+}  // namespace playground2
+
+#endif  // SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
diff --git a/sandbox/linux/seccomp-bpf/util.cc b/sandbox/linux/seccomp-bpf/util.cc
deleted file mode 100644
index 904a169..0000000
--- a/sandbox/linux/seccomp-bpf/util.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (c) 2012 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 <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-#include "sandbox/linux/seccomp-bpf/util.h"
-
-namespace playground2 {
-
-bool Util::sendFds(int transport, const void *buf, size_t len, ...) {
-  int count = 0;
-  va_list ap;
-  va_start(ap, len);
-  while (va_arg(ap, int) >= 0) {
-    ++count;
-  }
-  va_end(ap);
-  if (!count) {
-    return false;
-  }
-  char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
-  memset(cmsg_buf, 0, sizeof(cmsg_buf));
-  struct iovec  iov[2] = { { 0 } };
-  struct msghdr msg    = { 0 };
-  int dummy            = 0;
-  iov[0].iov_base      = &dummy;
-  iov[0].iov_len       = sizeof(dummy);
-  if (buf && len > 0) {
-    iov[1].iov_base    = const_cast<void *>(buf);
-    iov[1].iov_len     = len;
-  }
-  msg.msg_iov          = iov;
-  msg.msg_iovlen       = (buf && len > 0) ? 2 : 1;
-  msg.msg_control      = cmsg_buf;
-  msg.msg_controllen   = CMSG_LEN(count*sizeof(int));
-  struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level     = SOL_SOCKET;
-  cmsg->cmsg_type      = SCM_RIGHTS;
-  cmsg->cmsg_len       = CMSG_LEN(count*sizeof(int));
-  va_start(ap, len);
-  for (int i = 0, fd; (fd = va_arg(ap, int)) >= 0; ++i) {
-    (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i] = fd;
-  }
-  return sendmsg(transport, &msg, 0) ==
-      static_cast<ssize_t>(sizeof(dummy) + ((buf && len > 0) ? len : 0));
-}
-
-bool Util::getFds(int transport, void *buf, size_t *len, ...) {
-  int count = 0;
-  va_list ap;
-  va_start(ap, len);
-  for (int *fd; (fd = va_arg(ap, int *)) != NULL; ++count) {
-    *fd = -1;
-  }
-  va_end(ap);
-  if (!count) {
-    return false;
-  }
-  char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
-  memset(cmsg_buf, 0, sizeof(cmsg_buf));
-  struct iovec iov[2] = { { 0 } };
-  struct msghdr msg   = { 0 };
-  int err;
-  iov[0].iov_base     = &err;
-  iov[0].iov_len      = sizeof(int);
-  if (buf && len && *len > 0) {
-    iov[1].iov_base   = buf;
-    iov[1].iov_len    = *len;
-  }
-  msg.msg_iov         = iov;
-  msg.msg_iovlen      = (buf && len && *len > 0) ? 2 : 1;
-  msg.msg_control     = cmsg_buf;
-  msg.msg_controllen  = CMSG_LEN(count*sizeof(int));
-  ssize_t bytes = recvmsg(transport, &msg, 0);
-  if (len) {
-    *len = bytes > static_cast<int>(sizeof(int)) ? bytes - sizeof(int) : 0;
-  }
-  if (bytes != static_cast<ssize_t>(sizeof(int) + iov[1].iov_len)) {
-    if (bytes >= 0) {
-      errno = 0;
-    }
-    return false;
-  }
-  if (err) {
-    // "err" is the first four bytes of the payload. If these are non-zero,
-    // the sender on the other side of the socketpair sent us an errno value.
-    // We don't expect to get any file handles in this case.
-    errno = err;
-    return false;
-  }
-  struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
-  if ((msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) ||
-      !cmsg                                    ||
-      cmsg->cmsg_level != SOL_SOCKET           ||
-      cmsg->cmsg_type  != SCM_RIGHTS           ||
-      cmsg->cmsg_len   != CMSG_LEN(count*sizeof(int))) {
-    errno = EBADF;
-    return false;
-  }
-  va_start(ap, len);
-  for (int *fd, i = 0; (fd = va_arg(ap, int *)) != NULL; ++i) {
-    *fd = (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i];
-  }
-  va_end(ap);
-  return true;
-}
-
-void Util::closeAllBut(int fd, ...) {
-  int proc_fd;
-  int fdir;
-  if ((proc_fd = Sandbox::proc_fd()) < 0 ||
-      (fdir = openat(proc_fd, "self/fd", O_RDONLY|O_DIRECTORY)) < 0) {
-    SANDBOX_DIE("Cannot access \"/proc/self/fd\"");
-  }
-  int dev_null = open("/dev/null", O_RDWR);
-  DIR *dir = fdopendir(fdir);
-  struct dirent de, *res;
-  while (!readdir_r(dir, &de, &res) && res) {
-    if (res->d_name[0] < '0') {
-      continue;
-    }
-    int i = atoi(res->d_name);
-    if (i >= 0 && i != dirfd(dir) && i != dev_null) {
-      va_list ap;
-      va_start(ap, fd);
-      for (int f = fd;; f = va_arg(ap, int)) {
-        if (f < 0) {
-          if (i <= 2) {
-            // Never ever close 0..2. If we cannot redirect to /dev/null,
-            // then we are better off leaving the standard descriptors open.
-            if (dev_null >= 0) {
-              if (HANDLE_EINTR(dup2(dev_null, i))) {
-                SANDBOX_DIE("Cannot dup2()");
-              }
-            }
-          } else {
-            if (HANDLE_EINTR(close(i))) { }
-          }
-          break;
-        } else if (i == f) {
-          break;
-        }
-      }
-      va_end(ap);
-    }
-  }
-  closedir(dir);
-  if (dev_null >= 0) {
-    if (HANDLE_EINTR(close(dev_null))) { }
-  }
-  return;
-}
-
-}  // namespace
diff --git a/sandbox/linux/seccomp-bpf/util.h b/sandbox/linux/seccomp-bpf/util.h
deleted file mode 100644
index 3e4d41b..0000000
--- a/sandbox/linux/seccomp-bpf/util.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2012 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_UTIL_H__
-#define SANDBOX_LINUX_SECCOMP_BPF_UTIL_H__
-
-namespace playground2 {
-
-class Util {
- public:
-  static bool sendFds(int transport, const void *buf, size_t len, ...);
-  static bool getFds(int transport, void *buf, size_t *len, ...);
-  static void closeAllBut(int fd, ...);
-};
-
-}  // namespace
-
-#endif  // SANDBOX_LINUX_SECCOMP_BPF_UTIL_H__
diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc
index 40a1aa2..60c0eab 100644
--- a/sandbox/linux/seccomp-bpf/verifier.cc
+++ b/sandbox/linux/seccomp-bpf/verifier.cc
@@ -2,90 +2,218 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string.h>
+
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
 #include "sandbox/linux/seccomp-bpf/verifier.h"
 
 
-namespace playground2 {
+namespace {
 
-bool Verifier::VerifyBPF(const std::vector<struct sock_filter>& program,
-                         const Sandbox::Evaluators& evaluators,
-                         const char **err) {
-  *err = NULL;
-  if (evaluators.size() != 1) {
-    *err = "Not implemented";
-    return false;
+using playground2::ErrorCode;
+using playground2::Sandbox;
+using playground2::Verifier;
+using playground2::arch_seccomp_data;
+
+struct State {
+  State(const std::vector<struct sock_filter>& p,
+        const struct arch_seccomp_data& d) :
+    program(p),
+    data(d),
+    ip(0),
+    accumulator(0),
+    acc_is_valid(false) {
   }
-  Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first;
-  void *aux                                 = evaluators.begin()->second;
-  for (SyscallIterator iter(false); !iter.Done(); ) {
-    uint32_t sysnum = iter.Next();
-    // We ideally want to iterate over the full system call range and values
-    // just above and just below this range. This gives us the full result set
-    // of the "evaluators".
-    // On Intel systems, this can fail in a surprising way, as a cleared bit 30
-    // indicates either i386 or x86-64; and a set bit 30 indicates x32. And
-    // unless we pay attention to setting this bit correctly, an early check in
-    // our BPF program will make us fail with a misleading error code.
-    struct arch_seccomp_data data = { static_cast<int>(sysnum),
-                                      static_cast<uint32_t>(SECCOMP_ARCH) };
-#if defined(__i386__) || defined(__x86_64__)
-#if defined(__x86_64__) && defined(__ILP32__)
-    if (!(sysnum & 0x40000000u)) {
-      continue;
+  const std::vector<struct sock_filter>& program;
+  const struct arch_seccomp_data&        data;
+  unsigned int                           ip;
+  uint32_t                               accumulator;
+  bool                                   acc_is_valid;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(State);
+};
+
+uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code,
+                           const struct arch_seccomp_data& data) {
+  if (code.error_type() == ErrorCode::ET_SIMPLE ||
+      code.error_type() == ErrorCode::ET_TRAP) {
+    return code.err();
+  } else if (code.error_type() == ErrorCode::ET_COND) {
+    if (code.width() == ErrorCode::TP_32BIT &&
+        (data.args[code.argno()] >> 32) &&
+        (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
+        0xFFFFFFFF80000000ull) {
+      return sandbox->Unexpected64bitArgument().err();
     }
-#else
-    if (sysnum & 0x40000000u) {
-      continue;
+    switch (code.op()) {
+    case ErrorCode::OP_EQUAL:
+      return EvaluateErrorCode(sandbox,
+                               (code.width() == ErrorCode::TP_32BIT
+                                ? uint32_t(data.args[code.argno()])
+                                : data.args[code.argno()]) == code.value()
+                               ? *code.passed()
+                               : *code.failed(),
+                               data);
+    case ErrorCode::OP_HAS_ALL_BITS:
+      return EvaluateErrorCode(sandbox,
+                               ((code.width() == ErrorCode::TP_32BIT
+                                 ? uint32_t(data.args[code.argno()])
+                                 : data.args[code.argno()]) & code.value())
+                               == code.value()
+                               ? *code.passed()
+                               : *code.failed(),
+                               data);
+    case ErrorCode::OP_HAS_ANY_BITS:
+      return EvaluateErrorCode(sandbox,
+                               (code.width() == ErrorCode::TP_32BIT
+                                ? uint32_t(data.args[code.argno()])
+                                : data.args[code.argno()]) & code.value()
+                               ? *code.passed()
+                               : *code.failed(),
+                               data);
+    default:
+      return SECCOMP_RET_INVALID;
     }
-#endif
-#endif
-    ErrorCode code = evaluate_syscall(sysnum, aux);
-    uint32_t computed_ret = EvaluateBPF(program, data, err);
+  } else {
+    return SECCOMP_RET_INVALID;
+  }
+}
+
+bool VerifyErrorCode(Sandbox *sandbox,
+                     const std::vector<struct sock_filter>& program,
+                     struct arch_seccomp_data *data,
+                     const ErrorCode& root_code,
+                     const ErrorCode& code,
+                     const char **err) {
+  if (code.error_type() == ErrorCode::ET_SIMPLE ||
+      code.error_type() == ErrorCode::ET_TRAP) {
+    uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
     if (*err) {
       return false;
-    } else if (computed_ret != code.err()) {
+    } else if (computed_ret != EvaluateErrorCode(sandbox, root_code, *data)) {
+      // For efficiency's sake, we'd much rather compare "computed_ret"
+      // against "code.err()". This works most of the time, but it doesn't
+      // always work for nested conditional expressions. The test values
+      // that we generate on the fly to probe expressions can trigger
+      // code flow decisions in multiple nodes of the decision tree, and the
+      // only way to compute the correct error code in that situation is by
+      // calling EvaluateErrorCode().
       *err = "Exit code from BPF program doesn't match";
       return false;
     }
+  } else if (code.error_type() == ErrorCode::ET_COND) {
+    if (code.argno() < 0 || code.argno() >= 6) {
+      *err = "Invalid argument number in error code";
+      return false;
+    }
+    switch (code.op()) {
+    case ErrorCode::OP_EQUAL:
+      // Verify that we can check a 32bit value (or the LSB of a 64bit value)
+      // for equality.
+      data->args[code.argno()] = code.value();
+      if (!VerifyErrorCode(sandbox, program, data, root_code,
+                           *code.passed(), err)) {
+        return false;
+      }
+
+      // Change the value to no longer match and verify that this is detected
+      // as an inequality.
+      data->args[code.argno()] = code.value() ^ 0x55AA55AA;
+      if (!VerifyErrorCode(sandbox, program, data, root_code,
+                           *code.failed(), err)) {
+        return false;
+      }
+
+      // BPF programs can only ever operate on 32bit values. So, we have
+      // generated additional BPF instructions that inspect the MSB. Verify
+      // that they behave as intended.
+      if (code.width() == ErrorCode::TP_32BIT) {
+        if (code.value() >> 32) {
+          SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
+                      "against a 64bit constant; this test is always false.");
+        }
+
+        // If the system call argument was intended to be a 32bit parameter,
+        // verify that it is a fatal error if a 64bit value is ever passed
+        // here.
+        data->args[code.argno()] = 0x100000000ull;
+        if (!VerifyErrorCode(sandbox, program, data, root_code,
+                             sandbox->Unexpected64bitArgument(),
+                             err)) {
+          return false;
+        }
+      } else {
+        // If the system call argument was intended to be a 64bit parameter,
+        // verify that we can handle (in-)equality for the MSB. This is
+        // essentially the same test that we did earlier for the LSB.
+        // We only need to verify the behavior of the inequality test. We
+        // know that the equality test already passed, as unlike the kernel
+        // the Verifier does operate on 64bit quantities.
+        data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
+        if (!VerifyErrorCode(sandbox, program, data, root_code,
+                             *code.failed(), err)) {
+          return false;
+        }
+      }
+      break;
+    case ErrorCode::OP_HAS_ALL_BITS:
+    case ErrorCode::OP_HAS_ANY_BITS:
+      // A comprehensive test of bit values is difficult and potentially rather
+      // time-expensive. We avoid doing so at run-time and instead rely on the
+      // unittest for full testing. The test that we have here covers just the
+      // common cases. We test against the bitmask itself, all zeros and all
+      // ones.
+      {
+        // Testing "any" bits against a zero mask is always false. So, there
+        // are some cases, where we expect tests to take the "failed()" branch
+        // even though this is a test that normally should take "passed()".
+        const ErrorCode& passed =
+          (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) ||
+
+          // On a 32bit system, it is impossible to pass a 64bit value as a
+          // system call argument. So, some additional tests always evaluate
+          // as false.
+          ((code.value() & ~uint64_t(uintptr_t(-1))) &&
+           code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
+          (code.value() && !(code.value() & uintptr_t(-1)) &&
+           code.op() == ErrorCode::OP_HAS_ANY_BITS)
+
+          ? *code.failed() : *code.passed();
+
+        // Similary, testing for "all" bits in a zero mask is always true. So,
+        // some cases pass despite them normally failing.
+        const ErrorCode& failed =
+          !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS
+          ? *code.passed() : *code.failed();
+
+        data->args[code.argno()] = code.value() & uintptr_t(-1);
+        if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
+          return false;
+        }
+        data->args[code.argno()] = uintptr_t(-1);
+        if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
+          return false;
+        }
+        data->args[code.argno()] = 0;
+        if (!VerifyErrorCode(sandbox, program, data, root_code, failed, err)) {
+          return false;
+        }
+      }
+      break;
+    default: // TODO(markus): Need to add support for OP_GREATER
+      *err = "Unsupported operation in conditional error code";
+      return false;
+    }
+  } else {
+    *err = "Attempting to return invalid error code from BPF program";
+    return false;
   }
   return true;
 }
 
-uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
-                               const struct arch_seccomp_data& data,
-                               const char **err) {
-  *err = NULL;
-  if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
-    *err = "Invalid program length";
-    return 0;
-  }
-  for (State state(program, data); !*err; ++state.ip) {
-    if (state.ip >= program.size()) {
-      *err = "Invalid instruction pointer in BPF program";
-      break;
-    }
-    const struct sock_filter& insn = program[state.ip];
-    switch (BPF_CLASS(insn.code)) {
-    case BPF_LD:
-      Ld(&state, insn, err);
-      break;
-    case BPF_JMP:
-      Jmp(&state, insn, err);
-      break;
-    case BPF_RET:
-      return Ret(&state, insn, err);
-    default:
-      *err = "Unexpected instruction in BPF program";
-      break;
-    }
-  }
-  return 0;
-}
-
-void Verifier::Ld(State *state, const struct sock_filter& insn,
-                  const char **err) {
+void Ld(State *state, const struct sock_filter& insn, const char **err) {
   if (BPF_SIZE(insn.code) != BPF_W ||
       BPF_MODE(insn.code) != BPF_ABS) {
     *err = "Invalid BPF_LD instruction";
@@ -104,8 +232,7 @@
   return;
 }
 
-void Verifier::Jmp(State *state, const struct sock_filter& insn,
-                   const char **err) {
+void Jmp(State *state, const struct sock_filter& insn, const char **err) {
   if (BPF_OP(insn.code) == BPF_JA) {
     if (state->ip + insn.k + 1 >= state->program.size() ||
         state->ip + insn.k + 1 <= state->ip) {
@@ -156,8 +283,7 @@
   }
 }
 
-uint32_t Verifier::Ret(State *, const struct sock_filter& insn,
-                       const char **err) {
+uint32_t Ret(State *, const struct sock_filter& insn, const char **err) {
   if (BPF_SRC(insn.code) != BPF_K) {
     *err = "Invalid BPF_RET instruction";
     return 0;
@@ -165,4 +291,159 @@
   return insn.k;
 }
 
+void Alu(State *state, const struct sock_filter& insn, const char **err) {
+  if (BPF_OP(insn.code) == BPF_NEG) {
+    state->accumulator = -state->accumulator;
+    return;
+  } else {
+    if (BPF_SRC(insn.code) != BPF_K) {
+      *err = "Unexpected source operand in arithmetic operation";
+      return;
+    }
+    switch (BPF_OP(insn.code)) {
+    case BPF_ADD:
+      state->accumulator += insn.k;
+      break;
+    case BPF_SUB:
+      state->accumulator -= insn.k;
+      break;
+    case BPF_MUL:
+      state->accumulator *= insn.k;
+      break;
+    case BPF_DIV:
+      if (!insn.k) {
+        *err = "Illegal division by zero";
+        break;
+      }
+      state->accumulator /= insn.k;
+      break;
+    case BPF_MOD:
+      if (!insn.k) {
+        *err = "Illegal division by zero";
+        break;
+      }
+      state->accumulator %= insn.k;
+      break;
+    case BPF_OR:
+      state->accumulator |= insn.k;
+      break;
+    case BPF_XOR:
+      state->accumulator ^= insn.k;
+      break;
+    case BPF_AND:
+      state->accumulator &= insn.k;
+      break;
+    case BPF_LSH:
+      if (insn.k > 32) {
+        *err = "Illegal shift operation";
+        break;
+      }
+      state->accumulator <<= insn.k;
+      break;
+    case BPF_RSH:
+      if (insn.k > 32) {
+        *err = "Illegal shift operation";
+        break;
+      }
+      state->accumulator >>= insn.k;
+      break;
+    default:
+      *err = "Invalid operator in arithmetic operation";
+      break;
+    }
+  }
+}
+
+}  // namespace
+
+namespace playground2 {
+
+bool Verifier::VerifyBPF(Sandbox *sandbox,
+                         const std::vector<struct sock_filter>& program,
+                         const Sandbox::Evaluators& evaluators,
+                         const char **err) {
+  *err = NULL;
+  if (evaluators.size() != 1) {
+    *err = "Not implemented";
+    return false;
+  }
+  Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first;
+  void *aux                                 = evaluators.begin()->second;
+  for (SyscallIterator iter(false); !iter.Done(); ) {
+    uint32_t sysnum = iter.Next();
+    // We ideally want to iterate over the full system call range and values
+    // just above and just below this range. This gives us the full result set
+    // of the "evaluators".
+    // On Intel systems, this can fail in a surprising way, as a cleared bit 30
+    // indicates either i386 or x86-64; and a set bit 30 indicates x32. And
+    // unless we pay attention to setting this bit correctly, an early check in
+    // our BPF program will make us fail with a misleading error code.
+    struct arch_seccomp_data data = { static_cast<int>(sysnum),
+                                      static_cast<uint32_t>(SECCOMP_ARCH) };
+#if defined(__i386__) || defined(__x86_64__)
+#if defined(__x86_64__) && defined(__ILP32__)
+    if (!(sysnum & 0x40000000u)) {
+      continue;
+    }
+#else
+    if (sysnum & 0x40000000u) {
+      continue;
+    }
+#endif
+#endif
+    ErrorCode code = evaluate_syscall(sandbox, sysnum, aux);
+    if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
+                               const struct arch_seccomp_data& data,
+                               const char **err) {
+  *err = NULL;
+  if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
+    *err = "Invalid program length";
+    return 0;
+  }
+  for (State state(program, data); !*err; ++state.ip) {
+    if (state.ip >= program.size()) {
+      *err = "Invalid instruction pointer in BPF program";
+      break;
+    }
+    const struct sock_filter& insn = program[state.ip];
+    switch (BPF_CLASS(insn.code)) {
+    case BPF_LD:
+      Ld(&state, insn, err);
+      break;
+    case BPF_JMP:
+      Jmp(&state, insn, err);
+      break;
+    case BPF_RET: {
+      uint32_t r = Ret(&state, insn, err);
+      switch (r & SECCOMP_RET_ACTION) {
+      case SECCOMP_RET_TRAP:
+      case SECCOMP_RET_ERRNO:
+      case SECCOMP_RET_ALLOW:
+        break;
+      case SECCOMP_RET_KILL:     // We don't ever generate this
+      case SECCOMP_RET_TRACE:    // We don't ever generate this
+      case SECCOMP_RET_INVALID:  // Should never show up in BPF program
+      default:
+        *err = "Unexpected return code found in BPF program";
+        return 0;
+      }
+      return r; }
+    case BPF_ALU:
+      Alu(&state, insn, err);
+      break;
+    default:
+      *err = "Unexpected instruction in BPF program";
+      break;
+    }
+  }
+  return 0;
+}
+
 }  // namespace
diff --git a/sandbox/linux/seccomp-bpf/verifier.h b/sandbox/linux/seccomp-bpf/verifier.h
index 505015e..3e99a08 100644
--- a/sandbox/linux/seccomp-bpf/verifier.h
+++ b/sandbox/linux/seccomp-bpf/verifier.h
@@ -10,8 +10,6 @@
 #include <utility>
 #include <vector>
 
-#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
-
 
 namespace playground2 {
 
@@ -24,7 +22,8 @@
   // set by the "evaluators".
   // Upon success, "err" is set to NULL. Upon failure, it contains a static
   // error message that does not need to be free()'d.
-  static bool VerifyBPF(const std::vector<struct sock_filter>& program,
+  static bool VerifyBPF(Sandbox *sandbox,
+                        const std::vector<struct sock_filter>& program,
                         const Sandbox::Evaluators& evaluators,
                         const char **err);
 
@@ -41,32 +40,6 @@
                               const char **err);
 
  private:
-  struct State {
-    State(const std::vector<struct sock_filter>& p,
-          const struct arch_seccomp_data& d) :
-      program(p),
-      data(d),
-      ip(0),
-      accumulator(0),
-      acc_is_valid(false) {
-    }
-    const std::vector<struct sock_filter>& program;
-    const struct arch_seccomp_data&        data;
-    unsigned int                           ip;
-    uint32_t                               accumulator;
-    bool                                   acc_is_valid;
-
-   private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(State);
-  };
-
-  static void     Ld (State *state, const struct sock_filter& insn,
-                      const char **err);
-  static void     Jmp(State *state, const struct sock_filter& insn,
-                      const char **err);
-  static uint32_t Ret(State *state, const struct sock_filter& insn,
-                      const char **err);
-
   DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier);
 };
 
diff --git a/sandbox/linux/services/android_arm_ucontext.h b/sandbox/linux/services/android_arm_ucontext.h
new file mode 100644
index 0000000..d1446c6
--- /dev/null
+++ b/sandbox/linux/services/android_arm_ucontext.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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_SERVICES_ANDROID_ARM_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+// We also need greg_t for the sandbox, include it in this header as well.
+typedef unsigned long greg_t;
+
+//typedef unsigned long sigset_t;
+typedef struct ucontext {
+  unsigned long   uc_flags;
+  struct ucontext  *uc_link;
+  stack_t     uc_stack;
+  struct sigcontext uc_mcontext;
+  sigset_t    uc_sigmask;
+  /* Allow for uc_sigmask growth.  Glibc uses a 1024-bit sigset_t.  */
+  int     __not_used[32 - (sizeof (sigset_t) / sizeof (int))];
+  /* Last for extensibility.  Eight byte aligned because some
+     coprocessors require eight byte alignment.  */
+  unsigned long   uc_regspace[128] __attribute__((__aligned__(8)));
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif  // __BIONIC_HAVE_UCONTEXT_T
+
+#endif  // SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
diff --git a/sandbox/linux/services/android_i386_ucontext.h b/sandbox/linux/services/android_i386_ucontext.h
new file mode 100644
index 0000000..580ac70
--- /dev/null
+++ b/sandbox/linux/services/android_i386_ucontext.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 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_SERVICES_ANDROID_I386_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_
+
+// We do something compatible with glibc. Hopefully, at some point Android will
+// provide that for us, and __BIONIC_HAVE_UCONTEXT_T should be defined.
+// This is mostly copied from breakpad (common/android/include/sys/ucontext.h),
+// except we do use sigset_t for uc_sigmask instead of a custom type.
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+/* 80-bit floating-point register */
+struct _libc_fpreg {
+  unsigned short significand[4];
+  unsigned short exponent;
+};
+
+/* Simple floating-point state, see FNSTENV instruction */
+struct _libc_fpstate {
+  unsigned long cw;
+  unsigned long sw;
+  unsigned long tag;
+  unsigned long ipoff;
+  unsigned long cssel;
+  unsigned long dataoff;
+  unsigned long datasel;
+  struct _libc_fpreg _st[8];
+  unsigned long status;
+};
+
+typedef uint32_t greg_t;
+
+typedef struct {
+  uint32_t gregs[19];
+  struct _libc_fpstate* fpregs;
+  uint32_t oldmask;
+  uint32_t cr2;
+} mcontext_t;
+
+enum {
+  REG_GS = 0,
+  REG_FS,
+  REG_ES,
+  REG_DS,
+  REG_EDI,
+  REG_ESI,
+  REG_EBP,
+  REG_ESP,
+  REG_EBX,
+  REG_EDX,
+  REG_ECX,
+  REG_EAX,
+  REG_TRAPNO,
+  REG_ERR,
+  REG_EIP,
+  REG_CS,
+  REG_EFL,
+  REG_UESP,
+  REG_SS,
+};
+
+typedef struct ucontext {
+  uint32_t uc_flags;
+  struct ucontext* uc_link;
+  stack_t uc_stack;
+  mcontext_t uc_mcontext;
+  sigset_t uc_sigmask;
+  struct _libc_fpstate __fpregs_mem;
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif  // __BIONIC_HAVE_UCONTEXT_T
+
+#endif  // SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_
diff --git a/sandbox/linux/services/android_ucontext.h b/sandbox/linux/services/android_ucontext.h
new file mode 100644
index 0000000..437bbab
--- /dev/null
+++ b/sandbox/linux/services/android_ucontext.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2013 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_SERVICES_ANDROID_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
+
+#if defined(__ANDROID__)
+
+#if defined(__arm__)
+#include "sandbox/linux/services/android_arm_ucontext.h"
+#elif defined(__i386__)
+#include "sandbox/linux/services/android_i386_ucontext.h"
+#else
+#error "No support for your architecture in Android header"
+#endif
+
+#else  // __ANDROID__
+#error "Android header file included on non Android."
+#endif  // __ANDROID__
+
+#endif  // SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
diff --git a/sandbox/linux/services/broker_process.cc b/sandbox/linux/services/broker_process.cc
new file mode 100644
index 0000000..ea71b8f
--- /dev/null
+++ b/sandbox/linux/services/broker_process.cc
@@ -0,0 +1,358 @@
+// Copyright (c) 2012 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/services/broker_process.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+
+namespace {
+
+static const int kCommandOpen = 'O';
+static const size_t kMaxMessageLength = 4096;
+
+// Some flags will need special treatment on the client side and are not
+// supported for now.
+int ForCurrentProcessFlagsMask() {
+  return O_CLOEXEC | O_NONBLOCK;
+}
+
+// Check whether |requested_filename| is in |allowed_file_names|.
+// See GetFileNameIfAllowedAccess() for an explaination of |file_to_open|.
+// async signal safe if |file_to_open| is NULL.
+// TODO(jln): assert signal safety.
+bool GetFileNameInWhitelist(const std::vector<std::string>& allowed_file_names,
+                            const std::string& requested_filename,
+                            const char** file_to_open) {
+  if (file_to_open && *file_to_open) {
+    // Make sure that callers never pass a non-empty string. In case callers
+    // wrongly forget to check the return value and look at the string
+    // instead, this could catch bugs.
+    RAW_LOG(FATAL, "*file_to_open should be NULL");
+    return false;
+  }
+  std::vector<std::string>::const_iterator it;
+  it = std::find(allowed_file_names.begin(), allowed_file_names.end(),
+                 requested_filename);
+  if (it < allowed_file_names.end()) {  // requested_filename was found?
+    if (file_to_open)
+      *file_to_open = it->c_str();
+    return true;
+  }
+  return false;
+}
+
+// We maintain a list of flags that have been reviewed for "sanity" and that
+// we're ok to allow in the broker.
+// I.e. here is where we wouldn't add O_RESET_FILE_SYSTEM.
+bool IsAllowedOpenFlags(int flags) {
+  // First, check the access mode
+  const int access_mode = flags & O_ACCMODE;
+  if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
+      access_mode != O_RDWR) {
+    return false;
+  }
+
+  // We only support a 2-parameters open, so we forbid O_CREAT.
+  if (flags & O_CREAT) {
+    return false;
+  }
+
+  // Some flags affect the behavior of the current process. We don't support
+  // them and don't allow them for now.
+  if (flags & ForCurrentProcessFlagsMask()) {
+    return false;
+  }
+
+  // Now check that all the flags are known to us.
+  const int creation_and_status_flags = flags & ~O_ACCMODE;
+
+  const int known_flags =
+    O_APPEND | O_ASYNC | O_CLOEXEC | O_CREAT | O_DIRECT |
+    O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME | O_NOCTTY |
+    O_NOFOLLOW | O_NONBLOCK | O_NDELAY | O_SYNC | O_TRUNC;
+
+  const int unknown_flags = ~known_flags;
+  const bool has_unknown_flags = creation_and_status_flags & unknown_flags;
+  return !has_unknown_flags;
+}
+
+}  // namespace
+
+namespace sandbox {
+
+BrokerProcess::BrokerProcess(const std::vector<std::string>& allowed_r_files,
+                             const std::vector<std::string>& allowed_w_files,
+                             bool fast_check_in_client,
+                             bool quiet_failures_for_tests)
+    : initialized_(false),
+      is_child_(false),
+      fast_check_in_client_(fast_check_in_client),
+      quiet_failures_for_tests_(quiet_failures_for_tests),
+      broker_pid_(-1),
+      allowed_r_files_(allowed_r_files),
+      allowed_w_files_(allowed_w_files),
+      ipc_socketpair_(-1) {
+}
+
+BrokerProcess::~BrokerProcess() {
+  if (initialized_ && ipc_socketpair_ != -1) {
+    void (HANDLE_EINTR(close(ipc_socketpair_)));
+  }
+}
+
+bool BrokerProcess::Init(bool (*sandbox_callback)(void)) {
+  CHECK(!initialized_);
+  int socket_pair[2];
+  // Use SOCK_SEQPACKET, because we need to preserve message boundaries
+  // but we also want to be notified (recvmsg should return and not block)
+  // when the connection has been broken (one of the processes died).
+  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair)) {
+    LOG(ERROR) << "Failed to create socketpair";
+    return false;
+  }
+
+  int child_pid = fork();
+  if (child_pid == -1) {
+    (void) HANDLE_EINTR(close(socket_pair[0]));
+    (void) HANDLE_EINTR(close(socket_pair[1]));
+    return false;
+  }
+  if (child_pid) {
+    // We are the parent and we have just forked our broker process.
+    (void) HANDLE_EINTR(close(socket_pair[0]));
+    // We should only be able to write to the IPC channel. We'll always send
+    // a new file descriptor to receive the reply on.
+    shutdown(socket_pair[1], SHUT_RD);
+    ipc_socketpair_ = socket_pair[1];
+    is_child_ = false;
+    broker_pid_ = child_pid;
+    initialized_ = true;
+    return true;
+  } else {
+    // We are the broker.
+    (void) HANDLE_EINTR(close(socket_pair[1]));
+    // We should only be able to read from this IPC channel. We will send our
+    // replies on a new file descriptor attached to the requests.
+    shutdown(socket_pair[0], SHUT_WR);
+    ipc_socketpair_ = socket_pair[0];
+    is_child_ = true;
+    // Enable the sandbox if provided.
+    if (sandbox_callback) {
+      CHECK(sandbox_callback());
+    }
+    initialized_ = true;
+    for (;;) {
+      HandleRequest();
+    }
+    _exit(1);
+  }
+  NOTREACHED();
+}
+
+// This function needs to be async signal safe.
+int BrokerProcess::Open(const char* pathname, int flags) const {
+  RAW_CHECK(initialized_);  // async signal safe CHECK().
+  if (!pathname)
+    return -EFAULT;
+  // There is no point in forwarding a request that we know will be denied.
+  // Of course, the real security check needs to be on the other side of the
+  // IPC.
+  if (fast_check_in_client_) {
+    if (!GetFileNameIfAllowedAccess(pathname, flags, NULL))
+      return -EPERM;
+  }
+
+  Pickle write_pickle;
+  write_pickle.WriteInt(kCommandOpen);
+  write_pickle.WriteString(pathname);
+  write_pickle.WriteInt(flags);
+  RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+
+  int returned_fd = -1;
+  uint8_t reply_buf[kMaxMessageLength];
+  // Send a request (in write_pickle) as well that will include a new
+  // temporary socketpair (created internally by SendRecvMsg()).
+  // Then read the reply on this new socketpair in reply_buf and put an
+  // eventual attached file descriptor in |returned_fd|.
+  // TODO(jln): this API needs some rewriting and documentation.
+  ssize_t msg_len = UnixDomainSocket::SendRecvMsg(ipc_socketpair_,
+                                                  reply_buf,
+                                                  sizeof(reply_buf),
+                                                  &returned_fd,
+                                                  write_pickle);
+  if (msg_len <= 0) {
+    if (!quiet_failures_for_tests_)
+      RAW_LOG(ERROR, "Could not make request to broker process");
+    return -ENOMEM;
+  }
+
+  Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+  PickleIterator iter(read_pickle);
+  int return_value = -1;
+  // Now deserialize the return value and eventually return the file
+  // descriptor.
+  if (read_pickle.ReadInt(&iter, &return_value)) {
+    if (return_value < 0) {
+      RAW_CHECK(returned_fd == -1);
+      return return_value;
+    } else {
+      // We have a real file descriptor to return.
+      RAW_CHECK(returned_fd >= 0);
+      return returned_fd;
+    }
+  } else {
+    RAW_LOG(ERROR, "Could not read pickle");
+    return -1;
+  }
+}
+
+// Handle a request on the IPC channel ipc_socketpair_.
+// A request should have a file descriptor attached on which we will reply and
+// that we will then close.
+// A request should start with an int that will be used as the command type.
+bool BrokerProcess::HandleRequest() const {
+
+  std::vector<int> fds;
+  char buf[kMaxMessageLength];
+  errno = 0;
+  const ssize_t msg_len = UnixDomainSocket::RecvMsg(ipc_socketpair_, buf,
+                                                    sizeof(buf), &fds);
+
+  if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
+    // EOF from our parent, or our parent died, we should die.
+    _exit(0);
+  }
+
+  // The parent should send exactly one file descriptor, on which we
+  // will write the reply.
+  if (msg_len < 0 || fds.size() != 1 || fds.at(0) < 0) {
+    PLOG(ERROR) << "Error reading message from the client";
+    return false;
+  }
+
+  const int temporary_ipc = fds.at(0);
+
+  Pickle pickle(buf, msg_len);
+  PickleIterator iter(pickle);
+  int command_type;
+  if (pickle.ReadInt(&iter, &command_type)) {
+    bool r = false;
+    // Go through all the possible IPC messages.
+    switch (command_type) {
+      case kCommandOpen:
+        // We reply on the file descriptor sent to us via the IPC channel.
+        r = HandleOpenRequest(temporary_ipc, pickle, iter);
+        (void) HANDLE_EINTR(close(temporary_ipc));
+        return r;
+      default:
+        NOTREACHED();
+        return false;
+    }
+  }
+
+  LOG(ERROR) << "Error parsing IPC request";
+  return false;
+}
+
+// Handle an open request contained in |read_pickle| and send the reply
+// on |reply_ipc|.
+bool BrokerProcess::HandleOpenRequest(int reply_ipc,
+                                      const Pickle& read_pickle,
+                                      PickleIterator iter) const {
+  std::string requested_filename;
+  int flags = 0;
+  if (!read_pickle.ReadString(&iter, &requested_filename) ||
+      !read_pickle.ReadInt(&iter, &flags)) {
+    return -1;
+  }
+
+  Pickle write_pickle;
+  std::vector<int> opened_files;
+
+  const char* file_to_open = NULL;
+  const bool safe_to_open_file = GetFileNameIfAllowedAccess(
+      requested_filename.c_str(), flags, &file_to_open);
+
+  if (safe_to_open_file) {
+    CHECK(file_to_open);
+    // O_CLOEXEC doesn't hurt (even though we won't execve()), and this
+    // property won't be passed to the client.
+    // We may want to think about O_NONBLOCK as well.
+    // We're doing a 2-parameter open, so we don't support O_CREAT. It doesn't
+    // hurt to always pass a third argument though.
+    int opened_fd = open(file_to_open, flags | O_CLOEXEC, 0);
+    if (opened_fd < 0) {
+      write_pickle.WriteInt(-errno);
+    } else {
+      // Success.
+      opened_files.push_back(opened_fd);
+      write_pickle.WriteInt(0);
+    }
+  } else {
+    write_pickle.WriteInt(-EPERM);
+  }
+
+  CHECK_LE(write_pickle.size(), kMaxMessageLength);
+  ssize_t sent = UnixDomainSocket::SendMsg(reply_ipc, write_pickle.data(),
+                                           write_pickle.size(), opened_files);
+
+  // Close anything we have opened in this process.
+  for (std::vector<int>::iterator it = opened_files.begin();
+       it < opened_files.end(); ++it) {
+    (void) HANDLE_EINTR(close(*it));
+  }
+
+  if (sent <= 0) {
+    LOG(ERROR) << "Could not send IPC reply";
+    return false;
+  }
+  return true;
+}
+
+// For paranoia, if |file_to_open| is not NULL, we will return the matching
+// string from the white list.
+// Async signal safe only if |file_to_open| is NULL.
+// Even if an attacker managed to fool the string comparison mechanism, we
+// would not open an attacker-controlled file name.
+// Return true if access should be allowed, false otherwise.
+bool BrokerProcess::GetFileNameIfAllowedAccess(const char* requested_filename,
+    int requested_flags, const char** file_to_open) const {
+  if (!IsAllowedOpenFlags(requested_flags)) {
+    return false;
+  }
+  switch (requested_flags & O_ACCMODE) {
+    case O_RDONLY:
+      return GetFileNameInWhitelist(allowed_r_files_, requested_filename,
+                                    file_to_open);
+    case O_WRONLY:
+      return GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+                                    file_to_open);
+    case O_RDWR:
+    {
+      bool allowed_for_read_and_write =
+          GetFileNameInWhitelist(allowed_r_files_, requested_filename, NULL) &&
+          GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+                                 file_to_open);
+      return allowed_for_read_and_write;
+    }
+    default:
+      return false;
+  }
+}
+
+}  // namespace sandbox.
diff --git a/sandbox/linux/services/broker_process.h b/sandbox/linux/services/broker_process.h
new file mode 100644
index 0000000..d04f703
--- /dev/null
+++ b/sandbox/linux/services/broker_process.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 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_SERVICES_BROKER_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/pickle.h"
+#include "base/process.h"
+
+namespace sandbox {
+
+// Create a new "broker" process to which we can send requests via an IPC
+// channel.
+// This is a low level IPC mechanism that is suitable to be called from a
+// signal handler.
+// A process would typically create a broker process before entering
+// sandboxing.
+// 1. BrokerProcess open_broker(read_whitelist, write_whitelist);
+// 2. CHECK(open_broker.Init(NULL));
+// 3. Enable sandbox.
+// 4. Use open_broker.Open() to open files.
+class BrokerProcess {
+ public:
+  // |allowed_file_names| is a white list of files that can be opened later via
+  // the Open() API.
+  // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for
+  // unit tests, don't use it.
+  explicit BrokerProcess(const std::vector<std::string>& allowed_r_files_,
+                         const std::vector<std::string>& allowed_w_files_,
+                         bool fast_check_in_client = true,
+                         bool quiet_failures_for_tests = false);
+  ~BrokerProcess();
+  // Will initialize the broker process. There should be no threads at this
+  // point, since we need to fork().
+  // sandbox_callback is a function that should be called to enable the
+  // sandbox in the broker.
+  bool Init(bool (*sandbox_callback)(void));
+
+  // Can be used in place of open(). Will be async signal safe.
+  // The implementation only supports certain white listed flags and will
+  // return -EPERM on other flags.
+  // It's similar to the open() system call and will return -errno on errors.
+  int Open(const char* pathname, int flags) const;
+
+  int broker_pid() const { return broker_pid_; }
+
+ private:
+  bool HandleRequest() const;
+  bool HandleOpenRequest(int reply_ipc, const Pickle& read_pickle,
+                         PickleIterator iter) const;
+  bool GetFileNameIfAllowedAccess(const char*, int, const char**) const;
+  bool initialized_;  // Whether we've been through Init() yet.
+  bool is_child_;  // Whether we're the child (broker process).
+  bool fast_check_in_client_;  // Whether to forward a request that we know
+                               // will be denied to the broker.
+  bool quiet_failures_for_tests_;  // Disable certain error message when
+                                   // testing for failures.
+  pid_t broker_pid_;  // The PID of the broker (child).
+  const std::vector<std::string> allowed_r_files_;  // Files allowed for read.
+  const std::vector<std::string> allowed_w_files_;  // Files allowed for write.
+  int ipc_socketpair_;  // Our communication channel to parent or child.
+  DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerProcess);
+};
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
diff --git a/sandbox/linux/services/broker_process_unittest.cc b/sandbox/linux/services/broker_process_unittest.cc
new file mode 100644
index 0000000..ab57035
--- /dev/null
+++ b/sandbox/linux/services/broker_process_unittest.cc
@@ -0,0 +1,311 @@
+// Copyright (c) 2012 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/services/broker_process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string>
+#include <vector>
+
+#if defined(OS_ANDROID)
+#include "base/android/path_utils.h"
+#endif
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+#if defined(OS_ANDROID)
+  #define DISABLE_ON_ANDROID(function) DISABLED_##function
+#else
+  #define DISABLE_ON_ANDROID(function) function
+#endif
+
+TEST(BrokerProcess, CreateAndDestroy) {
+  std::vector<std::string> read_whitelist;
+  read_whitelist.push_back("/proc/cpuinfo");
+
+  BrokerProcess* open_broker = new BrokerProcess(read_whitelist,
+                                                 std::vector<std::string>());
+  ASSERT_TRUE(open_broker->Init(NULL));
+  pid_t broker_pid = open_broker->broker_pid();
+  delete(open_broker);
+
+  // Now we check that the broker has exited properly.
+  int status = 0;
+  ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid);
+  ASSERT_TRUE(WIFEXITED(status));
+  ASSERT_EQ(WEXITSTATUS(status), 0);
+}
+
+TEST(BrokerProcess, TestOpenNull) {
+  const std::vector<std::string> empty;
+  BrokerProcess open_broker(empty, empty);
+  ASSERT_TRUE(open_broker.Init(NULL));
+
+  int fd = open_broker.Open(NULL, O_RDONLY);
+  ASSERT_EQ(fd, -EFAULT);
+}
+
+void TestOpenFilePerms(bool fast_check_in_client) {
+  const char kR_WhiteListed[] = "/proc/DOESNOTEXIST1";
+  const char kW_WhiteListed[] = "/proc/DOESNOTEXIST2";
+  const char kRW_WhiteListed[] = "/proc/DOESNOTEXIST3";
+  const char k_NotWhitelisted[] = "/proc/DOESNOTEXIST4";
+
+  std::vector<std::string> read_whitelist;
+  read_whitelist.push_back(kR_WhiteListed);
+  read_whitelist.push_back(kRW_WhiteListed);
+
+  std::vector<std::string> write_whitelist;
+  write_whitelist.push_back(kW_WhiteListed);
+  write_whitelist.push_back(kRW_WhiteListed);
+
+  BrokerProcess open_broker(read_whitelist,
+                            write_whitelist,
+                            fast_check_in_client);
+  ASSERT_TRUE(open_broker.Init(NULL));
+
+  int fd = -1;
+  fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
+  ASSERT_EQ(fd, -ENOENT);
+  fd = open_broker.Open(kR_WhiteListed, O_WRONLY);
+  ASSERT_EQ(fd, -EPERM);
+  fd = open_broker.Open(kR_WhiteListed, O_RDWR);
+  ASSERT_EQ(fd, -EPERM);
+
+  fd = open_broker.Open(kW_WhiteListed, O_RDONLY);
+  ASSERT_EQ(fd, -EPERM);
+  fd = open_broker.Open(kW_WhiteListed, O_WRONLY);
+  ASSERT_EQ(fd, -ENOENT);
+  fd = open_broker.Open(kW_WhiteListed, O_RDWR);
+  ASSERT_EQ(fd, -EPERM);
+
+  fd = open_broker.Open(kRW_WhiteListed, O_RDONLY);
+  ASSERT_EQ(fd, -ENOENT);
+  fd = open_broker.Open(kRW_WhiteListed, O_WRONLY);
+  ASSERT_EQ(fd, -ENOENT);
+  fd = open_broker.Open(kRW_WhiteListed, O_RDWR);
+  ASSERT_EQ(fd, -ENOENT);
+
+  fd = open_broker.Open(k_NotWhitelisted, O_RDONLY);
+  ASSERT_EQ(fd, -EPERM);
+  fd = open_broker.Open(k_NotWhitelisted, O_WRONLY);
+  ASSERT_EQ(fd, -EPERM);
+  fd = open_broker.Open(k_NotWhitelisted, O_RDWR);
+  ASSERT_EQ(fd, -EPERM);
+
+  // We have some extra sanity check for clearly wrong values.
+  fd = open_broker.Open(kRW_WhiteListed, O_RDONLY|O_WRONLY|O_RDWR);
+  ASSERT_EQ(fd, -EPERM);
+
+  // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
+  // is denied.
+  fd = open_broker.Open(kRW_WhiteListed, O_RDWR|O_CREAT);
+  ASSERT_EQ(fd, -EPERM);
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenFilePermsWithClientCheck) {
+  TestOpenFilePerms(true /* fast_check_in_client */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheck) {
+  TestOpenFilePerms(false /* fast_check_in_client */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+
+void TestOpenCpuinfo(bool fast_check_in_client) {
+  const char kFileCpuInfo[] = "/proc/cpuinfo";
+  std::vector<std::string> read_whitelist;
+  read_whitelist.push_back(kFileCpuInfo);
+
+  BrokerProcess* open_broker = new BrokerProcess(read_whitelist,
+                                                 std::vector<std::string>(),
+                                                 fast_check_in_client);
+  ASSERT_TRUE(open_broker->Init(NULL));
+  pid_t broker_pid = open_broker->broker_pid();
+
+  int fd = -1;
+  fd = open_broker->Open(kFileCpuInfo, O_RDWR);
+  ASSERT_EQ(fd, -EPERM);
+
+  // Open cpuinfo via the broker.
+  int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+  ASSERT_GE(cpuinfo_fd, 0);
+  char buf[3];
+  memset(buf, 0, sizeof(buf));
+  int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
+  ASSERT_GT(read_len1, 0);
+
+  // Open cpuinfo directly.
+  int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
+  ASSERT_GE(cpuinfo_fd2, 0);
+  char buf2[3];
+  memset(buf2, 1, sizeof(buf2));
+  int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
+  ASSERT_GT(read_len1, 0);
+
+  // The following is not guaranteed true, but will be in practice.
+  ASSERT_EQ(read_len1, read_len2);
+  // Compare the cpuinfo as returned by the broker with the one we opened
+  // ourselves.
+  ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
+
+  if (fd >= 0)
+    close(fd);
+  if (cpuinfo_fd >= 0)
+    close(cpuinfo_fd);
+  if (cpuinfo_fd2 >= 0)
+    close(cpuinfo_fd);
+
+  delete(open_broker);
+
+  // Now we check that the broker has exited properly.
+  int status = 0;
+  ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid);
+  ASSERT_TRUE(WIFEXITED(status));
+  ASSERT_EQ(WEXITSTATUS(status), 0);
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenCpuinfoWithClientCheck) {
+  TestOpenCpuinfo(true /* fast_check_in_client */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheck) {
+  TestOpenCpuinfo(false /* fast_check_in_client */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+// Disabled until we implement a mkstemp that doesn't require JNI.
+TEST(BrokerProcess, DISABLE_ON_ANDROID(OpenFileRW)) {
+  const char basename[] = "BrokerProcessXXXXXX";
+  char template_name[2048];
+#if defined(OS_ANDROID)
+  base::FilePath cache_directory;
+  ASSERT_TRUE(base::android::GetCacheDirectory(&cache_directory));
+  ssize_t length = snprintf(template_name, sizeof(template_name),
+                            "%s%s",
+                            cache_directory.value().c_str(), basename);
+  ASSERT_LT(length, static_cast<ssize_t>(sizeof(template_name)));
+#else
+  strncpy(template_name, basename, sizeof(basename) - 1);
+  template_name[sizeof(basename) - 1] = '\0';
+#endif
+  int tempfile = mkstemp(template_name);
+  ASSERT_GE(tempfile, 0);
+  char tempfile_name[2048];
+  int written = snprintf(tempfile_name, sizeof(tempfile_name),
+                         "/proc/self/fd/%d", tempfile);
+  ASSERT_LT(written, static_cast<int>(sizeof(tempfile_name)));
+
+  std::vector<std::string> whitelist;
+  whitelist.push_back(tempfile_name);
+
+  BrokerProcess open_broker(whitelist, whitelist);
+  ASSERT_TRUE(open_broker.Init(NULL));
+
+  int tempfile2 = -1;
+  tempfile2 = open_broker.Open(tempfile_name, O_RDWR);
+  ASSERT_GE(tempfile2, 0);
+
+  // Write to the descriptor opened by the broker.
+  char test_text[] = "TESTTESTTEST";
+  ssize_t len = write(tempfile2, test_text, sizeof(test_text));
+  ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+
+  // Read back from the original file descriptor what we wrote through
+  // the descriptor provided by the broker.
+  char buf[1024];
+  len = read(tempfile, buf, sizeof(buf));
+
+  ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+  ASSERT_EQ(memcmp(test_text, buf, sizeof(test_text)), 0);
+
+  // Cleanup the temporary file.
+  char tempfile_full_path[2048];
+  // Make sure tempfile_full_path will terminate with a 0.
+  memset(tempfile_full_path, 0, sizeof(tempfile_full_path));
+  ssize_t ret = readlink(tempfile_name, tempfile_full_path,
+                         sizeof(tempfile_full_path));
+  ASSERT_GT(ret, 0);
+  // Make sure we still have a trailing zero in tempfile_full_path.
+  ASSERT_LT(ret, static_cast<ssize_t>(sizeof(tempfile_full_path)));
+  ASSERT_EQ(unlink(tempfile_full_path), 0);
+
+  ASSERT_EQ(close(tempfile), 0);
+  ASSERT_EQ(close(tempfile2), 0);
+}
+
+// Sandbox test because we could get a SIGPIPE.
+SANDBOX_TEST(BrokerProcess, BrokerDied) {
+  std::vector<std::string> read_whitelist;
+  read_whitelist.push_back("/proc/cpuinfo");
+
+  BrokerProcess open_broker(read_whitelist,
+                            std::vector<std::string>(),
+                            true /* fast_check_in_client */,
+                            true /* quiet_failures_for_tests */);
+  SANDBOX_ASSERT(open_broker.Init(NULL));
+  pid_t broker_pid = open_broker.broker_pid();
+  SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
+
+  // Now we check that the broker has exited properly.
+  int status = 0;
+  SANDBOX_ASSERT(waitpid(broker_pid, &status, 0) == broker_pid);
+  SANDBOX_ASSERT(WIFSIGNALED(status));
+  SANDBOX_ASSERT(WTERMSIG(status) == SIGKILL);
+  // Hopefully doing Open with a dead broker won't SIGPIPE us.
+  SANDBOX_ASSERT(open_broker.Open("/proc/cpuinfo", O_RDONLY) == -ENOMEM);
+}
+
+void TestComplexFlags(bool fast_check_in_client) {
+  std::vector<std::string> whitelist;
+  whitelist.push_back("/proc/cpuinfo");
+
+  BrokerProcess open_broker(whitelist,
+                            whitelist,
+                            fast_check_in_client);
+  ASSERT_TRUE(open_broker.Init(NULL));
+  // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
+  // Presently, the right thing is to always deny them since they are not
+  // supported.
+  int fd = -1;
+  fd = open_broker.Open("/proc/cpuinfo", O_RDONLY);
+  ASSERT_GE(fd, 0);
+  ASSERT_EQ(close(fd), 0);
+
+  ASSERT_EQ(open_broker.Open("/proc/cpuinfo", O_RDONLY | O_CLOEXEC), -EPERM);
+  ASSERT_EQ(open_broker.Open("/proc/cpuinfo", O_RDONLY | O_NONBLOCK), -EPERM);
+}
+
+TEST(BrokerProcess, ComplexFlagsWithClientCheck) {
+  TestComplexFlags(true /* fast_check_in_client */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerProcess, ComplexFlagsNoClientCheck) {
+  TestComplexFlags(false /* fast_check_in_client */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+}  // namespace sandbox
diff --git a/sandbox/linux/services/libc_urandom_override.cc b/sandbox/linux/services/libc_urandom_override.cc
index ee34045..3f99572 100644
--- a/sandbox/linux/services/libc_urandom_override.cc
+++ b/sandbox/linux/services/libc_urandom_override.cc
@@ -10,8 +10,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "base/eintr_wrapper.h"
 #include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
 #include "base/rand_util.h"
 
 // Note: this file is used by the zygote and nacl_helper.
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc
index 45d700b..7a174ef 100644
--- a/sandbox/linux/suid/client/setuid_sandbox_client.cc
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -6,10 +6,10 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include "base/eintr_wrapper.h"
 #include "base/environment.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
 #include "base/string_number_conversions.h"
 
 #include "sandbox/linux/suid/common/sandbox.h"
diff --git a/sandbox/linux/suid/linux_util.c b/sandbox/linux/suid/linux_util.c
index c5af0d0..256468f 100644
--- a/sandbox/linux/suid/linux_util.c
+++ b/sandbox/linux/suid/linux_util.c
@@ -5,9 +5,12 @@
 // The following is duplicated from base/linux_utils.cc.
 // We shouldn't link against C++ code in a setuid binary.
 
+#define _GNU_SOURCE  // For O_DIRECTORY
 #include "linux_util.h"
 
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -23,29 +26,42 @@
 // socket.
 //   inode_out: (output) set to the inode number on success
 //   path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
-static bool ProcPathGetInode(ino_t* inode_out, const char* path) {
+static bool ProcPathGetInodeAt(ino_t* inode_out, int base_dir_fd,
+                               const char* path) {
+  // We also check that the path is relative.
+  if (!inode_out || !path || *path == '/')
+    return false;
   char buf[256];
-  const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
-  if (n == -1)
+  const ssize_t n = readlinkat(base_dir_fd, path, buf, sizeof(buf) - 1);
+  if (n < 0)
     return false;
   buf[n] = 0;
 
   if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1))
     return false;
 
-  char *endptr;
-  const unsigned long long int inode_ul =
+  char *endptr = NULL;
+  errno = 0;
+  const unsigned long long int inode_ull =
       strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
-  if (*endptr != ']')
+  if (inode_ull == ULLONG_MAX || !endptr || *endptr != ']' || errno != 0)
     return false;
 
-  if (inode_ul == ULLONG_MAX)
-    return false;
-
-  *inode_out = inode_ul;
+  *inode_out = inode_ull;
   return true;
 }
 
+static DIR* opendirat(int base_dir_fd, const char* name) {
+  // Also check that |name| is relative.
+  if (base_dir_fd < 0 || !name || *name == '/')
+    return NULL;
+  int new_dir_fd = openat(base_dir_fd, name, O_RDONLY | O_DIRECTORY);
+  if (new_dir_fd < 0)
+    return NULL;
+
+  return fdopendir(new_dir_fd);
+}
+
 bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
   bool already_found = false;
 
@@ -56,9 +72,10 @@
   const uid_t uid = getuid();
   struct dirent* dent;
   while ((dent = readdir(proc))) {
-    char *endptr;
+    char *endptr = NULL;
+    errno = 0;
     const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
-    if (pid_ul == ULONG_MAX || *endptr)
+    if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
       continue;
 
     // We have this setuid code here because the zygote and its children have
@@ -66,34 +83,39 @@
     // extra check so users cannot accidentally gain information about other
     // users' processes. To determine process ownership, we use the property
     // that if user foo owns process N, then /proc/N is owned by foo.
+    int proc_pid_fd = -1;
     {
       char buf[256];
       struct stat statbuf;
       snprintf(buf, sizeof(buf), "/proc/%lu", pid_ul);
-      if (stat(buf, &statbuf) < 0)
+      proc_pid_fd = open(buf, O_RDONLY | O_DIRECTORY);
+      if (proc_pid_fd < 0)
         continue;
-      if (uid != statbuf.st_uid)
+      if (fstat(proc_pid_fd, &statbuf) < 0 || uid != statbuf.st_uid) {
+        close(proc_pid_fd);
         continue;
+      }
     }
 
-    char buf[256];
-    snprintf(buf, sizeof(buf), "/proc/%lu/fd", pid_ul);
-    DIR* fd = opendir(buf);
-    if (!fd)
+    DIR* fd = opendirat(proc_pid_fd, "fd");
+    if (!fd) {
+      close(proc_pid_fd);
       continue;
+    }
 
     while ((dent = readdir(fd))) {
-      int printed = snprintf(buf, sizeof(buf), "/proc/%lu/fd/%s", pid_ul,
-                             dent->d_name);
+      char buf[256];
+      int printed = snprintf(buf, sizeof(buf), "fd/%s", dent->d_name);
       if (printed < 0 || printed >= (int)(sizeof(buf) - 1)) {
         continue;
       }
 
       ino_t fd_inode;
-      if (ProcPathGetInode(&fd_inode, buf)) {
+      if (ProcPathGetInodeAt(&fd_inode, proc_pid_fd, buf)) {
         if (fd_inode == socket_inode) {
           if (already_found) {
             closedir(fd);
+            close(proc_pid_fd);
             closedir(proc);
             return false;
           }
@@ -105,6 +127,7 @@
       }
     }
     closedir(fd);
+    close(proc_pid_fd);
   }
   closedir(proc);
 
diff --git a/sandbox/linux/tests/main.cc b/sandbox/linux/tests/main.cc
index 4412645..8142545 100644
--- a/sandbox/linux/tests/main.cc
+++ b/sandbox/linux/tests/main.cc
@@ -4,7 +4,6 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-
 int main(int argc, char *argv[]) {
   testing::InitGoogleTest(&argc, argv);
   // Always go through re-execution for death tests.
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
index 105c45b..3d049ab 100644
--- a/sandbox/linux/tests/unit_tests.cc
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -2,24 +2,98 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
 #include <stdio.h>
 #include <sys/resource.h>
 #include <sys/time.h>
+#include <unistd.h>
 
 #include "base/file_util.h"
+#include "base/third_party/valgrind/valgrind.h"
 #include "sandbox/linux/tests/unit_tests.h"
 
+namespace {
+std::string TestFailedMessage(const std::string& msg) {
+  return msg.empty() ? "" : "Actual test failure: " + msg;
+}
+
+int GetSubProcessTimeoutTimeInSeconds() {
+  // 10s ought to be enough for anybody.
+  return 10;
+}
+
+// Returns the number of threads of the current process or -1.
+int CountThreads() {
+  struct stat task_stat;
+  int task_d = stat("/proc/self/task", &task_stat);
+  // task_stat.st_nlink should be the number of tasks + 2 (accounting for
+  // "." and "..".
+  if (task_d != 0 || task_stat.st_nlink < 3)
+    return -1;
+  return task_stat.st_nlink - 2;
+}
+
+}  // namespace
+
 namespace sandbox {
 
 static const int kExpectedValue = 42;
+static const int kIgnoreThisTest = 43;
+static const int kExitWithAssertionFailure = 1;
+static const int kExitForTimeout = 2;
 
-void UnitTests::RunTestInProcess(UnitTests::Test test, void *arg) {
-  // Runs a test in a sub-process. This is necessary for most of the code
-  // 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.
+static void SigAlrmHandler(int) {
+    const char failure_message[] = "Timeout reached!\n";
+    // Make sure that we never block here.
+    if (!fcntl(2, F_SETFL,  O_NONBLOCK)) {
+      if (write(2, failure_message, sizeof(failure_message) - 1) < 0) {
+      }
+    }
+    _exit(kExitForTimeout);
+}
+
+// Set a timeout with a handler that will automatically fail the
+// test.
+static void SetProcessTimeout(int time_in_seconds) {
+  struct sigaction act = {};
+  act.sa_handler = SigAlrmHandler;
+  SANDBOX_ASSERT(sigemptyset(&act.sa_mask) == 0);
+  act.sa_flags = 0;
+
+  struct sigaction old_act;
+  SANDBOX_ASSERT(sigaction(SIGALRM, &act, &old_act) == 0);
+
+  // We don't implemenet signal chaining, so make sure that nothing else
+  // is expecting to handle SIGALRM.
+  SANDBOX_ASSERT((old_act.sa_flags & SA_SIGINFO) == 0);
+  SANDBOX_ASSERT(old_act.sa_handler == SIG_DFL);
+  sigset_t sigalrm_set;
+  SANDBOX_ASSERT(sigemptyset(&sigalrm_set) == 0);
+  SANDBOX_ASSERT(sigaddset(&sigalrm_set, SIGALRM) == 0);
+  SANDBOX_ASSERT(sigprocmask(SIG_UNBLOCK, &sigalrm_set, NULL) == 0);
+  SANDBOX_ASSERT(alarm(time_in_seconds) == 0);  // There should be no previous
+                                                // alarm.
+}
+
+// Runs a test in a sub-process. This is necessary for most of the code
+// 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,
+                                 DeathCheck death, const void *death_aux) {
+  if (CountThreads() != 1) {
+    // We need to use fork(), so we can't be multi-threaded.
+    // TODO(jln): change this to a fatal error once we can launch
+    // Android tests with --exe.
+    fprintf(stderr, "WARNING: running sandbox tests with multiple threads"
+                    " is not supported and will make the tests flaky.\n");
+  }
   int fds[2];
   ASSERT_EQ(0, pipe(fds));
+  // Check that our pipe is not on one of the standard file descriptor.
+  SANDBOX_ASSERT(fds[0] > 2 && fds[1] > 2);
 
   pid_t pid;
   ASSERT_LE(0, (pid = fork()));
@@ -31,6 +105,12 @@
     SANDBOX_ASSERT(!close(fds[0]));
     SANDBOX_ASSERT(!close(fds[1]));
 
+    // Don't set a timeout if running on Valgrind, since it's generally much
+    // slower.
+    if (!RUNNING_ON_VALGRIND) {
+      SetProcessTimeout(GetSubProcessTimeoutTimeInSeconds());
+    }
+
     // Disable core files. They are not very useful for our individual test
     // cases.
     struct rlimit no_core = { 0 };
@@ -41,24 +121,56 @@
   }
 
   (void)HANDLE_EINTR(close(fds[1]));
-  std::vector<char> msg;
+  std::vector<char> msg_buf;
   ssize_t rc;
-  do {
-    const unsigned int kCapacity = 256;
-    size_t len = msg.size();
-    msg.resize(len + kCapacity);
-    rc = HANDLE_EINTR(read(fds[0], &msg[len], kCapacity));
-    msg.resize(len + std::max(rc, static_cast<ssize_t>(0)));
-  } while (rc > 0);
-  std::string details;
-  if (!msg.empty()) {
-    details = "Actual test failure: " + std::string(msg.begin(), msg.end());
+
+  // Make sure read() will never block as we'll use poll() to
+  // block with a timeout instead.
+  const int fcntl_ret = fcntl(fds[0], F_SETFL,  O_NONBLOCK);
+  ASSERT_EQ(fcntl_ret, 0);
+  struct pollfd poll_fd = { fds[0], POLLIN | POLLRDHUP, 0 };
+
+  int poll_ret;
+  // We prefer the SIGALRM timeout to trigger in the child than this timeout
+  // so we double the common value here.
+  int poll_timeout = GetSubProcessTimeoutTimeInSeconds() * 2 * 1000;
+  while ((poll_ret = poll(&poll_fd, 1, poll_timeout) > 0)) {
+    const size_t kCapacity = 256;
+    const size_t len = msg_buf.size();
+    msg_buf.resize(len + kCapacity);
+    rc = HANDLE_EINTR(read(fds[0], &msg_buf[len], kCapacity));
+    msg_buf.resize(len + std::max(rc, static_cast<ssize_t>(0)));
+    if (rc <= 0)
+      break;
   }
+  ASSERT_NE(poll_ret, -1) << "poll() failed";
+  ASSERT_NE(poll_ret, 0) << "Timeout while reading child state";
   (void)HANDLE_EINTR(close(fds[0]));
+  std::string msg(msg_buf.begin(), msg_buf.end());
 
   int status = 0;
   int waitpid_returned = HANDLE_EINTR(waitpid(pid, &status, 0));
-  ASSERT_EQ(pid, waitpid_returned) << details;
+  ASSERT_EQ(pid, waitpid_returned) << TestFailedMessage(msg);
+
+  // At run-time, we sometimes decide that a test shouldn't actually
+  // run (e.g. when testing sandbox features on a kernel that doesn't
+  // have sandboxing support). When that happens, don't attempt to
+  // call the "death" function, as it might be looking for a
+  // death-test condition that would never have triggered.
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != kIgnoreThisTest ||
+      !msg.empty()) {
+    // We use gtest's ASSERT_XXX() macros instead of the DeathCheck
+    // functions.  This means, on failure, "return" is called. This
+    // only works correctly, if the call of the "death" callback is
+    // the very last thing in our function.
+    death(status, msg, death_aux);
+  }
+}
+
+void UnitTests::DeathSuccess(int status, const std::string& msg,
+                             const void *) {
+  std::string details(TestFailedMessage(msg));
+
   bool subprocess_terminated_normally = WIFEXITED(status);
   ASSERT_TRUE(subprocess_terminated_normally) << details;
   int subprocess_exit_status = WEXITSTATUS(status);
@@ -67,11 +179,52 @@
   EXPECT_FALSE(subprocess_exited_but_printed_messages) << details;
 }
 
+void UnitTests::DeathMessage(int status, const std::string& msg,
+                             const void *aux) {
+  std::string details(TestFailedMessage(msg));
+  const char *expected_msg = static_cast<const char *>(aux);
+
+  bool subprocess_terminated_normally = WIFEXITED(status);
+  ASSERT_TRUE(subprocess_terminated_normally) << details;
+  int subprocess_exit_status = WEXITSTATUS(status);
+  ASSERT_EQ(kExitWithAssertionFailure, subprocess_exit_status) << details;
+  bool subprocess_exited_without_matching_message =
+    msg.find(expected_msg) == std::string::npos;
+  EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
+}
+
+void UnitTests::DeathExitCode(int status, const std::string& msg,
+                              const void *aux) {
+  int expected_exit_code = static_cast<int>(reinterpret_cast<intptr_t>(aux));
+  std::string details(TestFailedMessage(msg));
+
+  bool subprocess_terminated_normally = WIFEXITED(status);
+  ASSERT_TRUE(subprocess_terminated_normally) << details;
+  int subprocess_exit_status = WEXITSTATUS(status);
+  ASSERT_EQ(subprocess_exit_status, expected_exit_code) << details;
+}
+
+void UnitTests::DeathBySignal(int status, const std::string& msg,
+                              const void *aux) {
+  int expected_signo = static_cast<int>(reinterpret_cast<intptr_t>(aux));
+  std::string details(TestFailedMessage(msg));
+
+  bool subprocess_terminated_by_signal = WIFSIGNALED(status);
+  ASSERT_TRUE(subprocess_terminated_by_signal) << details;
+  int subprocess_signal_number = WTERMSIG(status);
+  ASSERT_EQ(subprocess_signal_number, expected_signo) << details;
+}
+
 void UnitTests::AssertionFailure(const char *expr, const char *file,
                                  int line) {
   fprintf(stderr, "%s:%d:%s", file, line, expr);
   fflush(stderr);
-  _exit(1);
+  _exit(kExitWithAssertionFailure);
+}
+
+void UnitTests::IgnoreThisTest() {
+  fflush(stderr);
+  _exit(kIgnoreThisTest);
 }
 
 }  // namespace
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h
index d6b4761..78bf9bc 100644
--- a/sandbox/linux/tests/unit_tests.h
+++ b/sandbox/linux/tests/unit_tests.h
@@ -10,17 +10,41 @@
 
 namespace sandbox {
 
-// Define a new test case that runs inside of a death test. This is necessary,
-// as most of our tests by definition make global and irreversible changes to
-// the system (i.e. they install a sandbox). GTest provides death tests as a
-// tool to isolate global changes from the rest of the tests.
-#define SANDBOX_TEST(test_case_name, test_name)                               \
+// While it is perfectly OK for a complex test to provide its own DeathCheck
+// function. Most death tests have very simple requirements. These tests should
+// use one of the predefined DEATH_XXX macros as an argument to
+// SANDBOX_DEATH_TEST(). You can check for a (sub-)string in the output of the
+// test, for a particular exit code, or for a particular death signal.
+// NOTE: If you do decide to write your own DeathCheck, make sure to use
+//       gtests's ASSERT_XXX() macros instead of SANDBOX_ASSERT(). See
+//       unit_tests.cc for examples.
+#define DEATH_SUCCESS()     sandbox::UnitTests::DeathSuccess, NULL
+#define DEATH_MESSAGE(msg)  sandbox::UnitTests::DeathMessage,                 \
+                            static_cast<const void *>(                        \
+                                static_cast<const char *>(msg))
+#define DEATH_EXIT_CODE(rc) sandbox::UnitTests::DeathExitCode,                \
+                            reinterpret_cast<void *>(static_cast<intptr_t>(rc))
+#define DEATH_BY_SIGNAL(s)  sandbox::UnitTests::DeathExitCode,                \
+                            reinterpret_cast<void *>(static_cast<intptr_t>(s))
+
+// A SANDBOX_DEATH_TEST is just like a SANDBOX_TEST (see below), but it assumes
+// 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);             \
+    sandbox::UnitTests::RunTestInProcess(TEST_##test_name, NULL, 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
+// changes to the system (i.e. they install a sandbox). GTest provides death
+// tests as a tool to isolate global changes from the rest of the tests.
+#define SANDBOX_TEST(test_case_name, test_name)                               \
+  SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS())
+
 // Simple assertion macro that is compatible with running inside of a death
 // test. We unfortunately cannot use any of the GTest macros.
 #define SANDBOX_STR(x) #x
@@ -33,18 +57,56 @@
 class UnitTests {
  public:
   typedef void (*Test)(void *);
+  typedef void (*DeathCheck)(int status, const std::string& msg,
+                             const void *aux);
 
   // Runs a test inside a short-lived process. Do not call this function
   // 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);
+  static void RunTestInProcess(Test test, void *arg, DeathCheck death,
+                               const void *death_aux);
 
   // Report a useful error message and terminate the current SANDBOX_TEST().
   // Calling this function from outside a SANDBOX_TEST() is unlikely to do
   // anything useful.
   static void AssertionFailure(const char *expr, const char *file, int line);
 
+  // Sometimes we determine at run-time that a test should be disabled.
+  // Call this method if we want to return from a test and completely
+  // ignore its results.
+  // You should not call this method, if the test already ran any test-relevant
+  // code. Most notably, you should not call it, you already wrote any messages
+  // to stderr.
+  static void IgnoreThisTest();
+
+  // A DeathCheck method that verifies that the test completed succcessfully.
+  // This is the default test mode for SANDBOX_TEST(). The "aux" parameter
+  // of this DeathCheck is unused (and thus unnamed)
+  static void DeathSuccess(int status, const std::string& msg, const void *);
+
+  // A DeathCheck method that verifies that the test completed with error
+  // code "1" and printed a message containing a particular substring. The
+  // "aux" pointer should point to a C-string containing the expected error
+  // message. This method is useful for checking assertion failures such as
+  // in SANDBOX_ASSERT() and/or SANDBOX_DIE().
+  static void DeathMessage(int status, const std::string& msg,
+                           const void *aux);
+
+  // A DeathCheck method that verifies that the test completed with a
+  // particular exit code. If the test output any messages to stderr, they are
+  // silently ignored. The expected exit code should be passed in by
+  // casting the its "int" value to a "void *", which is then used for "aux".
+  static void DeathExitCode(int status, const std::string& msg,
+                            const void *aux);
+
+  // A DeathCheck method that verifies that the test was terminated by a
+  // particular signal. If the test output any messages to stderr, they are
+  // silently ignore. The expected signal number should be passed in by
+  // casting the its "int" value to a "void *", which is then used for "aux".
+  static void DeathBySignal(int status, const std::string& msg,
+                            const void *aux);
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(UnitTests);
 };