
--- a/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc
+++ b/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc
@@ -27,6 +27,11 @@ ResultExpr SandboxBPFBasePolicyAndroid::
     case __NR_clone:
     case __NR_epoll_pwait:
     case __NR_flock:
+#if defined(__x86_64__) || defined(__aarch64__)
+    case __NR_newfstatat:
+#elif defined(__i386__) || defined(__arm__) || defined(__mips__)
+    case __NR_fstatat64:
+#endif
     case __NR_getpriority:
     case __NR_ioctl:
     case __NR_mremap:
@@ -35,13 +40,14 @@ ResultExpr SandboxBPFBasePolicyAndroid::
     // access. It may be possible to restrict the filesystem with SELinux.
     // Currently we rely on the app/service UID isolation to create a
     // filesystem "sandbox".
-#if !ARCH_CPU_ARM64
+#if !defined(ARCH_CPU_ARM64)
     case __NR_open:
 #endif
     case __NR_openat:
     case __NR_pread64:
     case __NR_rt_sigtimedwait:
     case __NR_setpriority:
+    case __NR_set_tid_address:
     case __NR_sigaltstack:
 #if defined(__i386__) || defined(__arm__)
     case __NR_ugetrlimit:
--- a/content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.cc
+++ b/content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.cc
@@ -25,13 +25,15 @@
 #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
 
-using sandbox::SyscallSets;
 using sandbox::bpf_dsl::Allow;
 using sandbox::bpf_dsl::Arg;
 using sandbox::bpf_dsl::Error;
 using sandbox::bpf_dsl::If;
 using sandbox::bpf_dsl::ResultExpr;
+using sandbox::syscall_broker::BrokerFilePermission;
+using sandbox::SyscallSets;
 
 namespace content {
 
@@ -46,38 +48,25 @@ inline bool IsChromeOS() {
 }
 
 inline bool IsArchitectureArm() {
-#if defined(__arm__)
+#if defined(__arm__) || defined(__aarch64__)
   return true;
 #else
   return false;
 #endif
 }
 
-void AddArmMaliGpuWhitelist(std::vector<std::string>* read_whitelist,
-                            std::vector<std::string>* write_whitelist) {
+void AddArmMaliGpuWhitelist(std::vector<BrokerFilePermission>* permissions) {
   // Device file needed by the ARM GPU userspace.
   static const char kMali0Path[] = "/dev/mali0";
 
-  // Devices needed for video decode acceleration on ARM.
-  static const char kDevMfcDecPath[] = "/dev/mfc-dec";
+  // Video processor used on ARM Exynos platforms.
   static const char kDevGsc1Path[] = "/dev/gsc1";
 
-  // Devices needed for video encode acceleration on ARM.
-  static const char kDevMfcEncPath[] = "/dev/mfc-enc";
-
-  read_whitelist->push_back(kMali0Path);
-  read_whitelist->push_back(kDevMfcDecPath);
-  read_whitelist->push_back(kDevGsc1Path);
-  read_whitelist->push_back(kDevMfcEncPath);
-
-  write_whitelist->push_back(kMali0Path);
-  write_whitelist->push_back(kDevMfcDecPath);
-  write_whitelist->push_back(kDevGsc1Path);
-  write_whitelist->push_back(kDevMfcEncPath);
+  permissions->push_back(BrokerFilePermission::ReadWrite(kMali0Path));
+  permissions->push_back(BrokerFilePermission::ReadWrite(kDevGsc1Path));
 }
 
-void AddArmGpuWhitelist(std::vector<std::string>* read_whitelist,
-                        std::vector<std::string>* write_whitelist) {
+void AddArmGpuWhitelist(std::vector<BrokerFilePermission>* permissions) {
   // On ARM we're enabling the sandbox before the X connection is made,
   // so we need to allow access to |.Xauthority|.
   static const char kXAuthorityPath[] = "/home/chronos/.Xauthority";
@@ -87,12 +76,12 @@ void AddArmGpuWhitelist(std::vector<std:
   static const char kLibGlesPath[] = "/usr/lib/libGLESv2.so.2";
   static const char kLibEglPath[] = "/usr/lib/libEGL.so.1";
 
-  read_whitelist->push_back(kXAuthorityPath);
-  read_whitelist->push_back(kLdSoCache);
-  read_whitelist->push_back(kLibGlesPath);
-  read_whitelist->push_back(kLibEglPath);
+  permissions->push_back(BrokerFilePermission::ReadOnly(kXAuthorityPath));
+  permissions->push_back(BrokerFilePermission::ReadOnly(kLdSoCache));
+  permissions->push_back(BrokerFilePermission::ReadOnly(kLibGlesPath));
+  permissions->push_back(BrokerFilePermission::ReadOnly(kLibEglPath));
 
-  AddArmMaliGpuWhitelist(read_whitelist, write_whitelist);
+  AddArmMaliGpuWhitelist(permissions);
 }
 
 class CrosArmGpuBrokerProcessPolicy : public CrosArmGpuProcessPolicy {
@@ -113,8 +102,11 @@ class CrosArmGpuBrokerProcessPolicy : pu
 // openat allowed.
 ResultExpr CrosArmGpuBrokerProcessPolicy::EvaluateSyscall(int sysno) const {
   switch (sysno) {
+#if !defined(__aarch64__)
     case __NR_access:
     case __NR_open:
+#endif  // !defined(__aarch64__)
+    case __NR_faccessat:
     case __NR_openat:
       return Allow();
     default:
@@ -130,13 +122,13 @@ CrosArmGpuProcessPolicy::CrosArmGpuProce
 CrosArmGpuProcessPolicy::~CrosArmGpuProcessPolicy() {}
 
 ResultExpr CrosArmGpuProcessPolicy::EvaluateSyscall(int sysno) const {
-#if defined(__arm__)
+#if defined(__arm__) || defined(__aarch64__)
   if (allow_shmat_ && sysno == __NR_shmat)
     return Allow();
-#endif  // defined(__arm__)
+#endif  // defined(__arm__) || defined(__aarch64__)
 
   switch (sysno) {
-#if defined(__arm__)
+#if defined(__arm__) || defined(__aarch64__)
     // ARM GPU sandbox is started earlier so we need to allow networking
     // in the sandbox.
     case __NR_connect:
@@ -151,7 +143,7 @@ ResultExpr CrosArmGpuProcessPolicy::Eval
       const Arg<int> domain(0);
       return If(domain == AF_UNIX, Allow()).Else(Error(EPERM));
     }
-#endif  // defined(__arm__)
+#endif  // defined(__arm__) || defined(__aarch64__)
     default:
       // Default to the generic GPU policy.
       return GpuProcessPolicy::EvaluateSyscall(sysno);
@@ -163,14 +155,11 @@ bool CrosArmGpuProcessPolicy::PreSandbox
   // Create a new broker process.
   DCHECK(!broker_process());
 
-  std::vector<std::string> read_whitelist_extra;
-  std::vector<std::string> write_whitelist_extra;
   // Add ARM-specific files to whitelist in the broker.
+  std::vector<BrokerFilePermission> permissions;
 
-  AddArmGpuWhitelist(&read_whitelist_extra, &write_whitelist_extra);
-  InitGpuBrokerProcess(CrosArmGpuBrokerProcessPolicy::Create,
-                       read_whitelist_extra,
-                       write_whitelist_extra);
+  AddArmGpuWhitelist(&permissions);
+  InitGpuBrokerProcess(CrosArmGpuBrokerProcessPolicy::Create, permissions);
 
   const int dlopen_flag = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE;
 
--- a/content/common/sandbox_linux/bpf_gpu_policy_linux.cc
+++ b/content/common/sandbox_linux/bpf_gpu_policy_linux.cc
@@ -29,14 +29,16 @@
 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
 #include "sandbox/linux/syscall_broker/broker_process.h"
 
-using sandbox::BrokerProcess;
-using sandbox::SyscallSets;
 using sandbox::arch_seccomp_data;
 using sandbox::bpf_dsl::Allow;
 using sandbox::bpf_dsl::ResultExpr;
 using sandbox::bpf_dsl::Trap;
+using sandbox::syscall_broker::BrokerFilePermission;
+using sandbox::syscall_broker::BrokerProcess;
+using sandbox::SyscallSets;
 
 namespace content {
 
@@ -67,23 +69,44 @@ inline bool IsArchitectureI386() {
 }
 
 inline bool IsArchitectureArm() {
-#if defined(__arm__)
+#if defined(__arm__) || defined(__aarch64__)
   return true;
 #else
   return false;
 #endif
 }
 
-bool IsAcceleratedVideoEnabled() {
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
+inline bool IsOzone() {
+#if defined(USE_OZONE)
+  return true;
+#else
+  return false;
+#endif
+}
+
+inline bool UseLibV4L2() {
+#if defined(USE_LIBV4L2)
+  return true;
+#else
+  return false;
+#endif
+}
+
+bool IsAcceleratedVaapiVideoEncodeEnabled() {
   bool accelerated_encode_enabled = false;
 #if defined(OS_CHROMEOS)
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
   accelerated_encode_enabled =
       !command_line.HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode);
 #endif
-  return !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode) ||
-         accelerated_encode_enabled;
+  return accelerated_encode_enabled;
+}
+
+bool IsAcceleratedVideoDecodeEnabled() {
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  return !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode);
 }
 
 intptr_t GpuSIGSYS_Handler(const struct arch_seccomp_data& args,
@@ -92,6 +115,7 @@ intptr_t GpuSIGSYS_Handler(const struct
   BrokerProcess* broker_process =
       static_cast<BrokerProcess*>(aux_broker_process);
   switch (args.nr) {
+#if !defined(__aarch64__)
     case __NR_access:
       return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
                                     static_cast<int>(args.args[1]));
@@ -102,6 +126,15 @@ intptr_t GpuSIGSYS_Handler(const struct
 #endif
       return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
                                   static_cast<int>(args.args[1]));
+#endif  // !defined(__aarch64__)
+    case __NR_faccessat:
+      if (static_cast<int>(args.args[0]) == AT_FDCWD) {
+        return
+            broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
+                                    static_cast<int>(args.args[2]));
+      } else {
+        return -EPERM;
+      }
     case __NR_openat:
       // Allow using openat() as open().
       if (static_cast<int>(args.args[0]) == AT_FDCWD) {
@@ -117,6 +150,18 @@ intptr_t GpuSIGSYS_Handler(const struct
   }
 }
 
+void AddV4L2GpuWhitelist(std::vector<BrokerFilePermission>* permissions) {
+  if (IsAcceleratedVideoDecodeEnabled()) {
+    // Device node for V4L2 video decode accelerator drivers.
+    static const char kDevVideoDecPath[] = "/dev/video-dec";
+    permissions->push_back(BrokerFilePermission::ReadWrite(kDevVideoDecPath));
+  }
+
+  // Device node for V4L2 video encode accelerator drivers.
+  static const char kDevVideoEncPath[] = "/dev/video-enc";
+  permissions->push_back(BrokerFilePermission::ReadWrite(kDevVideoEncPath));
+}
+
 class GpuBrokerProcessPolicy : public GpuProcessPolicy {
  public:
   static sandbox::bpf_dsl::Policy* Create() {
@@ -132,13 +177,21 @@ class GpuBrokerProcessPolicy : public Gp
 };
 
 // x86_64/i386 or desktop ARM.
-// A GPU broker policy is the same as a GPU policy with open and
-// openat allowed.
+// A GPU broker policy is the same as a GPU policy with access, open,
+// openat and in the non-Chrome OS case unlink allowed.
 ResultExpr GpuBrokerProcessPolicy::EvaluateSyscall(int sysno) const {
   switch (sysno) {
+#if !defined(__aarch64__)
     case __NR_access:
     case __NR_open:
+#endif  // !defined(__aarch64__)
+    case __NR_faccessat:
     case __NR_openat:
+#if !defined(OS_CHROMEOS)
+    // The broker process needs to able to unlink the temporary
+    // files that it may create. This is used by DRI3.
+    case __NR_unlink:
+#endif
       return Allow();
     default:
       return GpuProcessPolicy::EvaluateSyscall(sysno);
@@ -165,7 +218,7 @@ bool UpdateProcessTypeAndEnableSandbox(
   DCHECK(broker_sandboxer_allocator);
   UpdateProcessTypeToGpuBroker();
   return SandboxSeccompBPF::StartSandboxWithExternalPolicy(
-      make_scoped_ptr(broker_sandboxer_allocator()));
+      make_scoped_ptr(broker_sandboxer_allocator()), base::ScopedFD());
 }
 
 }  // namespace
@@ -182,6 +235,9 @@ GpuProcessPolicy::~GpuProcessPolicy() {}
 // Main policy for x86_64/i386. Extended by CrosArmGpuProcessPolicy.
 ResultExpr GpuProcessPolicy::EvaluateSyscall(int sysno) const {
   switch (sysno) {
+#if !defined(OS_CHROMEOS)
+    case __NR_ftruncate:
+#endif
     case __NR_ioctl:
       return Allow();
     case __NR_mincore:
@@ -201,13 +257,14 @@ ResultExpr GpuProcessPolicy::EvaluateSys
     // TODO(jln): restrict prctl.
     case __NR_prctl:
       return Allow();
+#if !defined(__aarch64__)
     case __NR_access:
     case __NR_open:
+#endif  // !defined(__aarch64__)
+    case __NR_faccessat:
     case __NR_openat:
       DCHECK(broker_process_);
       return Trap(GpuSIGSYS_Handler, broker_process_);
-    case __NR_setpriority:
-      return sandbox::RestrictGetSetpriority(GetPolicyPid());
     case __NR_sched_getaffinity:
     case __NR_sched_setaffinity:
       return sandbox::RestrictSchedTarget(GetPolicyPid(), sysno);
@@ -231,13 +288,13 @@ bool GpuProcessPolicy::PreSandboxHook()
   // Create a new broker process.
   InitGpuBrokerProcess(
       GpuBrokerProcessPolicy::Create,
-      std::vector<std::string>(),  // No extra files in whitelist.
-      std::vector<std::string>());
+      std::vector<BrokerFilePermission>());  // No extra files in whitelist.
 
   if (IsArchitectureX86_64() || IsArchitectureI386()) {
     // Accelerated video dlopen()'s some shared objects
     // inside the sandbox, so preload them now.
-    if (IsAcceleratedVideoEnabled()) {
+    if (IsAcceleratedVaapiVideoEncodeEnabled() ||
+        IsAcceleratedVideoDecodeEnabled()) {
       const char* I965DrvVideoPath = NULL;
 
       if (IsArchitectureX86_64()) {
@@ -248,7 +305,11 @@ bool GpuProcessPolicy::PreSandboxHook()
 
       dlopen(I965DrvVideoPath, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
       dlopen("libva.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
+#if defined(USE_OZONE)
+      dlopen("libva-drm.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
+#elif defined(USE_X11)
       dlopen("libva-x11.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
+#endif
     }
   }
 
@@ -257,32 +318,36 @@ bool GpuProcessPolicy::PreSandboxHook()
 
 void GpuProcessPolicy::InitGpuBrokerProcess(
     sandbox::bpf_dsl::Policy* (*broker_sandboxer_allocator)(void),
-    const std::vector<std::string>& read_whitelist_extra,
-    const std::vector<std::string>& write_whitelist_extra) {
+    const std::vector<BrokerFilePermission>& permissions_extra) {
   static const char kDriRcPath[] = "/etc/drirc";
   static const char kDriCard0Path[] = "/dev/dri/card0";
+  static const char kDevShm[] = "/dev/shm/";
 
   CHECK(broker_process_ == NULL);
 
   // All GPU process policies need these files brokered out.
-  std::vector<std::string> read_whitelist;
-  read_whitelist.push_back(kDriCard0Path);
-  read_whitelist.push_back(kDriRcPath);
-  // Add eventual extra files from read_whitelist_extra.
-  read_whitelist.insert(read_whitelist.end(),
-                        read_whitelist_extra.begin(),
-                        read_whitelist_extra.end());
-
-  std::vector<std::string> write_whitelist;
-  write_whitelist.push_back(kDriCard0Path);
-  // Add eventual extra files from write_whitelist_extra.
-  write_whitelist.insert(write_whitelist.end(),
-                         write_whitelist_extra.begin(),
-                         write_whitelist_extra.end());
-
-  broker_process_ = new BrokerProcess(GetFSDeniedErrno(),
-                                      read_whitelist,
-                                      write_whitelist);
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadWrite(kDriCard0Path));
+  permissions.push_back(BrokerFilePermission::ReadOnly(kDriRcPath));
+  if (!IsChromeOS()) {
+    permissions.push_back(
+        BrokerFilePermission::ReadWriteCreateUnlinkRecursive(kDevShm));
+  } else if (IsArchitectureArm() || IsOzone()){
+    AddV4L2GpuWhitelist(&permissions);
+    if (UseLibV4L2()) {
+      dlopen("/usr/lib/libv4l2.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
+      // This is a device-specific encoder plugin.
+      dlopen("/usr/lib/libv4l/plugins/libv4l-encplugin.so",
+          RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
+    }
+  }
+
+  // Add eventual extra files from permissions_extra.
+  for (const auto& perm : permissions_extra) {
+    permissions.push_back(perm);
+  }
+
+  broker_process_ = new BrokerProcess(GetFSDeniedErrno(), permissions);
   // The initialization callback will perform generic initialization and then
   // call broker_sandboxer_callback.
   CHECK(broker_process_->Init(base::Bind(&UpdateProcessTypeAndEnableSandbox,
--- a/content/common/sandbox_linux/bpf_gpu_policy_linux.h
+++ b/content/common/sandbox_linux/bpf_gpu_policy_linux.h
@@ -12,8 +12,11 @@
 #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h"
 
 namespace sandbox {
+namespace syscall_broker {
+class BrokerFilePermission;
 class BrokerProcess;
 }
+}
 
 namespace content {
 
@@ -32,15 +35,17 @@ class GpuProcessPolicy : public SandboxB
   // Start a broker process to handle open() inside the sandbox.
   // |broker_sandboxer_allocator| is a function pointer which can allocate a
   // suitable sandbox policy for the broker process itself.
-  // |read_whitelist_extra| and |write_whitelist_extra| are lists of file
-  // names that should be whitelisted by the broker process, in addition to
+  // |permissions_extra| is a list of file permissions
+  // that should be whitelisted by the broker process, in addition to
   // the basic ones.
   void InitGpuBrokerProcess(
       sandbox::bpf_dsl::Policy* (*broker_sandboxer_allocator)(void),
-      const std::vector<std::string>& read_whitelist_extra,
-      const std::vector<std::string>& write_whitelist_extra);
+      const std::vector<sandbox::syscall_broker::BrokerFilePermission>&
+          permissions_extra);
 
-  sandbox::BrokerProcess* broker_process() { return broker_process_; }
+  sandbox::syscall_broker::BrokerProcess* broker_process() {
+    return broker_process_;
+  }
 
  private:
   // A BrokerProcess is a helper that is started before the sandbox is engaged
@@ -50,7 +55,7 @@ class GpuProcessPolicy : public SandboxB
   // vital to the process.
   // This is allocated by InitGpuBrokerProcess, called from PreSandboxHook(),
   // which executes iff the sandbox is going to be enabled afterwards.
-  sandbox::BrokerProcess* broker_process_;
+  sandbox::syscall_broker::BrokerProcess* broker_process_;
 
   // eglCreateWindowSurface() needs mincore().
   bool allow_mincore_;
--- a/content/common/sandbox_linux/sandbox_init_linux.cc
+++ b/content/common/sandbox_linux/sandbox_init_linux.cc
@@ -4,14 +4,17 @@
 
 #include "content/public/common/sandbox_init.h"
 
+#include "base/files/scoped_file.h"
 #include "base/memory/scoped_ptr.h"
 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
 #include "sandbox/linux/bpf_dsl/policy.h"
 
 namespace content {
 
-bool InitializeSandbox(scoped_ptr<sandbox::bpf_dsl::Policy> policy) {
-  return SandboxSeccompBPF::StartSandboxWithExternalPolicy(policy.Pass());
+bool InitializeSandbox(scoped_ptr<sandbox::bpf_dsl::Policy> policy,
+                       base::ScopedFD proc_task_fd) {
+  return SandboxSeccompBPF::StartSandboxWithExternalPolicy(policy.Pass(),
+                                                           proc_task_fd.Pass());
 }
 
 scoped_ptr<sandbox::bpf_dsl::Policy> GetBPFSandboxBaselinePolicy() {
--- a/content/common/sandbox_linux/sandbox_linux.cc
+++ b/content/common/sandbox_linux/sandbox_linux.cc
@@ -11,6 +11,8 @@
 #include <unistd.h>
 
 #include <limits>
+#include <string>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -30,7 +32,7 @@
 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/sandbox_linux.h"
-#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/proc_util.h"
 #include "sandbox/linux/services/thread_helpers.h"
 #include "sandbox/linux/services/yama.h"
 #include "sandbox/linux/suid/client/setuid_sandbox_client.h"
@@ -90,10 +92,12 @@ int OpenProcTaskFd(int proc_fd) {
   if (proc_fd >= 0) {
     // If a handle to /proc is available, use it. This allows to bypass file
     // system restrictions.
-    proc_self_task = openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY);
+    proc_self_task =
+        openat(proc_fd, "self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
   } else {
     // Otherwise, make an attempt to access the file system directly.
-    proc_self_task = open("/proc/self/task/", O_RDONLY | O_DIRECTORY);
+    proc_self_task =
+        open("/proc/self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
   }
   return proc_self_task;
 }
@@ -108,7 +112,9 @@ LinuxSandbox::LinuxSandbox()
       sandbox_status_flags_(kSandboxLinuxInvalid),
       pre_initialized_(false),
       seccomp_bpf_supported_(false),
+      seccomp_bpf_with_tsync_supported_(false),
       yama_is_enforcing_(false),
+      initialize_sandbox_ran_(false),
       setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create())
 {
   if (setuid_sandbox_client_ == NULL) {
@@ -121,6 +127,9 @@ LinuxSandbox::LinuxSandbox()
 }
 
 LinuxSandbox::~LinuxSandbox() {
+  if (pre_initialized_) {
+    CHECK(initialize_sandbox_ran_);
+  }
 }
 
 LinuxSandbox* LinuxSandbox::GetInstance() {
@@ -144,12 +153,14 @@ void LinuxSandbox::PreinitializeSandbox(
   // its contents before the sandbox is enabled.  It also pre-opens the
   // object files that are already loaded in the process address space.
   base::debug::EnableInProcessStackDumpingForSandbox();
+#endif  // !defined(NDEBUG)
 
-  // Open proc_fd_ only in Debug mode so that forgetting to close it doesn't
-  // produce a sandbox escape in Release mode.
+  // Open proc_fd_. It would break the security of the setuid sandbox if it was
+  // not closed.
+  // If LinuxSandbox::PreinitializeSandbox() runs, InitializeSandbox() must run
+  // as well.
   proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
   CHECK_GE(proc_fd_, 0);
-#endif  // !defined(NDEBUG)
   // We "pre-warm" the code that detects supports for seccomp BPF.
   if (SandboxSeccompBPF::IsSeccompBPFDesired()) {
     if (!SandboxSeccompBPF::SupportsSandbox()) {
@@ -157,6 +168,10 @@ void LinuxSandbox::PreinitializeSandbox(
     } else {
       seccomp_bpf_supported_ = true;
     }
+
+    if (SandboxSeccompBPF::SupportsSandboxWithTsync()) {
+      seccomp_bpf_with_tsync_supported_ = true;
+    }
   }
 
   // Yama is a "global", system-level status. We assume it will not regress
@@ -167,6 +182,14 @@ void LinuxSandbox::PreinitializeSandbox(
   pre_initialized_ = true;
 }
 
+std::vector<int> LinuxSandbox::GetFileDescriptorsToClose() {
+  std::vector<int> fds;
+  if (proc_fd_ >= 0) {
+    fds.push_back(proc_fd_);
+  }
+  return fds;
+}
+
 bool LinuxSandbox::InitializeSandbox() {
   LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
   return linux_sandbox->InitializeSandboxImpl();
@@ -178,7 +201,9 @@ void LinuxSandbox::StopThread(base::Thre
 }
 
 int LinuxSandbox::GetStatus() {
-  CHECK(pre_initialized_);
+  if (!pre_initialized_) {
+    return 0;
+  }
   if (kSandboxLinuxInvalid == sandbox_status_flags_) {
     // Initialize sandbox_status_flags_.
     sandbox_status_flags_ = 0;
@@ -197,6 +222,13 @@ int LinuxSandbox::GetStatus() {
       sandbox_status_flags_ |= kSandboxLinuxSeccompBPF;
     }
 
+#if 0
+    if (seccomp_bpf_with_tsync_supported() &&
+        SandboxSeccompBPF::ShouldEnableSeccompBPF(switches::kRendererProcess)) {
+      sandbox_status_flags_ |= kSandboxLinuxSeccompTSYNC;
+    }
+#endif
+
     if (yama_is_enforcing_) {
       sandbox_status_flags_ |= kSandboxLinuxYama;
     }
@@ -209,27 +241,14 @@ int LinuxSandbox::GetStatus() {
 // PID namespaces and existing sandboxes, so "self" must really be used instead
 // of using the pid.
 bool LinuxSandbox::IsSingleThreaded() const {
-  bool is_single_threaded = false;
   base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_));
 
-// In Debug mode, it's mandatory to be able to count threads to catch bugs.
-#if !defined(NDEBUG)
-  // Using CHECK here since we want to check all the cases where
-  // !defined(NDEBUG)
-  // gets built.
   CHECK(proc_self_task.is_valid())
       << "Could not count threads, the sandbox was not "
       << "pre-initialized properly.";
-#endif  // !defined(NDEBUG)
 
-  if (!proc_self_task.is_valid()) {
-    // Pretend to be monothreaded if it can't be determined (for instance the
-    // setuid sandbox is already engaged but no proc_fd_ is available).
-    is_single_threaded = true;
-  } else {
-    is_single_threaded =
-        sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
-  }
+  const bool is_single_threaded =
+      sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
 
   return is_single_threaded;
 }
@@ -247,16 +266,23 @@ sandbox::SetuidSandboxClient*
 bool LinuxSandbox::StartSeccompBPF(const std::string& process_type) {
   CHECK(!seccomp_bpf_started_);
   CHECK(pre_initialized_);
-  if (seccomp_bpf_supported())
-    seccomp_bpf_started_ = SandboxSeccompBPF::StartSandbox(process_type);
+  if (seccomp_bpf_supported()) {
+    base::ScopedFD proc_self_task(OpenProcTaskFd(proc_fd_));
+    seccomp_bpf_started_ =
+        SandboxSeccompBPF::StartSandbox(process_type, proc_self_task.Pass());
+  }
 
-  if (seccomp_bpf_started_)
+  if (seccomp_bpf_started_) {
     LogSandboxStarted("seccomp-bpf");
+  }
 
   return seccomp_bpf_started_;
 }
 
 bool LinuxSandbox::InitializeSandboxImpl() {
+  DCHECK(!initialize_sandbox_ran_);
+  initialize_sandbox_ran_ = true;
+
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   const std::string process_type =
       command_line->GetSwitchValueASCII(switches::kProcessType);
@@ -329,6 +355,11 @@ bool LinuxSandbox::seccomp_bpf_supported
   return seccomp_bpf_supported_;
 }
 
+bool LinuxSandbox::seccomp_bpf_with_tsync_supported() const {
+  CHECK(pre_initialized_);
+  return seccomp_bpf_with_tsync_supported_;
+}
+
 bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
   (void) process_type;
 #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
@@ -379,7 +410,7 @@ bool LinuxSandbox::LimitAddressSpace(con
 }
 
 bool LinuxSandbox::HasOpenDirectories() const {
-  return sandbox::Credentials().HasOpenDirectory(proc_fd_);
+  return sandbox::ProcUtil::HasOpenDirectory(proc_fd_);
 }
 
 void LinuxSandbox::SealSandbox() {
--- a/content/common/sandbox_linux/sandbox_linux.h
+++ b/content/common/sandbox_linux/sandbox_linux.h
@@ -6,6 +6,7 @@
 #define CONTENT_COMMON_SANDBOX_LINUX_SANDBOX_LINUX_H_
 
 #include <string>
+#include <vector>
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
@@ -49,8 +50,19 @@ class LinuxSandbox {
   // Do some initialization that can only be done before any of the sandboxes
   // are enabled. If using the setuid sandbox, this should be called manually
   // before the setuid sandbox is engaged.
+  // Security: When this runs, it is imperative that either InitializeSandbox()
+  // runs as well or that all file descriptors returned in
+  // GetFileDescriptorsToClose() get closed.
+  // Otherwise file descriptors that bypass the security of the setuid sandbox
+  // would be kept open. One must be particularly careful if a process performs
+  // a fork().
   void PreinitializeSandbox();
 
+  // Return a list of file descriptors to close if PreinitializeSandbox() ran
+  // but InitializeSandbox() won't. Avoid using.
+  // TODO(jln): get rid of this hack.
+  std::vector<int> GetFileDescriptorsToClose();
+
   // Initialize the sandbox with the given pre-built configuration. Currently
   // seccomp-bpf and address space limitations (the setuid sandbox works
   // differently and is set-up in the Zygote). This will instantiate the
@@ -105,8 +117,9 @@ class LinuxSandbox {
   // are the non-static implementations.
   bool InitializeSandboxImpl();
   void StopThreadImpl(base::Thread* thread);
-  // We must have been pre_initialized_ before using this.
+  // We must have been pre_initialized_ before using these.
   bool seccomp_bpf_supported() const;
+  bool seccomp_bpf_with_tsync_supported() const;
   // Returns true if it can be determined that the current process has open
   // directories that are not managed by the LinuxSandbox class. This would
   // be a vulnerability as it would allow to bypass the setuid sandbox.
@@ -131,7 +144,9 @@ class LinuxSandbox {
   // Did PreinitializeSandbox() run?
   bool pre_initialized_;
   bool seccomp_bpf_supported_;  // Accurate if pre_initialized_.
+  bool seccomp_bpf_with_tsync_supported_;  // Accurate if pre_initialized_.
   bool yama_is_enforcing_;  // Accurate if pre_initialized_.
+  bool initialize_sandbox_ran_;  // InitializeSandbox() was called.
   scoped_ptr<sandbox::SetuidSandboxClient> setuid_sandbox_client_;
 #if defined(ANY_OF_AMTLU_SANITIZER)
   scoped_ptr<__sanitizer_sandbox_arguments> sanitizer_args_;
--- a/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc
+++ b/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc
@@ -19,6 +19,8 @@
 
 #if defined(USE_SECCOMP_BPF)
 
+#include "base/files/scoped_file.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/posix/eintr_wrapper.h"
 #include "content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.h"
 #include "content/common/sandbox_linux/bpf_gpu_policy_linux.h"
@@ -59,7 +61,8 @@ namespace content {
 #if defined(USE_SECCOMP_BPF)
 namespace {
 
-void StartSandboxWithPolicy(sandbox::bpf_dsl::Policy* policy);
+void StartSandboxWithPolicy(sandbox::bpf_dsl::Policy* policy,
+                            base::ScopedFD proc_task_fd);
 
 inline bool IsChromeOS() {
 #if defined(OS_CHROMEOS)
@@ -144,15 +147,17 @@ void RunSandboxSanityChecks(const std::s
 
 
 // This function takes ownership of |policy|.
-void StartSandboxWithPolicy(sandbox::bpf_dsl::Policy* policy) {
+void StartSandboxWithPolicy(sandbox::bpf_dsl::Policy* policy,
+                            base::ScopedFD proc_task_fd) {
   // Starting the sandbox is a one-way operation. The kernel doesn't allow
   // us to unload a sandbox policy after it has been started. Nonetheless,
   // in order to make the use of the "Sandbox" object easier, we allow for
   // the object to be destroyed after the sandbox has been started. Note that
   // doing so does not stop the sandbox.
-  SandboxBPF sandbox;
-  sandbox.SetSandboxPolicy(policy);
-  CHECK(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+  SandboxBPF sandbox(policy);
+
+  sandbox.SetProcTaskFd(proc_task_fd.Pass());
+  CHECK(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
 }
 
 // nacl_helper needs to be tiny and includes only part of content/
@@ -181,7 +186,8 @@ scoped_ptr<SandboxBPFBasePolicy> GetGpuP
 
 // Initialize the seccomp-bpf sandbox.
 bool StartBPFSandbox(const base::CommandLine& command_line,
-                     const std::string& process_type) {
+                     const std::string& process_type,
+                     base::ScopedFD proc_task_fd) {
   scoped_ptr<SandboxBPFBasePolicy> policy;
 
   if (process_type == switches::kGpuProcess) {
@@ -198,7 +204,7 @@ bool StartBPFSandbox(const base::Command
   }
 
   CHECK(policy->PreSandboxHook());
-  StartSandboxWithPolicy(policy.release());
+  StartSandboxWithPolicy(policy.release(), proc_task_fd.Pass());
 
   RunSandboxSanityChecks(process_type);
   return true;
@@ -246,22 +252,22 @@ bool SandboxSeccompBPF::ShouldEnableSecc
 
 bool SandboxSeccompBPF::SupportsSandbox() {
 #if defined(USE_SECCOMP_BPF)
-  // TODO(jln): pass the saved proc_fd_ from the LinuxSandbox singleton
-  // here.
-  SandboxBPF::SandboxStatus bpf_sandbox_status =
-      SandboxBPF::SupportsSeccompSandbox(-1);
-  // Kernel support is what we are interested in here. Other status
-  // such as STATUS_UNAVAILABLE (has threads) still indicate kernel support.
-  // We make this a negative check, since if there is a bug, we would rather
-  // "fail closed" (expect a sandbox to be available and try to start it).
-  if (bpf_sandbox_status != SandboxBPF::STATUS_UNSUPPORTED) {
-    return true;
-  }
+  return SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::SINGLE_THREADED);
+#endif
+  return false;
+}
+
+bool SandboxSeccompBPF::SupportsSandboxWithTsync() {
+#if defined(USE_SECCOMP_BPF)
+  return SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::MULTI_THREADED);
 #endif
   return false;
 }
 
-bool SandboxSeccompBPF::StartSandbox(const std::string& process_type) {
+bool SandboxSeccompBPF::StartSandbox(const std::string& process_type,
+                                     base::ScopedFD proc_task_fd) {
 #if defined(USE_SECCOMP_BPF)
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
@@ -271,7 +277,8 @@ bool SandboxSeccompBPF::StartSandbox(con
       SupportsSandbox()) {
     // If the kernel supports the sandbox, and if the command line says we
     // should enable it, enable it or die.
-    bool started_sandbox = StartBPFSandbox(command_line, process_type);
+    bool started_sandbox =
+        StartBPFSandbox(command_line, process_type, proc_task_fd.Pass());
     CHECK(started_sandbox);
     return true;
   }
@@ -280,11 +287,12 @@ bool SandboxSeccompBPF::StartSandbox(con
 }
 
 bool SandboxSeccompBPF::StartSandboxWithExternalPolicy(
-    scoped_ptr<sandbox::bpf_dsl::Policy> policy) {
+    scoped_ptr<sandbox::bpf_dsl::Policy> policy,
+    base::ScopedFD proc_task_fd) {
 #if defined(USE_SECCOMP_BPF)
   if (IsSeccompBPFDesired() && SupportsSandbox()) {
     CHECK(policy);
-    StartSandboxWithPolicy(policy.release());
+    StartSandboxWithPolicy(policy.release(), proc_task_fd.Pass());
     return true;
   }
 #endif  // defined(USE_SECCOMP_BPF)
--- a/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h
+++ b/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/basictypes.h"
+#include "base/files/scoped_file.h"
 #include "base/memory/scoped_ptr.h"
 
 namespace sandbox {
@@ -31,17 +32,20 @@ class SandboxSeccompBPF {
   static bool IsSeccompBPFDesired();
   // Should the sandbox be enabled for process_type ?
   static bool ShouldEnableSeccompBPF(const std::string& process_type);
-  // Check if the kernel supports this sandbox. It's useful to "prewarm"
-  // this, part of the result will be cached.
+  // Check if the kernel supports seccomp-bpf.
   static bool SupportsSandbox();
+  // Check if the kernel supports TSYNC (thread synchronization) with seccomp.
+  static bool SupportsSandboxWithTsync();
   // Start the sandbox and apply the policy for process_type, depending on
   // command line switches.
-  static bool StartSandbox(const std::string& process_type);
+  static bool StartSandbox(const std::string& process_type,
+                           base::ScopedFD proc_task_fd);
 
   // This is the API to enable a seccomp-bpf sandbox by using an
   // external policy.
   static bool StartSandboxWithExternalPolicy(
-      scoped_ptr<sandbox::bpf_dsl::Policy> policy);
+      scoped_ptr<sandbox::bpf_dsl::Policy> policy,
+      base::ScopedFD proc_task_fd);
   // The "baseline" policy can be a useful base to build a sandbox policy.
   static scoped_ptr<sandbox::bpf_dsl::Policy> GetBaselinePolicy();
 
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -10,7 +10,7 @@ declare_args() {
   compile_credentials = is_linux
 
   compile_seccomp_bpf_demo =
-    (is_linux && (cpu_arch == "x86" || cpu_arch == "x64"))
+      is_linux && (cpu_arch == "x86" || cpu_arch == "x64")
 }
 
 # We have two principal targets: sandbox and sandbox_linux_unittests
@@ -58,9 +58,7 @@ source_set("sandbox_linux_test_utils") {
       "seccomp-bpf/sandbox_bpf_test_runner.cc",
       "seccomp-bpf/sandbox_bpf_test_runner.h",
     ]
-    deps += [
-      ":seccomp_bpf",
-    ]
+    deps += [ ":seccomp_bpf" ]
   }
 }
 
@@ -68,13 +66,16 @@ source_set("sandbox_linux_test_utils") {
 test("sandbox_linux_unittests") {
   sources = [
     "services/scoped_process_unittest.cc",
+    "services/syscall_wrappers_unittest.cc",
     "services/thread_helpers_unittests.cc",
     "services/yama_unittests.cc",
+    "syscall_broker/broker_file_permission_unittest.cc",
     "syscall_broker/broker_process_unittest.cc",
     "tests/main.cc",
     "tests/scoped_temporary_file.cc",
     "tests/scoped_temporary_file.h",
     "tests/scoped_temporary_file_unittest.cc",
+    "tests/test_utils_unittest.cc",
     "tests/unit_tests_unittest.cc",
   ]
 
@@ -92,9 +93,7 @@ test("sandbox_linux_unittests") {
   }
 
   if (compile_suid_client) {
-    sources += [
-      "suid/client/setuid_sandbox_client_unittest.cc",
-    ]
+    sources += [ "suid/client/setuid_sandbox_client_unittest.cc" ]
   }
   if (use_seccomp_bpf) {
     sources += [
@@ -106,6 +105,7 @@ test("sandbox_linux_unittests") {
       "seccomp-bpf/bpf_tests_unittest.cc",
       "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",
     ]
@@ -113,6 +113,7 @@ test("sandbox_linux_unittests") {
   if (compile_credentials) {
     sources += [
       "services/credentials_unittest.cc",
+      "services/proc_util_unittest.cc",
       "services/unix_domain_socket_unittest.cc",
     ]
   }
@@ -150,15 +151,12 @@ component("seccomp_bpf") {
     "bpf_dsl/policy_compiler.cc",
     "bpf_dsl/policy_compiler.h",
     "bpf_dsl/trap_registry.h",
-    "seccomp-bpf/basicblock.cc",
-    "seccomp-bpf/basicblock.h",
     "seccomp-bpf/codegen.cc",
     "seccomp-bpf/codegen.h",
     "seccomp-bpf/die.cc",
     "seccomp-bpf/die.h",
     "seccomp-bpf/errorcode.cc",
     "seccomp-bpf/errorcode.h",
-    "seccomp-bpf/instruction.h",
     "seccomp-bpf/linux_seccomp.h",
     "seccomp-bpf/sandbox_bpf.cc",
     "seccomp-bpf/sandbox_bpf.h",
@@ -174,6 +172,7 @@ component("seccomp_bpf") {
   defines = [ "SANDBOX_IMPLEMENTATION" ]
 
   deps = [
+    ":sandbox_services",
     ":sandbox_services_headers",
     "//base",
   ]
@@ -199,7 +198,7 @@ component("seccomp_bpf_helpers") {
 }
 
 if (is_linux) {
-# The setuid sandbox for Linux.
+  # The setuid sandbox for Linux.
   executable("chrome_sandbox") {
     sources = [
       "suid/common/sandbox.h",
@@ -212,6 +211,7 @@ if (is_linux) {
     cflags = [
       # For ULLONG_MAX
       "-std=gnu99",
+
       # These files have a suspicious comparison.
       # TODO fix this and re-enable this warning.
       "-Wno-sign-compare",
@@ -225,13 +225,19 @@ component("sandbox_services") {
     "services/init_process_reaper.h",
     "services/scoped_process.cc",
     "services/scoped_process.h",
+    "services/syscall_wrappers.cc",
+    "services/syscall_wrappers.h",
     "services/thread_helpers.cc",
     "services/thread_helpers.h",
     "services/yama.h",
     "services/yama.cc",
+    "syscall_broker/broker_channel.cc",
+    "syscall_broker/broker_channel.h",
     "syscall_broker/broker_client.cc",
     "syscall_broker/broker_client.h",
     "syscall_broker/broker_common.h",
+    "syscall_broker/broker_file_permission.cc",
+    "syscall_broker/broker_file_permission.h",
     "syscall_broker/broker_host.cc",
     "syscall_broker/broker_host.h",
     "syscall_broker/broker_policy.cc",
@@ -246,7 +252,10 @@ component("sandbox_services") {
     sources += [
       "services/credentials.cc",
       "services/credentials.h",
+      "services/proc_util.cc",
+      "services/proc_util.h",
     ]
+
     # For capabilities.cc.
     configs += [ "//build/config/linux:libcap" ]
   }
@@ -318,7 +327,6 @@ if (is_android) {
   #    ":sandbox_linux_unittests",
   #  ]
   #}
-
   # TODO(GYP) convert this.
   #      {
   #      'target_name': 'sandbox_linux_jni_unittests_apk',
--- a/sandbox/linux/bpf_dsl/bpf_dsl.cc
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.cc
@@ -10,12 +10,17 @@
 #include "base/memory/ref_counted.h"
 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
 #include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
 #include "sandbox/linux/seccomp-bpf/errorcode.h"
 
 namespace sandbox {
 namespace bpf_dsl {
 namespace {
 
+intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
+  SANDBOX_DIE(static_cast<char*>(aux));
+}
+
 class AllowResultExprImpl : public internal::ResultExprImpl {
  public:
   AllowResultExprImpl() {}
@@ -24,6 +29,8 @@ class AllowResultExprImpl : public inter
     return ErrorCode(ErrorCode::ERR_ALLOWED);
   }
 
+  bool IsAllow() const override { return true; }
+
  private:
   ~AllowResultExprImpl() override {}
 
@@ -40,6 +47,8 @@ class ErrorResultExprImpl : public inter
     return pc->Error(err_);
   }
 
+  bool IsDeny() const override { return true; }
+
  private:
   ~ErrorResultExprImpl() override {}
 
@@ -48,22 +57,6 @@ class ErrorResultExprImpl : public inter
   DISALLOW_COPY_AND_ASSIGN(ErrorResultExprImpl);
 };
 
-class KillResultExprImpl : public internal::ResultExprImpl {
- public:
-  explicit KillResultExprImpl(const char* msg) : msg_(msg) { DCHECK(msg_); }
-
-  ErrorCode Compile(PolicyCompiler* pc) const override {
-    return pc->Kill(msg_);
-  }
-
- private:
-  ~KillResultExprImpl() override {}
-
-  const char* msg_;
-
-  DISALLOW_COPY_AND_ASSIGN(KillResultExprImpl);
-};
-
 class TraceResultExprImpl : public internal::ResultExprImpl {
  public:
   TraceResultExprImpl(uint16_t aux) : aux_(aux) {}
@@ -82,44 +75,27 @@ class TraceResultExprImpl : public inter
 
 class TrapResultExprImpl : public internal::ResultExprImpl {
  public:
-  TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg)
-      : func_(func), arg_(arg) {
+  TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg, bool safe)
+      : func_(func), arg_(arg), safe_(safe) {
     DCHECK(func_);
   }
 
   ErrorCode Compile(PolicyCompiler* pc) const override {
-    return pc->Trap(func_, arg_);
+    return pc->Trap(func_, arg_, safe_);
   }
 
- private:
-  ~TrapResultExprImpl() override {}
+  bool HasUnsafeTraps() const override { return safe_ == false; }
 
-  TrapRegistry::TrapFnc func_;
-  const void* arg_;
-
-  DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl);
-};
-
-class UnsafeTrapResultExprImpl : public internal::ResultExprImpl {
- public:
-  UnsafeTrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg)
-      : func_(func), arg_(arg) {
-    DCHECK(func_);
-  }
-
-  ErrorCode Compile(PolicyCompiler* pc) const override {
-    return pc->UnsafeTrap(func_, arg_);
-  }
-
-  bool HasUnsafeTraps() const override { return true; }
+  bool IsDeny() const override { return true; }
 
  private:
-  ~UnsafeTrapResultExprImpl() override {}
+  ~TrapResultExprImpl() override {}
 
   TrapRegistry::TrapFnc func_;
   const void* arg_;
+  bool safe_;
 
-  DISALLOW_COPY_AND_ASSIGN(UnsafeTrapResultExprImpl);
+  DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl);
 };
 
 class IfThenResultExprImpl : public internal::ResultExprImpl {
@@ -258,6 +234,14 @@ bool ResultExprImpl::HasUnsafeTraps() co
   return false;
 }
 
+bool ResultExprImpl::IsAllow() const {
+  return false;
+}
+
+bool ResultExprImpl::IsDeny() const {
+  return false;
+}
+
 uint64_t DefaultMask(size_t size) {
   switch (size) {
     case 4:
@@ -291,7 +275,7 @@ ResultExpr Error(int err) {
 }
 
 ResultExpr Kill(const char* msg) {
-  return ResultExpr(new const KillResultExprImpl(msg));
+  return Trap(BPFFailure, msg);
 }
 
 ResultExpr Trace(uint16_t aux) {
@@ -299,11 +283,13 @@ ResultExpr Trace(uint16_t aux) {
 }
 
 ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) {
-  return ResultExpr(new const TrapResultExprImpl(trap_func, aux));
+  return ResultExpr(
+      new const TrapResultExprImpl(trap_func, aux, true /* safe */));
 }
 
 ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) {
-  return ResultExpr(new const UnsafeTrapResultExprImpl(trap_func, aux));
+  return ResultExpr(
+      new const TrapResultExprImpl(trap_func, aux, false /* unsafe */));
 }
 
 BoolExpr BoolConst(bool value) {
--- a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
@@ -47,6 +47,12 @@ class ResultExprImpl : public base::RefC
   // contains an unsafe trap expression.
   virtual bool HasUnsafeTraps() const;
 
+  // IsAllow returns whether the result expression is an "allow" result.
+  virtual bool IsAllow() const;
+
+  // IsAllow returns whether the result expression is a "deny" result.
+  virtual bool IsDeny() const;
+
  protected:
   ResultExprImpl() {}
   virtual ~ResultExprImpl() {}
--- a/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_more_unittest.cc
@@ -30,6 +30,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/sys_info.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
 #include "sandbox/linux/bpf_dsl/policy.h"
@@ -41,6 +42,8 @@
 #include "sandbox/linux/seccomp-bpf/syscall.h"
 #include "sandbox/linux/seccomp-bpf/trap.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
 #include "sandbox/linux/syscall_broker/broker_process.h"
 #include "sandbox/linux/tests/scoped_temporary_file.h"
 #include "sandbox/linux/tests/unit_tests.h"
@@ -74,28 +77,6 @@ void EnableUnsafeTraps() {
   Die::SuppressInfoMessages(true);
 }
 
-// This test should execute no matter whether we have kernel support. So,
-// we make it a TEST() instead of a BPF_TEST().
-TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupports)) {
-  // We check that we don't crash, but it's ok if the kernel doesn't
-  // support it.
-  bool seccomp_bpf_supported =
-      SandboxBPF::SupportsSeccompSandbox(-1) == SandboxBPF::STATUS_AVAILABLE;
-  // We want to log whether or not seccomp BPF is actually supported
-  // since actual test coverage depends on it.
-  RecordProperty("SeccompBPFSupported",
-                 seccomp_bpf_supported ? "true." : "false.");
-  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, DISABLE_ON_TSAN(CallSupportsTwice)) {
-  SandboxBPF::SupportsSeccompSandbox(-1);
-  SandboxBPF::SupportsSeccompSandbox(-1);
-}
-
 // BPF_TEST does a lot of the boiler-plate code around setting up a
 // policy and optional passing data between the caller, the policy and
 // any Trap() handlers. This is great for writing short and concise tests,
@@ -130,13 +111,12 @@ class VerboseAPITestingPolicy : public P
 };
 
 SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(VerboseAPITesting)) {
-  if (SandboxBPF::SupportsSeccompSandbox(-1) ==
-      sandbox::SandboxBPF::STATUS_AVAILABLE) {
+  if (SandboxBPF::SupportsSeccompSandbox(
+          SandboxBPF::SeccompLevel::SINGLE_THREADED)) {
     static int counter = 0;
 
-    SandboxBPF sandbox;
-    sandbox.SetSandboxPolicy(new VerboseAPITestingPolicy(&counter));
-    BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+    SandboxBPF sandbox(new VerboseAPITestingPolicy(&counter));
+    BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
 
     BPF_ASSERT_EQ(0, counter);
     BPF_ASSERT_EQ(0, syscall(__NR_uname, 0));
@@ -178,6 +158,14 @@ BPF_TEST_C(SandboxBPF, ApplyBasicBlackli
   BlacklistNanosleepPolicy::AssertNanosleepFails();
 }
 
+BPF_TEST_C(SandboxBPF, UseVsyscall, BlacklistNanosleepPolicy) {
+  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
+  // we may get SIGKILL-ed. Detect this!
+  BPF_ASSERT_NE(static_cast<time_t>(-1), time(&current_time));
+}
+
 // Now do a simple whitelist test
 
 class WhitelistGetpidPolicy : public Policy {
@@ -203,7 +191,7 @@ class WhitelistGetpidPolicy : public Pol
 BPF_TEST_C(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
   // getpid() should be allowed
   errno = 0;
-  BPF_ASSERT(syscall(__NR_getpid) > 0);
+  BPF_ASSERT(sys_getpid() > 0);
   BPF_ASSERT(errno == 0);
 
   // getpgid() should be denied
@@ -246,7 +234,7 @@ BPF_TEST(SandboxBPF,
          int /* (*BPF_AUX) */) {
   // getpid() should work properly
   errno = 0;
-  BPF_ASSERT(syscall(__NR_getpid) > 0);
+  BPF_ASSERT(sys_getpid() > 0);
   BPF_ASSERT(errno == 0);
 
   // Our Auxiliary Data, should be reset by the signal handler
@@ -395,9 +383,8 @@ BPF_TEST_C(SandboxBPF, StackingPolicy, S
 
   // Stack a second sandbox with its own policy. Verify that we can further
   // restrict filters, but we cannot relax existing filters.
-  SandboxBPF sandbox;
-  sandbox.SetSandboxPolicy(new StackingPolicyPartTwo());
-  BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+  SandboxBPF sandbox(new StackingPolicyPartTwo());
+  BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
 
   errno = 0;
   BPF_ASSERT(syscall(__NR_getppid, 0) == -1);
@@ -478,9 +465,9 @@ int ArmPrivateSysnoToErrno(int sysno) {
 class ArmPrivatePolicy : public Policy {
  public:
   ArmPrivatePolicy() {}
-  virtual ~ArmPrivatePolicy() {}
+  ~ArmPrivatePolicy() override {}
 
-  virtual ResultExpr EvaluateSyscall(int sysno) const override {
+  ResultExpr EvaluateSyscall(int sysno) const override {
     DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
     // Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
     // ARM private system calls.
@@ -512,7 +499,7 @@ intptr_t CountSyscalls(const struct arch
 
   // Verify that within the callback function all filtering is temporarily
   // disabled.
-  BPF_ASSERT(syscall(__NR_getpid) > 1);
+  BPF_ASSERT(sys_getpid() > 1);
 
   // Verify that we can now call the underlying system call without causing
   // infinite recursion.
@@ -549,7 +536,7 @@ class GreyListedPolicy : public Policy {
 };
 
 BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* (*BPF_AUX) */) {
-  BPF_ASSERT(syscall(__NR_getpid) == -1);
+  BPF_ASSERT(sys_getpid() == -1);
   BPF_ASSERT(errno == EPERM);
   BPF_ASSERT(*BPF_AUX == 0);
   BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid));
@@ -755,30 +742,35 @@ bool NoOpCallback() {
 class InitializedOpenBroker {
  public:
   InitializedOpenBroker() : initialized_(false) {
-    std::vector<std::string> allowed_files;
-    allowed_files.push_back("/proc/allowed");
-    allowed_files.push_back("/proc/cpuinfo");
+    std::vector<syscall_broker::BrokerFilePermission> permissions;
+    permissions.push_back(
+        syscall_broker::BrokerFilePermission::ReadOnly("/proc/allowed"));
+    permissions.push_back(
+        syscall_broker::BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
 
     broker_process_.reset(
-        new BrokerProcess(EPERM, allowed_files, std::vector<std::string>()));
+        new syscall_broker::BrokerProcess(EPERM, permissions));
     BPF_ASSERT(broker_process() != NULL);
     BPF_ASSERT(broker_process_->Init(base::Bind(&NoOpCallback)));
 
     initialized_ = true;
   }
   bool initialized() { return initialized_; }
-  class BrokerProcess* broker_process() { return broker_process_.get(); }
+  class syscall_broker::BrokerProcess* broker_process() {
+    return broker_process_.get();
+  }
 
  private:
   bool initialized_;
-  scoped_ptr<class BrokerProcess> broker_process_;
+  scoped_ptr<class syscall_broker::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);
+  syscall_broker::BrokerProcess* broker_process =
+      static_cast<syscall_broker::BrokerProcess*>(aux);
   switch (args.nr) {
     case __NR_faccessat:  // access is a wrapper of faccessat in android
       BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
@@ -824,7 +816,7 @@ class DenyOpenPolicy : public Policy {
 #endif
       case __NR_openat:
         // We get a InitializedOpenBroker class, but our trap handler wants
-        // the BrokerProcess object.
+        // the syscall_broker::BrokerProcess object.
         return Trap(BrokerOpenTrapHandler, iob_->broker_process());
       default:
         return Allow();
@@ -844,7 +836,7 @@ BPF_TEST(SandboxBPF,
          DenyOpenPolicy,
          InitializedOpenBroker /* (*BPF_AUX) */) {
   BPF_ASSERT(BPF_AUX->initialized());
-  BrokerProcess* broker_process = BPF_AUX->broker_process();
+  syscall_broker::BrokerProcess* broker_process = BPF_AUX->broker_process();
   BPF_ASSERT(broker_process != NULL);
 
   // First, use the broker "manually"
@@ -914,7 +906,7 @@ ResultExpr SimpleCondTestPolicy::Evaluat
         flags_argument_position = 2;
 
       // Allow opening files for reading, but don't allow writing.
-      COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
+      static_assert(O_RDONLY == 0, "O_RDONLY must be all zero bits");
       const Arg<int> flags(flags_argument_position);
       return If((flags & O_ACCMODE) != 0, Error(EROFS)).Else(Allow());
     }
@@ -959,9 +951,10 @@ class EqualityStressTest {
     // 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(
+    static_assert(
         kNumTestCases < (int)(MAX_PUBLIC_SYSCALL - MIN_SYSCALL - 10),
-        num_test_cases_must_be_significantly_smaller_than_num_system_calls);
+        "kNumTestCases must be significantly smaller than the number "
+        "of 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
@@ -2065,8 +2058,8 @@ class TraceAllPolicy : public Policy {
 };
 
 SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(SeccompRetTrace)) {
-  if (SandboxBPF::SupportsSeccompSandbox(-1) !=
-      sandbox::SandboxBPF::STATUS_AVAILABLE) {
+  if (!SandboxBPF::SupportsSeccompSandbox(
+          SandboxBPF::SeccompLevel::SINGLE_THREADED)) {
     return;
   }
 
@@ -2090,12 +2083,11 @@ SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN
     pid_t my_pid = getpid();
     BPF_ASSERT_NE(-1, ptrace(PTRACE_TRACEME, -1, NULL, NULL));
     BPF_ASSERT_EQ(0, raise(SIGSTOP));
-    SandboxBPF sandbox;
-    sandbox.SetSandboxPolicy(new TraceAllPolicy);
-    BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+    SandboxBPF sandbox(new TraceAllPolicy);
+    BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
 
     // getpid is allowed.
-    BPF_ASSERT_EQ(my_pid, syscall(__NR_getpid));
+    BPF_ASSERT_EQ(my_pid, sys_getpid());
 
     // write to stdout is skipped and returns a fake value.
     BPF_ASSERT_EQ(kExpectedReturnValue,
@@ -2263,8 +2255,17 @@ void* TsyncApplyToTwoThreadsFunc(void* c
 }
 
 SANDBOX_TEST(SandboxBPF, Tsync) {
-  if (SandboxBPF::SupportsSeccompThreadFilterSynchronization() !=
-      SandboxBPF::STATUS_AVAILABLE) {
+  const bool supports_multi_threaded = SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::MULTI_THREADED);
+// On Chrome OS tsync is mandatory.
+#if defined(OS_CHROMEOS)
+  if (base::SysInfo::IsRunningOnChromeOS()) {
+    BPF_ASSERT_EQ(true, supports_multi_threaded);
+  }
+// else a Chrome OS build not running on a Chrome OS device e.g. Chrome bots.
+// In this case fall through.
+#endif
+  if (!supports_multi_threaded) {
     return;
   }
 
@@ -2280,9 +2281,8 @@ SANDBOX_TEST(SandboxBPF, Tsync) {
   BPF_ASSERT_EQ(0, HANDLE_EINTR(syscall(__NR_nanosleep, &ts, NULL)));
 
   // Engage the sandbox.
-  SandboxBPF sandbox;
-  sandbox.SetSandboxPolicy(new BlacklistNanosleepPolicy());
-  BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::PROCESS_MULTI_THREADED));
+  SandboxBPF sandbox(new BlacklistNanosleepPolicy());
+  BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::MULTI_THREADED));
 
   // This thread should have the filter applied as well.
   BlacklistNanosleepPolicy::AssertNanosleepFails();
@@ -2312,9 +2312,8 @@ SANDBOX_DEATH_TEST(
   base::Thread thread("sandbox.linux.StartMultiThreadedAsSingleThreaded");
   BPF_ASSERT(thread.Start());
 
-  SandboxBPF sandbox;
-  sandbox.SetSandboxPolicy(new AllowAllPolicy());
-  BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
+  SandboxBPF sandbox(new AllowAllPolicy());
+  BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
 }
 
 // http://crbug.com/407357
@@ -2325,9 +2324,8 @@ SANDBOX_DEATH_TEST(
     DEATH_MESSAGE(
         "Cannot start sandbox; process may be single-threaded when "
         "reported as not")) {
-  SandboxBPF sandbox;
-  sandbox.SetSandboxPolicy(new AllowAllPolicy());
-  BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::PROCESS_MULTI_THREADED));
+  SandboxBPF sandbox(new AllowAllPolicy());
+  BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::SeccompLevel::MULTI_THREADED));
 }
 #endif  // !defined(THREAD_SANITIZER)
 
--- a/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
@@ -15,10 +15,12 @@
 #include "base/files/scoped_file.h"
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
 #include "sandbox/linux/bpf_dsl/policy.h"
 #include "sandbox/linux/seccomp-bpf/bpf_tests.h"
 #include "sandbox/linux/seccomp-bpf/errorcode.h"
 #include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 #define CASES SANDBOX_BPF_DSL_CASES
 
@@ -328,6 +330,48 @@ BPF_TEST_C(BPFDSL, SwitchTest, SwitchPol
   ASSERT_SYSCALL_RESULT(-EACCES, fcntl, sock_fd.get(), F_DUPFD, 0);
 }
 
+static intptr_t DummyTrap(const struct arch_seccomp_data& data, void* aux) {
+  return 0;
+}
+
+TEST(BPFDSL, IsAllowDeny) {
+  ResultExpr allow = Allow();
+  EXPECT_TRUE(allow->IsAllow());
+  EXPECT_FALSE(allow->IsDeny());
+
+  ResultExpr error = Error(ENOENT);
+  EXPECT_FALSE(error->IsAllow());
+  EXPECT_TRUE(error->IsDeny());
+
+  ResultExpr trace = Trace(42);
+  EXPECT_FALSE(trace->IsAllow());
+  EXPECT_FALSE(trace->IsDeny());
+
+  ResultExpr trap = Trap(DummyTrap, nullptr);
+  EXPECT_FALSE(trap->IsAllow());
+  EXPECT_TRUE(trap->IsDeny());
+
+  const Arg<int> arg(0);
+  ResultExpr maybe = If(arg == 0, Allow()).Else(Error(EPERM));
+  EXPECT_FALSE(maybe->IsAllow());
+  EXPECT_FALSE(maybe->IsDeny());
+}
+
+TEST(BPFDSL, HasUnsafeTraps) {
+  ResultExpr allow = Allow();
+  EXPECT_FALSE(allow->HasUnsafeTraps());
+
+  ResultExpr safe = Trap(DummyTrap, nullptr);
+  EXPECT_FALSE(safe->HasUnsafeTraps());
+
+  ResultExpr unsafe = UnsafeTrap(DummyTrap, nullptr);
+  EXPECT_TRUE(unsafe->HasUnsafeTraps());
+
+  const Arg<int> arg(0);
+  ResultExpr maybe = If(arg == 0, allow).Else(unsafe);
+  EXPECT_TRUE(maybe->HasUnsafeTraps());
+}
+
 }  // namespace
 }  // namespace bpf_dsl
 }  // namespace sandbox
--- a/sandbox/linux/bpf_dsl/policy_compiler.cc
+++ b/sandbox/linux/bpf_dsl/policy_compiler.cc
@@ -18,7 +18,6 @@
 #include "sandbox/linux/seccomp-bpf/codegen.h"
 #include "sandbox/linux/seccomp-bpf/die.h"
 #include "sandbox/linux/seccomp-bpf/errorcode.h"
-#include "sandbox/linux/seccomp-bpf/instruction.h"
 #include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
 #include "sandbox/linux/seccomp-bpf/syscall.h"
 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
@@ -55,12 +54,6 @@ bool HasExactlyOneBit(uint64_t x) {
   return x != 0 && (x & (x - 1)) == 0;
 }
 
-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));
-}
-
 // 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) {
@@ -72,11 +65,8 @@ intptr_t ReturnErrno(const struct arch_s
   return -err;
 }
 
-intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
-  SANDBOX_DIE(static_cast<char*>(aux));
-}
-
 bool HasUnsafeTraps(const Policy* policy) {
+  DCHECK(policy);
   for (uint32_t sysnum : SyscallSet::ValidOnly()) {
     if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) {
       return true;
@@ -88,9 +78,8 @@ bool HasUnsafeTraps(const Policy* policy
 }  // namespace
 
 struct PolicyCompiler::Range {
-  Range(uint32_t f, const ErrorCode& e) : from(f), err(e) {}
   uint32_t from;
-  ErrorCode err;
+  CodeGen::Node node;
 };
 
 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry)
@@ -99,13 +88,14 @@ PolicyCompiler::PolicyCompiler(const Pol
       conds_(),
       gen_(),
       has_unsafe_traps_(HasUnsafeTraps(policy_)) {
+  DCHECK(policy);
 }
 
 PolicyCompiler::~PolicyCompiler() {
 }
 
 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() {
-  if (!IsDenied(policy_->InvalidSyscall()->Compile(this))) {
+  if (!policy_->InvalidSyscall()->IsDeny()) {
     SANDBOX_DIE("Policies should deny invalid system calls.");
   }
 
@@ -122,8 +112,7 @@ scoped_ptr<CodeGen::Program> PolicyCompi
     }
 
     for (int sysnum : kSyscallsRequiredForUnsafeTraps) {
-      if (!policy_->EvaluateSyscall(sysnum)->Compile(this)
-               .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))) {
+      if (!policy_->EvaluateSyscall(sysnum)->IsAllow()) {
         SANDBOX_DIE(
             "Policies that use UnsafeTrap() must unconditionally allow all "
             "required system calls");
@@ -146,7 +135,7 @@ scoped_ptr<CodeGen::Program> PolicyCompi
   return program.Pass();
 }
 
-Instruction* PolicyCompiler::AssemblePolicy() {
+CodeGen::Node PolicyCompiler::AssemblePolicy() {
   // A compiled policy consists of three logical parts:
   //   1. Check that the "arch" field matches the expected architecture.
   //   2. If the policy involves unsafe traps, check if the syscall was
@@ -156,20 +145,17 @@ Instruction* PolicyCompiler::AssemblePol
   return CheckArch(MaybeAddEscapeHatch(DispatchSyscall()));
 }
 
-Instruction* PolicyCompiler::CheckArch(Instruction* passed) {
+CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) {
   // If the architecture doesn't match SECCOMP_ARCH, disallow the
   // system call.
   return gen_.MakeInstruction(
-      BPF_LD + BPF_W + BPF_ABS,
-      SECCOMP_ARCH_IDX,
+      BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX,
       gen_.MakeInstruction(
-          BPF_JMP + BPF_JEQ + BPF_K,
-          SECCOMP_ARCH,
-          passed,
-          RetExpression(Kill("Invalid audit architecture in BPF filter"))));
+          BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed,
+          CompileResult(Kill("Invalid audit architecture in BPF filter"))));
 }
 
-Instruction* PolicyCompiler::MaybeAddEscapeHatch(Instruction* rest) {
+CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) {
   // If no unsafe traps, then simply return |rest|.
   if (!has_unsafe_traps_) {
     return rest;
@@ -190,30 +176,24 @@ Instruction* PolicyCompiler::MaybeAddEsc
   // For simplicity, we check the full 64-bit instruction pointer even
   // on 32-bit architectures.
   return gen_.MakeInstruction(
-      BPF_LD + BPF_W + BPF_ABS,
-      SECCOMP_IP_LSB_IDX,
+      BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX,
       gen_.MakeInstruction(
-          BPF_JMP + BPF_JEQ + BPF_K,
-          low,
+          BPF_JMP + BPF_JEQ + BPF_K, low,
           gen_.MakeInstruction(
-              BPF_LD + BPF_W + BPF_ABS,
-              SECCOMP_IP_MSB_IDX,
-              gen_.MakeInstruction(
-                  BPF_JMP + BPF_JEQ + BPF_K,
-                  hi,
-                  RetExpression(ErrorCode(ErrorCode::ERR_ALLOWED)),
-                  rest)),
+              BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX,
+              gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hi,
+                                   CompileResult(Allow()), rest)),
           rest));
 }
 
-Instruction* PolicyCompiler::DispatchSyscall() {
+CodeGen::Node PolicyCompiler::DispatchSyscall() {
   // Evaluate all possible system calls and group their ErrorCodes into
   // ranges of identical codes.
   Ranges ranges;
   FindRanges(&ranges);
 
   // Compile the system call ranges to an optimized BPF jumptable
-  Instruction* jumptable = AssembleJumpTable(ranges.begin(), ranges.end());
+  CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end());
 
   // Grab the system call number, so that we can check it and then
   // execute the jump table.
@@ -221,12 +201,12 @@ Instruction* PolicyCompiler::DispatchSys
       BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable));
 }
 
-Instruction* PolicyCompiler::CheckSyscallNumber(Instruction* passed) {
+CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) {
   if (kIsIntel) {
     // On Intel architectures, verify that system call numbers are in the
     // expected number range.
-    Instruction* invalidX32 =
-        RetExpression(Kill("Illegal mixing of system call ABIs"));
+    CodeGen::Node invalidX32 =
+        CompileResult(Kill("Illegal mixing of system call ABIs"));
     if (kIsX32) {
       // The newer x32 API always sets bit 30.
       return gen_.MakeInstruction(
@@ -248,28 +228,32 @@ void PolicyCompiler::FindRanges(Ranges*
   // 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.
-  const ErrorCode invalid_err = policy_->InvalidSyscall()->Compile(this);
+  const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall());
   uint32_t old_sysnum = 0;
-  ErrorCode old_err = SyscallSet::IsValid(old_sysnum)
-                          ? policy_->EvaluateSyscall(old_sysnum)->Compile(this)
-                          : invalid_err;
+  CodeGen::Node old_node =
+      SyscallSet::IsValid(old_sysnum)
+          ? CompileResult(policy_->EvaluateSyscall(old_sysnum))
+          : invalid_node;
 
   for (uint32_t sysnum : SyscallSet::All()) {
-    ErrorCode err =
+    CodeGen::Node node =
         SyscallSet::IsValid(sysnum)
-            ? policy_->EvaluateSyscall(static_cast<int>(sysnum))->Compile(this)
-            : invalid_err;
-    if (!err.Equals(old_err)) {
-      ranges->push_back(Range(old_sysnum, old_err));
+            ? CompileResult(policy_->EvaluateSyscall(static_cast<int>(sysnum)))
+            : invalid_node;
+    // N.B., here we rely on CodeGen folding (i.e., returning the same
+    // node value for) identical code sequences, otherwise our jump
+    // table will blow up in size.
+    if (node != old_node) {
+      ranges->push_back(Range{old_sysnum, old_node});
       old_sysnum = sysnum;
-      old_err = err;
+      old_node = node;
     }
   }
-  ranges->push_back(Range(old_sysnum, old_err));
+  ranges->push_back(Range{old_sysnum, old_node});
 }
 
-Instruction* PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
-                                               Ranges::const_iterator stop) {
+CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
+                                                Ranges::const_iterator stop) {
   // We convert the list of system call ranges into jump table that performs
   // a binary search over the ranges.
   // As a sanity check, we need to have at least one distinct ranges for us
@@ -279,7 +263,7 @@ Instruction* PolicyCompiler::AssembleJum
   } 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 RetExpression(start->err);
+    return start->node;
   }
 
   // Pick the range object that is located at the mid point of our list.
@@ -289,12 +273,16 @@ Instruction* PolicyCompiler::AssembleJum
   Ranges::const_iterator mid = start + (stop - start) / 2;
 
   // Sub-divide the list of ranges and continue recursively.
-  Instruction* jf = AssembleJumpTable(start, mid);
-  Instruction* jt = AssembleJumpTable(mid, stop);
+  CodeGen::Node jf = AssembleJumpTable(start, mid);
+  CodeGen::Node jt = AssembleJumpTable(mid, stop);
   return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
 }
 
-Instruction* PolicyCompiler::RetExpression(const ErrorCode& err) {
+CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) {
+  return RetExpression(res->Compile(this));
+}
+
+CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) {
   switch (err.error_type()) {
     case ErrorCode::ET_COND:
       return CondExpression(err);
@@ -306,7 +294,7 @@ Instruction* PolicyCompiler::RetExpressi
   }
 }
 
-Instruction* PolicyCompiler::CondExpression(const ErrorCode& cond) {
+CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) {
   // Sanity check that |cond| makes sense.
   if (cond.argno_ < 0 || cond.argno_ >= 6) {
     SANDBOX_DIE("sandbox_bpf: invalid argument number");
@@ -328,8 +316,8 @@ Instruction* PolicyCompiler::CondExpress
   // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it
   // because some SandboxBPF unit tests exercise it.
 
-  Instruction* passed = RetExpression(*cond.passed_);
-  Instruction* failed = RetExpression(*cond.failed_);
+  CodeGen::Node passed = RetExpression(*cond.passed_);
+  CodeGen::Node failed = RetExpression(*cond.failed_);
 
   // We want to emit code to check "(arg & mask) == value" where arg, mask, and
   // value are 64-bit values, but the BPF machine is only 32-bit. We implement
@@ -341,16 +329,16 @@ Instruction* PolicyCompiler::CondExpress
                             failed);
 }
 
-Instruction* PolicyCompiler::CondExpressionHalf(const ErrorCode& cond,
-                                                ArgHalf half,
-                                                Instruction* passed,
-                                                Instruction* failed) {
+CodeGen::Node PolicyCompiler::CondExpressionHalf(const ErrorCode& cond,
+                                                 ArgHalf half,
+                                                 CodeGen::Node passed,
+                                                 CodeGen::Node failed) {
   if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) {
     // Special logic for sanity checking the upper 32-bits of 32-bit system
     // call arguments.
 
     // TODO(mdempsky): Compile Unexpected64bitArgument() just per program.
-    Instruction* invalid_64bit = RetExpression(Unexpected64bitArgument());
+    CodeGen::Node invalid_64bit = RetExpression(Unexpected64bitArgument());
 
     const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_);
     const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_);
@@ -455,7 +443,7 @@ Instruction* PolicyCompiler::CondExpress
 }
 
 ErrorCode PolicyCompiler::Unexpected64bitArgument() {
-  return Kill("Unexpected 64bit argument detected");
+  return Kill("Unexpected 64bit argument detected")->Compile(this);
 }
 
 ErrorCode PolicyCompiler::Error(int err) {
@@ -469,28 +457,19 @@ ErrorCode PolicyCompiler::Error(int err)
     // 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.
-    return Trap(ReturnErrno, reinterpret_cast<void*>(err));
+    return Trap(ReturnErrno, reinterpret_cast<void*>(err), true);
   }
 
   return ErrorCode(err);
 }
 
-ErrorCode PolicyCompiler::MakeTrap(TrapRegistry::TrapFnc fnc,
-                                   const void* aux,
-                                   bool safe) {
+ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc,
+                               const void* aux,
+                               bool safe) {
   uint16_t trap_id = registry_->Add(fnc, aux, safe);
   return ErrorCode(trap_id, fnc, aux, safe);
 }
 
-ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, const void* aux) {
-  return MakeTrap(fnc, aux, true /* Safe Trap */);
-}
-
-ErrorCode PolicyCompiler::UnsafeTrap(TrapRegistry::TrapFnc fnc,
-                                     const void* aux) {
-  return MakeTrap(fnc, aux, false /* Unsafe Trap */);
-}
-
 bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) {
   for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) {
     if (sysno == kSyscallsRequiredForUnsafeTraps[i]) {
@@ -514,9 +493,5 @@ ErrorCode PolicyCompiler::CondMaskedEqua
                    &*conds_.insert(failed).first);
 }
 
-ErrorCode PolicyCompiler::Kill(const char* msg) {
-  return Trap(BPFFailure, const_cast<char*>(msg));
-}
-
 }  // namespace bpf_dsl
 }  // namespace sandbox
--- a/sandbox/linux/bpf_dsl/policy_compiler.h
+++ b/sandbox/linux/bpf_dsl/policy_compiler.h
@@ -13,13 +13,12 @@
 
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
 #include "sandbox/linux/seccomp-bpf/codegen.h"
 #include "sandbox/linux/seccomp-bpf/errorcode.h"
 #include "sandbox/sandbox_export.h"
 
 namespace sandbox {
-struct Instruction;
-
 namespace bpf_dsl {
 class Policy;
 
@@ -39,25 +38,9 @@ class SANDBOX_EXPORT PolicyCompiler {
   // the specified error number.
   ErrorCode Error(int err);
 
-  // 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()
-  // handler.
-  ErrorCode Trap(TrapRegistry::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(TrapRegistry::TrapFnc fnc, const void* aux);
+  // Trap returns an ErrorCode to indicate the system call should
+  // instead invoke a trap handler.
+  ErrorCode Trap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe);
 
   // UnsafeTraps require some syscalls to always be allowed.
   // This helper function returns true for these calls.
@@ -79,9 +62,6 @@ class SANDBOX_EXPORT PolicyCompiler {
                             const ErrorCode& passed,
                             const ErrorCode& failed);
 
-  // Kill the program and print an error message.
-  ErrorCode Kill(const char* msg);
-
   // 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.
@@ -90,7 +70,6 @@ class SANDBOX_EXPORT PolicyCompiler {
  private:
   struct Range;
   typedef std::vector<Range> Ranges;
-  typedef std::map<uint32_t, ErrorCode> ErrMap;
   typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
 
   // Used by CondExpressionHalf to track which half of the argument it's
@@ -101,28 +80,28 @@ class SANDBOX_EXPORT PolicyCompiler {
   };
 
   // Compile the configured policy into a complete instruction sequence.
-  Instruction* AssemblePolicy();
+  CodeGen::Node AssemblePolicy();
 
   // Return an instruction sequence that checks the
   // arch_seccomp_data's "arch" field is valid, and then passes
   // control to |passed| if so.
-  Instruction* CheckArch(Instruction* passed);
+  CodeGen::Node CheckArch(CodeGen::Node passed);
 
   // If |has_unsafe_traps_| is true, returns an instruction sequence
   // that allows all system calls from Syscall::Call(), and otherwise
   // passes control to |rest|. Otherwise, simply returns |rest|.
-  Instruction* MaybeAddEscapeHatch(Instruction* rest);
+  CodeGen::Node MaybeAddEscapeHatch(CodeGen::Node rest);
 
   // Return an instruction sequence that loads and checks the system
   // call number, performs a binary search, and then dispatches to an
   // appropriate instruction sequence compiled from the current
   // policy.
-  Instruction* DispatchSyscall();
+  CodeGen::Node DispatchSyscall();
 
   // Return an instruction sequence that checks the system call number
   // (expected to be loaded in register A) and if valid, passes
   // control to |passed| (with register A still valid).
-  Instruction* CheckSyscallNumber(Instruction* passed);
+  CodeGen::Node CheckSyscallNumber(CodeGen::Node passed);
 
   // 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
@@ -132,31 +111,32 @@ class SANDBOX_EXPORT PolicyCompiler {
 
   // Returns a BPF program snippet that implements a jump table for the
   // given range of system call numbers. This function runs recursively.
-  Instruction* AssembleJumpTable(Ranges::const_iterator start,
-                                 Ranges::const_iterator stop);
+  CodeGen::Node AssembleJumpTable(Ranges::const_iterator start,
+                                  Ranges::const_iterator stop);
+
+  // CompileResult compiles an individual result expression into a
+  // CodeGen node.
+  CodeGen::Node CompileResult(const ResultExpr& res);
 
   // 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(const ErrorCode& err);
+  CodeGen::Node RetExpression(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(const ErrorCode& cond);
+  CodeGen::Node CondExpression(const ErrorCode& cond);
 
   // Returns a BPF program that evaluates half of a conditional expression;
   // it should only ever be called from CondExpression().
-  Instruction* CondExpressionHalf(const ErrorCode& cond,
-                                  ArgHalf half,
-                                  Instruction* passed,
-                                  Instruction* failed);
-
-  // MakeTrap is the common implementation for Trap and UnsafeTrap.
-  ErrorCode MakeTrap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe);
+  CodeGen::Node CondExpressionHalf(const ErrorCode& cond,
+                                   ArgHalf half,
+                                   CodeGen::Node passed,
+                                   CodeGen::Node failed);
 
   const Policy* policy_;
   TrapRegistry* registry_;
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -130,15 +130,12 @@
         'bpf_dsl/policy_compiler.cc',
         'bpf_dsl/policy_compiler.h',
         'bpf_dsl/trap_registry.h',
-        'seccomp-bpf/basicblock.cc',
-        'seccomp-bpf/basicblock.h',
         'seccomp-bpf/codegen.cc',
         'seccomp-bpf/codegen.h',
         'seccomp-bpf/die.cc',
         'seccomp-bpf/die.h',
         'seccomp-bpf/errorcode.cc',
         'seccomp-bpf/errorcode.h',
-        'seccomp-bpf/instruction.h',
         'seccomp-bpf/linux_seccomp.h',
         'seccomp-bpf/sandbox_bpf.cc',
         'seccomp-bpf/sandbox_bpf.h',
@@ -153,6 +150,7 @@
       ],
       'dependencies': [
         '../base/base.gyp:base',
+        'sandbox_services',
         'sandbox_services_headers',
       ],
       'defines': [
@@ -182,6 +180,7 @@
       ],
       'dependencies': [
         '../base/base.gyp:base',
+        'sandbox_services',
         'seccomp_bpf',
       ],
       'defines': [
@@ -224,13 +223,19 @@
         'services/init_process_reaper.h',
         'services/scoped_process.cc',
         'services/scoped_process.h',
+        'services/syscall_wrappers.cc',
+        'services/syscall_wrappers.h',
         'services/thread_helpers.cc',
         'services/thread_helpers.h',
         'services/yama.cc',
         'services/yama.h',
+        'syscall_broker/broker_channel.cc',
+        'syscall_broker/broker_channel.h',
         'syscall_broker/broker_client.cc',
         'syscall_broker/broker_client.h',
         'syscall_broker/broker_common.h',
+        'syscall_broker/broker_file_permission.cc',
+        'syscall_broker/broker_file_permission.h',
         'syscall_broker/broker_host.cc',
         'syscall_broker/broker_host.h',
         'syscall_broker/broker_policy.cc',
@@ -249,6 +254,8 @@
           'sources': [
             'services/credentials.cc',
             'services/credentials.h',
+            'services/proc_util.cc',
+            'services/proc_util.h',
           ],
           'dependencies': [
             # for capabilities.cc.
--- a/sandbox/linux/sandbox_linux_test_sources.gypi
+++ b/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -8,6 +8,7 @@
   'dependencies': [
     'sandbox',
     'sandbox_linux_test_utils',
+    'sandbox_services',
     '../base/base.gyp:base',
     '../base/base.gyp:test_support_base',
     '../testing/gtest.gyp:gtest',
@@ -17,13 +18,16 @@
   ],
   'sources': [
     'services/scoped_process_unittest.cc',
+    'services/syscall_wrappers_unittest.cc',
     'services/thread_helpers_unittests.cc',
     'services/yama_unittests.cc',
+    'syscall_broker/broker_file_permission_unittest.cc',
     'syscall_broker/broker_process_unittest.cc',
     'tests/main.cc',
     'tests/scoped_temporary_file.cc',
     'tests/scoped_temporary_file.h',
     'tests/scoped_temporary_file_unittest.cc',
+    'tests/test_utils_unittest.cc',
     'tests/unit_tests_unittest.cc',
   ],
   'conditions': [
@@ -42,6 +46,7 @@
         'seccomp-bpf/bpf_tests_unittest.cc',
         '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',
       ],
@@ -49,6 +54,7 @@
     [ 'compile_credentials==1', {
       'sources': [
         'services/credentials_unittest.cc',
+        'services/proc_util_unittest.cc',
         'services/unix_domain_socket_unittest.cc',
       ],
     }],
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -19,6 +19,7 @@
 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 
 // Changing this implementation will have an effect on *all* policies.
 // Currently this means: Renderer/Worker, GPU, Flash and NaCl.
@@ -186,7 +187,8 @@ ResultExpr EvaluateSyscallImpl(int fs_de
     defined(__aarch64__)
   if (sysno == __NR_socketpair) {
     // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen.
-    COMPILE_ASSERT(AF_UNIX == PF_UNIX, af_unix_pf_unix_different);
+    static_assert(AF_UNIX == PF_UNIX,
+                  "af_unix and pf_unix should not be different");
     const Arg<int> domain(0);
     return If(domain == AF_UNIX, Allow()).Else(CrashSIGSYS());
   }
@@ -237,12 +239,13 @@ ResultExpr EvaluateSyscallImpl(int fs_de
 BaselinePolicy::BaselinePolicy() : BaselinePolicy(EPERM) {}
 
 BaselinePolicy::BaselinePolicy(int fs_denied_errno)
-    : fs_denied_errno_(fs_denied_errno), policy_pid_(syscall(__NR_getpid)) {}
+    : fs_denied_errno_(fs_denied_errno), policy_pid_(sys_getpid()) {
+}
 
 BaselinePolicy::~BaselinePolicy() {
   // Make sure that this policy is created, used and destroyed by a single
   // process.
-  DCHECK_EQ(syscall(__NR_getpid), policy_pid_);
+  DCHECK_EQ(sys_getpid(), policy_pid_);
 }
 
 ResultExpr BaselinePolicy::EvaluateSyscall(int sysno) const {
@@ -250,7 +253,7 @@ ResultExpr BaselinePolicy::EvaluateSysca
   DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
   // Make sure that this policy is used in the creating process.
   if (1 == sysno) {
-    DCHECK_EQ(syscall(__NR_getpid), policy_pid_);
+    DCHECK_EQ(sys_getpid(), policy_pid_);
   }
   return EvaluateSyscallImpl(fs_denied_errno_, policy_pid_, sysno);
 }
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -32,34 +32,15 @@
 #include "sandbox/linux/seccomp-bpf/syscall.h"
 #include "sandbox/linux/services/android_futex.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 #include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/tests/test_utils.h"
 #include "sandbox/linux/tests/unit_tests.h"
 
 namespace sandbox {
 
 namespace {
 
-// |pid| is the return value of a fork()-like call. This
-// makes sure that if fork() succeeded the child exits
-// and the parent waits for it.
-void HandlePostForkReturn(pid_t pid) {
-  const int kChildExitCode = 1;
-  if (pid > 0) {
-    int status = 0;
-    PCHECK(pid == HANDLE_EINTR(waitpid(pid, &status, 0)));
-    CHECK(WIFEXITED(status));
-    CHECK_EQ(kChildExitCode, WEXITSTATUS(status));
-  } else if (pid == 0) {
-    _exit(kChildExitCode);
-  }
-}
-
-// Check that HandlePostForkReturn works.
-TEST(BaselinePolicy, HandlePostForkReturn) {
-  pid_t pid = fork();
-  HandlePostForkReturn(pid);
-}
-
 // This also tests that read(), write() and fstat() are allowed.
 void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) {
   BPF_ASSERT_LE(0, read_end.get());
@@ -106,36 +87,39 @@ BPF_TEST_C(BaselinePolicy, ForkErrno, Ba
   errno = 0;
   pid_t pid = fork();
   const int fork_errno = errno;
-  HandlePostForkReturn(pid);
+  TestUtils::HandlePostForkReturn(pid);
 
   BPF_ASSERT_EQ(-1, pid);
   BPF_ASSERT_EQ(EPERM, fork_errno);
 }
 
 pid_t ForkX86Glibc() {
-  return syscall(__NR_clone, CLONE_PARENT_SETTID | SIGCHLD);
+  static pid_t ptid;
+  return sys_clone(CLONE_PARENT_SETTID | SIGCHLD, nullptr, &ptid, nullptr,
+                   nullptr);
 }
 
 BPF_TEST_C(BaselinePolicy, ForkX86Eperm, BaselinePolicy) {
   errno = 0;
   pid_t pid = ForkX86Glibc();
   const int fork_errno = errno;
-  HandlePostForkReturn(pid);
+  TestUtils::HandlePostForkReturn(pid);
 
   BPF_ASSERT_EQ(-1, pid);
   BPF_ASSERT_EQ(EPERM, fork_errno);
 }
 
 pid_t ForkARMGlibc() {
-  return syscall(__NR_clone,
-                 CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD);
+  static pid_t ctid;
+  return sys_clone(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, nullptr,
+                   nullptr, &ctid, nullptr);
 }
 
 BPF_TEST_C(BaselinePolicy, ForkArmEperm, BaselinePolicy) {
   errno = 0;
   pid_t pid = ForkARMGlibc();
   const int fork_errno = errno;
-  HandlePostForkReturn(pid);
+  TestUtils::HandlePostForkReturn(pid);
 
   BPF_ASSERT_EQ(-1, pid);
   BPF_ASSERT_EQ(EPERM, fork_errno);
@@ -150,8 +134,8 @@ BPF_DEATH_TEST_C(BaselinePolicy,
                  DisallowedCloneFlagCrashes,
                  DEATH_SEGV_MESSAGE(GetCloneErrorMessageContentForTests()),
                  BaselinePolicy) {
-  pid_t pid = syscall(__NR_clone, CLONE_THREAD | SIGCHLD);
-  HandlePostForkReturn(pid);
+  pid_t pid = sys_clone(CLONE_THREAD | SIGCHLD);
+  TestUtils::HandlePostForkReturn(pid);
 }
 
 BPF_DEATH_TEST_C(BaselinePolicy,
--- a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -17,6 +17,7 @@
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #include "sandbox/linux/seccomp-bpf/syscall.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 
 #if defined(__mips__)
 // __NR_Linux, is defined in <asm/unistd.h>.
@@ -223,7 +224,7 @@ intptr_t SIGSYSSchedHandler(const struct
     case __NR_sched_setattr:
     case __NR_sched_setparam:
     case __NR_sched_setscheduler:
-      const pid_t tid = syscall(__NR_gettid);
+      const pid_t tid = sys_gettid();
       // The first argument is the pid.  If is our thread id, then replace it
       // with 0, which is equivalent and allowed by the policy.
       if (args.args[0] == static_cast<uint64_t>(tid)) {
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -40,6 +40,16 @@
 #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
 #endif
 
+// https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/private/bionic_prctl.h
+#if !defined(PR_SET_VMA)
+#define PR_SET_VMA 0x53564d41
+#endif
+
+// https://android.googlesource.com/platform/system/core/+/lollipop-release/libcutils/sched_policy.c
+#if !defined(PR_SET_TIMERSLACK_PID)
+#define PR_SET_TIMERSLACK_PID 41
+#endif
+
 #endif  // defined(OS_ANDROID)
 
 #if defined(__arm__) && !defined(MAP_STACK)
@@ -129,6 +139,9 @@ ResultExpr RestrictPrctl() {
   return Switch(option)
       .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE),
              Allow())
+#if defined(OS_ANDROID)
+      .CASES((PR_SET_VMA, PR_SET_TIMERSLACK_PID), Allow())
+#endif
       .Default(CrashSIGSYSPrctl());
 }
 
@@ -251,7 +264,7 @@ ResultExpr RestrictGetSetpriority(pid_t
 }
 
 ResultExpr RestrictClockID() {
-  COMPILE_ASSERT(4 == sizeof(clockid_t), clockid_is_not_32bit);
+  static_assert(4 == sizeof(clockid_t), "clockid_t is not 32bit");
   const Arg<clockid_t> clockid(0);
   return If(
 #if defined(OS_CHROMEOS)
@@ -286,5 +299,9 @@ ResultExpr RestrictSchedTarget(pid_t tar
   }
 }
 
+ResultExpr RestrictPrlimit64(pid_t target_pid) {
+  const Arg<pid_t> pid(0);
+  return If(pid == 0 || pid == target_pid, Allow()).Else(CrashSIGSYS());
+}
 
 }  // namespace sandbox.
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -87,6 +87,10 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr Restr
 SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSchedTarget(pid_t target_pid,
                                                        int sysno);
 
+// Restricts the |pid| argument of prlimit64 to 0 (meaning the calling process)
+// or target_pid.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimit64(pid_t target_pid);
+
 }  // namespace sandbox.
 
 #endif  // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -6,7 +6,9 @@
 
 #include <errno.h>
 #include <sched.h>
+#include <sys/resource.h>
 #include <sys/syscall.h>
+#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -23,6 +25,7 @@
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #include "sandbox/linux/seccomp-bpf/syscall.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 #include "sandbox/linux/tests/unit_tests.h"
 
 #if !defined(OS_ANDROID)
@@ -163,7 +166,7 @@ void CheckSchedGetParam(pid_t pid, struc
 
 void SchedGetParamThread(base::WaitableEvent* thread_run) {
   const pid_t pid = getpid();
-  const pid_t tid = syscall(__NR_gettid);
+  const pid_t tid = sys_gettid();
   BPF_ASSERT_NE(pid, tid);
 
   struct sched_param current_pid_param;
@@ -208,6 +211,35 @@ BPF_DEATH_TEST_C(ParameterRestrictions,
   sched_getparam(kInitPID, &param);
 }
 
+class RestrictPrlimit64Policy : public bpf_dsl::Policy {
+ public:
+  RestrictPrlimit64Policy() {}
+  ~RestrictPrlimit64Policy() override {}
+
+  ResultExpr EvaluateSyscall(int sysno) const override {
+    switch (sysno) {
+      case __NR_prlimit64:
+        return RestrictPrlimit64(getpid());
+      default:
+        return Allow();
+    }
+  }
+};
+
+BPF_TEST_C(ParameterRestrictions, prlimit64_allowed, RestrictPrlimit64Policy) {
+  BPF_ASSERT_EQ(0, sys_prlimit64(0, RLIMIT_AS, NULL, NULL));
+  BPF_ASSERT_EQ(0, sys_prlimit64(getpid(), RLIMIT_AS, NULL, NULL));
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+                 prlimit64_crash_not_self,
+                 DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+                 RestrictPrlimit64Policy) {
+  const pid_t kInitPID = 1;
+  BPF_ASSERT_NE(kInitPID, getpid());
+  sys_prlimit64(kInitPID, RLIMIT_AS, NULL, NULL);
+}
+
 }  // namespace
 
 }  // namespace sandbox
--- a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
@@ -17,6 +17,7 @@
 #include "sandbox/linux/bpf_dsl/policy.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 #include "sandbox/linux/tests/unit_tests.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -45,9 +46,9 @@ class EmptyClassTakingPolicy : public bp
     BPF_ASSERT(fourty_two);
     BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
   }
-  virtual ~EmptyClassTakingPolicy() {}
+  ~EmptyClassTakingPolicy() override {}
 
-  virtual ResultExpr EvaluateSyscall(int sysno) const override {
+  ResultExpr EvaluateSyscall(int sysno) const override {
     DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
     return Allow();
   }
@@ -82,21 +83,19 @@ TEST(BPFTest, BPFTesterCompatibilityDele
 
 class EnosysPtracePolicy : public bpf_dsl::Policy {
  public:
-  EnosysPtracePolicy() {
-    my_pid_ = syscall(__NR_getpid);
-  }
-  virtual ~EnosysPtracePolicy() {
+  EnosysPtracePolicy() { my_pid_ = sys_getpid(); }
+  ~EnosysPtracePolicy() override {
     // Policies should be able to bind with the process on which they are
     // created. They should never be created in a parent process.
-    BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid));
+    BPF_ASSERT_EQ(my_pid_, sys_getpid());
   }
 
-  virtual ResultExpr EvaluateSyscall(int system_call_number) const override {
+  ResultExpr EvaluateSyscall(int system_call_number) const override {
     CHECK(SandboxBPF::IsValidSyscallNumber(system_call_number));
     if (system_call_number == __NR_ptrace) {
       // The EvaluateSyscall function should run in the process that created
       // the current object.
-      BPF_ASSERT_EQ(my_pid_, syscall(__NR_getpid));
+      BPF_ASSERT_EQ(my_pid_, sys_getpid());
       return Error(ENOSYS);
     } else {
       return Allow();
@@ -111,12 +110,12 @@ class EnosysPtracePolicy : public bpf_ds
 class BasicBPFTesterDelegate : public BPFTesterDelegate {
  public:
   BasicBPFTesterDelegate() {}
-  virtual ~BasicBPFTesterDelegate() {}
+  ~BasicBPFTesterDelegate() override {}
 
-  virtual scoped_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+  scoped_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
     return scoped_ptr<bpf_dsl::Policy>(new EnosysPtracePolicy());
   }
-  virtual void RunTestFunction() override {
+  void RunTestFunction() override {
     errno = 0;
     int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
     BPF_ASSERT(-1 == ret);
--- a/sandbox/linux/seccomp-bpf/codegen.cc
+++ b/sandbox/linux/seccomp-bpf/codegen.cc
@@ -6,604 +6,161 @@
 
 #include <linux/filter.h>
 
-#include <set>
+#include <limits>
+#include <utility>
 
 #include "base/logging.h"
-#include "sandbox/linux/seccomp-bpf/basicblock.h"
-#include "sandbox/linux/seccomp-bpf/die.h"
-#include "sandbox/linux/seccomp-bpf/instruction.h"
 
-namespace sandbox {
-
-CodeGen::CodeGen() : compiled_(false) {}
-
-CodeGen::~CodeGen() {
-  for (Instructions::iterator iter = instructions_.begin();
-       iter != instructions_.end();
-       ++iter) {
-    delete *iter;
-  }
-  for (BasicBlocks::iterator iter = basic_blocks_.begin();
-       iter != basic_blocks_.end();
-       ++iter) {
-    delete *iter;
-  }
-}
+// This CodeGen implementation strives for simplicity while still
+// generating acceptable BPF programs under typical usage patterns
+// (e.g., by PolicyCompiler).
+//
+// The key to its simplicity is that BPF programs only support forward
+// jumps/branches, which allows constraining the DAG construction API
+// to make instruction nodes immutable. Immutable nodes admits a
+// simple greedy approach of emitting new instructions as needed and
+// then reusing existing ones that have already been emitted. This
+// cleanly avoids any need to compute basic blocks or apply
+// topological sorting because the API effectively sorts instructions
+// for us (e.g., before MakeInstruction() can be called to emit a
+// branch instruction, it must have already been called for each
+// branch path).
+//
+// This greedy algorithm is not without (theoretical) weakness though:
+//
+//   1. In the general case, we don't eliminate dead code.  If needed,
+//      we could trace back through the program in Compile() and elide
+//      any unneeded instructions, but in practice we only emit live
+//      instructions anyway.
+//
+//   2. By not dividing instructions into basic blocks and sorting, we
+//      lose an opportunity to move non-branch/non-return instructions
+//      adjacent to their successor instructions, which means we might
+//      need to emit additional jumps. But in practice, they'll
+//      already be nearby as long as callers don't go out of their way
+//      to interleave MakeInstruction() calls for unrelated code
+//      sequences.
 
-Instruction* CodeGen::MakeInstruction(uint16_t code,
-                                      uint32_t k,
-                                      Instruction* next) {
-  // We can handle non-jumping instructions and "always" jumps. Both of
-  // them are followed by exactly one "next" instruction.
-  // We allow callers to defer specifying "next", but then they must call
-  // "joinInstructions" later.
-  if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) {
-    SANDBOX_DIE(
-        "Must provide both \"true\" and \"false\" branch "
-        "for a BPF_JMP");
-  }
-  if (next && BPF_CLASS(code) == BPF_RET) {
-    SANDBOX_DIE("Cannot append instructions after a return statement");
-  }
-  if (BPF_CLASS(code) == BPF_JMP) {
-    // "Always" jumps use the "true" branch target, only.
-    Instruction* insn = new Instruction(code, 0, next, NULL);
-    instructions_.push_back(insn);
-    return insn;
-  } else {
-    // Non-jumping instructions do not use any of the branch targets.
-    Instruction* insn = new Instruction(code, k, next);
-    instructions_.push_back(insn);
-    return insn;
-  }
-}
-
-Instruction* CodeGen::MakeInstruction(uint16_t code,
-                                      uint32_t k,
-                                      Instruction* jt,
-                                      Instruction* jf) {
-  // We can handle all conditional jumps. They are followed by both a
-  // "true" and a "false" branch.
-  if (BPF_CLASS(code) != BPF_JMP || BPF_OP(code) == BPF_JA) {
-    SANDBOX_DIE("Expected a BPF_JMP instruction");
-  }
-  if (!jt || !jf) {
-    SANDBOX_DIE("Branches must jump to a valid instruction");
-  }
-  Instruction* insn = new Instruction(code, k, jt, jf);
-  instructions_.push_back(insn);
-  return insn;
-}
+namespace sandbox {
 
-void CodeGen::FindBranchTargets(const Instruction& instructions,
-                                BranchTargets* branch_targets) {
-  // Follow all possible paths through the "instructions" graph and compute
-  // a list of branch targets. This will later be needed to compute the
-  // boundaries of basic blocks.
-  // We maintain a set of all instructions that we have previously seen. This
-  // set ultimately converges on all instructions in the program.
-  std::set<const Instruction*> seen_instructions;
-  Instructions stack;
-  for (const Instruction* insn = &instructions; insn;) {
-    seen_instructions.insert(insn);
-    if (BPF_CLASS(insn->code) == BPF_JMP) {
-      // Found a jump. Increase count of incoming edges for each of the jump
-      // targets.
-      ++(*branch_targets)[insn->jt_ptr];
-      if (BPF_OP(insn->code) != BPF_JA) {
-        ++(*branch_targets)[insn->jf_ptr];
-        stack.push_back(const_cast<Instruction*>(insn));
-      }
-      // Start a recursive decent for depth-first traversal.
-      if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
-        // We haven't seen the "true" branch yet. Traverse it now. We have
-        // already remembered the "false" branch on the stack and will
-        // traverse it later.
-        insn = insn->jt_ptr;
-        continue;
-      } else {
-        // Now try traversing the "false" branch.
-        insn = NULL;
-      }
-    } else {
-      // This is a non-jump instruction, just continue to the next instruction
-      // (if any). It's OK if "insn" becomes NULL when reaching a return
-      // instruction.
-      if (!insn->next != (BPF_CLASS(insn->code) == BPF_RET)) {
-        SANDBOX_DIE(
-            "Internal compiler error; return instruction must be at "
-            "the end of the BPF program");
-      }
-      if (seen_instructions.find(insn->next) == seen_instructions.end()) {
-        insn = insn->next;
-      } else {
-        // We have seen this instruction before. That could happen if it is
-        // a branch target. No need to continue processing.
-        insn = NULL;
-      }
-    }
-    while (!insn && !stack.empty()) {
-      // We are done processing all the way to a leaf node, backtrack up the
-      // stack to any branches that we haven't processed yet. By definition,
-      // this has to be a "false" branch, as we always process the "true"
-      // branches right away.
-      insn = stack.back();
-      stack.pop_back();
-      if (seen_instructions.find(insn->jf_ptr) == seen_instructions.end()) {
-        // We haven't seen the "false" branch yet. So, that's where we'll
-        // go now.
-        insn = insn->jf_ptr;
-      } else {
-        // We have seen both the "true" and the "false" branch, continue
-        // up the stack.
-        if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
-          SANDBOX_DIE(
-              "Internal compiler error; cannot find all "
-              "branch targets");
-        }
-        insn = NULL;
-      }
-    }
-  }
-  return;
-}
+// kBranchRange is the maximum value that can be stored in
+// sock_filter's 8-bit jt and jf fields.
+const size_t kBranchRange = std::numeric_limits<uint8_t>::max();
 
-BasicBlock* CodeGen::MakeBasicBlock(Instruction* head, Instruction* tail) {
-  // Iterate over all the instructions between "head" and "tail" and
-  // insert them into a new basic block.
-  BasicBlock* bb = new BasicBlock;
-  for (;; head = head->next) {
-    bb->instructions.push_back(head);
-    if (head == tail) {
-      break;
-    }
-    if (BPF_CLASS(head->code) == BPF_JMP) {
-      SANDBOX_DIE("Found a jump inside of a basic block");
-    }
-  }
-  basic_blocks_.push_back(bb);
-  return bb;
-}
+const CodeGen::Node CodeGen::kNullNode;
 
-void CodeGen::AddBasicBlock(Instruction* head,
-                            Instruction* tail,
-                            const BranchTargets& branch_targets,
-                            TargetsToBlocks* basic_blocks,
-                            BasicBlock** firstBlock) {
-  // Add a new basic block to "basic_blocks". Also set "firstBlock", if it
-  // has not been set before.
-  BranchTargets::const_iterator iter = branch_targets.find(head);
-  if ((iter == branch_targets.end()) != !*firstBlock ||
-      !*firstBlock != basic_blocks->empty()) {
-    SANDBOX_DIE(
-        "Only the very first basic block should have no "
-        "incoming jumps");
-  }
-  BasicBlock* bb = MakeBasicBlock(head, tail);
-  if (!*firstBlock) {
-    *firstBlock = bb;
-  }
-  (*basic_blocks)[head] = bb;
-  return;
+CodeGen::CodeGen() : program_(), equivalent_(), memos_() {
 }
 
-BasicBlock* CodeGen::CutGraphIntoBasicBlocks(
-    Instruction* instructions,
-    const BranchTargets& branch_targets,
-    TargetsToBlocks* basic_blocks) {
-  // Textbook implementation of a basic block generator. All basic blocks
-  // start with a branch target and end with either a return statement or
-  // a jump (or are followed by an instruction that forms the beginning of a
-  // new block). Both conditional and "always" jumps are supported.
-  BasicBlock* first_block = NULL;
-  std::set<const Instruction*> seen_instructions;
-  Instructions stack;
-  Instruction* tail = NULL;
-  Instruction* head = instructions;
-  for (Instruction* insn = head; insn;) {
-    if (seen_instructions.find(insn) != seen_instructions.end()) {
-      // We somehow went in a circle. This should never be possible. Not even
-      // cyclic graphs are supposed to confuse us this much.
-      SANDBOX_DIE("Internal compiler error; cannot compute basic blocks");
-    }
-    seen_instructions.insert(insn);
-    if (tail && branch_targets.find(insn) != branch_targets.end()) {
-      // We reached a branch target. Start a new basic block (this means,
-      // flushing the previous basic block first).
-      AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block);
-      head = insn;
-    }
-    if (BPF_CLASS(insn->code) == BPF_JMP) {
-      // We reached a jump instruction, this completes our current basic
-      // block. Flush it and continue by traversing both the true and the
-      // false branch of the jump. We need to maintain a stack to do so.
-      AddBasicBlock(head, insn, branch_targets, basic_blocks, &first_block);
-      if (BPF_OP(insn->code) != BPF_JA) {
-        stack.push_back(insn->jf_ptr);
-      }
-      insn = insn->jt_ptr;
-
-      // If we are jumping to an instruction that we have previously
-      // processed, we are done with this branch. Continue by backtracking
-      // up the stack.
-      while (seen_instructions.find(insn) != seen_instructions.end()) {
-      backtracking:
-        if (stack.empty()) {
-          // We successfully traversed all reachable instructions.
-          return first_block;
-        } else {
-          // Going up the stack.
-          insn = stack.back();
-          stack.pop_back();
-        }
-      }
-      // Starting a new basic block.
-      tail = NULL;
-      head = insn;
-    } else {
-      // We found a non-jumping instruction, append it to current basic
-      // block.
-      tail = insn;
-      insn = insn->next;
-      if (!insn) {
-        // We reached a return statement, flush the current basic block and
-        // backtrack up the stack.
-        AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block);
-        goto backtracking;
-      }
-    }
-  }
-  return first_block;
+CodeGen::~CodeGen() {
 }
 
-// We define a comparator that inspects the sequence of instructions in our
-// basic block and any blocks referenced by this block. This function can be
-// used in a "less" comparator for the purpose of storing pointers to basic
-// blocks in STL containers; this gives an easy option to use STL to find
-// shared tail  sequences of basic blocks.
-static int PointerCompare(const BasicBlock* block1,
-                          const BasicBlock* block2,
-                          const TargetsToBlocks& blocks) {
-  // Return <0, 0, or >0 depending on the ordering of "block1" and "block2".
-  // If we are looking at the exact same block, this is trivial and we don't
-  // need to do a full comparison.
-  if (block1 == block2) {
-    return 0;
-  }
+void CodeGen::Compile(CodeGen::Node head, Program* out) {
+  DCHECK(out);
+  out->assign(program_.rbegin() + Offset(head), program_.rend());
+}
+
+CodeGen::Node CodeGen::MakeInstruction(uint16_t code,
+                                       uint32_t k,
+                                       Node jt,
+                                       Node jf) {
+  // To avoid generating redundant code sequences, we memoize the
+  // results from AppendInstruction().
+  auto res = memos_.insert(std::make_pair(MemoKey(code, k, jt, jf), kNullNode));
+  CodeGen::Node* node = &res.first->second;
+  if (res.second) {  // Newly inserted memo entry.
+    *node = AppendInstruction(code, k, jt, jf);
+  }
+  return *node;
+}
+
+CodeGen::Node CodeGen::AppendInstruction(uint16_t code,
+                                         uint32_t k,
+                                         Node jt,
+                                         Node jf) {
+  if (BPF_CLASS(code) == BPF_JMP) {
+    CHECK_NE(BPF_JA, BPF_OP(code)) << "CodeGen inserts JAs as needed";
 
-  // We compare the sequence of instructions in both basic blocks.
-  const Instructions& insns1 = block1->instructions;
-  const Instructions& insns2 = block2->instructions;
-  // Basic blocks should never be empty.
-  CHECK(!insns1.empty());
-  CHECK(!insns2.empty());
-
-  Instructions::const_iterator iter1 = insns1.begin();
-  Instructions::const_iterator iter2 = insns2.begin();
-  for (;; ++iter1, ++iter2) {
-    // If we have reached the end of the sequence of instructions in one or
-    // both basic blocks, we know the relative ordering between the two blocks
-    // and can return.
-    if (iter1 == insns1.end() || iter2 == insns2.end()) {
-      if (iter1 != insns1.end()) {
-        return 1;
-      }
-      if (iter2 != insns2.end()) {
-        return -1;
-      }
-
-      // If the two blocks are the same length (and have elementwise-equal code
-      // and k fields) and their last instructions are neither a JMP nor a RET
-      // (which is the only way we can reach this point), then we must compare
-      // their successors.
-      Instruction* const insns1_last = insns1.back();
-      Instruction* const insns2_last = insns2.back();
-      CHECK(BPF_CLASS(insns1_last->code) != BPF_JMP &&
-            BPF_CLASS(insns1_last->code) != BPF_RET);
-
-      // Non jumping instructions will always have a valid next instruction.
-      CHECK(insns1_last->next);
-      CHECK(insns2_last->next);
-      return PointerCompare(blocks.find(insns1_last->next)->second,
-                            blocks.find(insns2_last->next)->second,
-                            blocks);
-    }
-
-    // Compare the individual fields for both instructions.
-    const Instruction& insn1 = **iter1;
-    const Instruction& insn2 = **iter2;
-    if (insn1.code != insn2.code) {
-      return insn1.code - insn2.code;
-    }
-    if (insn1.k != insn2.k) {
-      return insn1.k - insn2.k;
-    }
-
-    // Sanity check: If we're looking at a JMP or RET instruction, by definition
-    // it should be the last instruction of the basic block.
-    if (BPF_CLASS(insn1.code) == BPF_JMP || BPF_CLASS(insn1.code) == BPF_RET) {
-      CHECK_EQ(insns1.back(), &insn1);
-      CHECK_EQ(insns2.back(), &insn2);
-    }
-
-    // RET instructions terminate execution, and only JMP instructions use the
-    // jt_ptr and jf_ptr fields.  Anything else can continue to the next
-    // instruction in the basic block.
-    if (BPF_CLASS(insn1.code) == BPF_RET) {
-      return 0;
-    } else if (BPF_CLASS(insn1.code) != BPF_JMP) {
-      continue;
-    }
-
-    // Recursively compare the "true" and "false" branches.
-    // A well-formed BPF program can't have any cycles, so we know
-    // that our recursive algorithm will ultimately terminate.
-    // In the unlikely event that the programmer made a mistake and
-    // went out of the way to give us a cyclic program, we will crash
-    // with a stack overflow. We are OK with that.
-    if (BPF_OP(insn1.code) != BPF_JA) {
-      int c = PointerCompare(blocks.find(insn1.jf_ptr)->second,
-                             blocks.find(insn2.jf_ptr)->second,
-                             blocks);
-      if (c != 0) {
-        return c;
-      }
-    }
-    return PointerCompare(blocks.find(insn1.jt_ptr)->second,
-                          blocks.find(insn2.jt_ptr)->second,
-                          blocks);
+    // Optimally adding jumps is rather tricky, so we use a quick
+    // approximation: by artificially reducing |jt|'s range, |jt| will
+    // stay within its true range even if we add a jump for |jf|.
+    jt = WithinRange(jt, kBranchRange - 1);
+    jf = WithinRange(jf, kBranchRange);
+    return Append(code, k, Offset(jt), Offset(jf));
   }
-}
 
-void CodeGen::MergeTails(TargetsToBlocks* blocks) {
-  // We enter all of our basic blocks into a set using the BasicBlock::Less()
-  // comparator. This naturally results in blocks with identical tails of
-  // instructions to map to the same entry in the set. Whenever we discover
-  // that a particular chain of instructions is already in the set, we merge
-  // the basic blocks and update the pointer in the "blocks" map.
-  // Returns the number of unique basic blocks.
-  // N.B. We don't merge instructions on a granularity that is finer than
-  //      a basic block. In practice, this is sufficiently rare that we don't
-  //      incur a big cost.
-  //      Similarly, we currently don't merge anything other than tails. In
-  //      the future, we might decide to revisit this decision and attempt to
-  //      merge arbitrary sub-sequences of instructions.
-  BasicBlock::Less<TargetsToBlocks> less(*blocks, PointerCompare);
-  typedef std::set<BasicBlock*, BasicBlock::Less<TargetsToBlocks> > Set;
-  Set seen_basic_blocks(less);
-  for (TargetsToBlocks::iterator iter = blocks->begin(); iter != blocks->end();
-       ++iter) {
-    BasicBlock* bb = iter->second;
-    Set::const_iterator entry = seen_basic_blocks.find(bb);
-    if (entry == seen_basic_blocks.end()) {
-      // This is the first time we see this particular sequence of
-      // instructions. Enter the basic block into the set of known
-      // basic blocks.
-      seen_basic_blocks.insert(bb);
-    } else {
-      // We have previously seen another basic block that defines the same
-      // sequence of instructions. Merge the two blocks and update the
-      // pointer in the "blocks" map.
-      iter->second = *entry;
-    }
+  CHECK_EQ(kNullNode, jf) << "Non-branch instructions shouldn't provide jf";
+  if (BPF_CLASS(code) == BPF_RET) {
+    CHECK_EQ(kNullNode, jt) << "Return instructions shouldn't provide jt";
+  } else {
+    // For non-branch/non-return instructions, execution always
+    // proceeds to the next instruction; so we need to arrange for
+    // that to be |jt|.
+    jt = WithinRange(jt, 0);
+    CHECK_EQ(0U, Offset(jt)) << "ICE: Failed to setup next instruction";
   }
+  return Append(code, k, 0, 0);
 }
 
-void CodeGen::ComputeIncomingBranches(BasicBlock* block,
-                                      const TargetsToBlocks& targets_to_blocks,
-                                      IncomingBranches* incoming_branches) {
-  // We increment the number of incoming branches each time we encounter a
-  // basic block. But we only traverse recursively the very first time we
-  // encounter a new block. This is necessary to make topological sorting
-  // work correctly.
-  if (++(*incoming_branches)[block] == 1) {
-    Instruction* last_insn = block->instructions.back();
-    if (BPF_CLASS(last_insn->code) == BPF_JMP) {
-      ComputeIncomingBranches(targets_to_blocks.find(last_insn->jt_ptr)->second,
-                              targets_to_blocks,
-                              incoming_branches);
-      if (BPF_OP(last_insn->code) != BPF_JA) {
-        ComputeIncomingBranches(
-            targets_to_blocks.find(last_insn->jf_ptr)->second,
-            targets_to_blocks,
-            incoming_branches);
-      }
-    } else if (BPF_CLASS(last_insn->code) != BPF_RET) {
-      ComputeIncomingBranches(targets_to_blocks.find(last_insn->next)->second,
-                              targets_to_blocks,
-                              incoming_branches);
-    }
+CodeGen::Node CodeGen::WithinRange(Node target, size_t range) {
+  // Just use |target| if it's already within range.
+  if (Offset(target) <= range) {
+    return target;
   }
-}
 
-void CodeGen::TopoSortBasicBlocks(BasicBlock* first_block,
-                                  const TargetsToBlocks& blocks,
-                                  BasicBlocks* basic_blocks) {
-  // Textbook implementation of a toposort. We keep looking for basic blocks
-  // that don't have any incoming branches (initially, this is just the
-  // "first_block") and add them to the topologically sorted list of
-  // "basic_blocks". As we do so, we remove outgoing branches. This potentially
-  // ends up making our descendants eligible for the sorted list. The
-  // sorting algorithm terminates when there are no more basic blocks that have
-  // no incoming branches. If we didn't move all blocks from the set of
-  // "unordered_blocks" to the sorted list of "basic_blocks", there must have
-  // been a cyclic dependency. This should never happen in a BPF program, as
-  // well-formed BPF programs only ever have forward branches.
-  IncomingBranches unordered_blocks;
-  ComputeIncomingBranches(first_block, blocks, &unordered_blocks);
-
-  std::set<BasicBlock*> heads;
-  for (;;) {
-    // Move block from "unordered_blocks" to "basic_blocks".
-    basic_blocks->push_back(first_block);
-
-    // Inspect last instruction in the basic block. This is typically either a
-    // jump or a return statement. But it could also be a "normal" instruction
-    // that is followed by a jump target.
-    Instruction* last_insn = first_block->instructions.back();
-    if (BPF_CLASS(last_insn->code) == BPF_JMP) {
-      // Remove outgoing branches. This might end up moving our descendants
-      // into set of "head" nodes that no longer have any incoming branches.
-      TargetsToBlocks::const_iterator iter;
-      if (BPF_OP(last_insn->code) != BPF_JA) {
-        iter = blocks.find(last_insn->jf_ptr);
-        if (!--unordered_blocks[iter->second]) {
-          heads.insert(iter->second);
-        }
-      }
-      iter = blocks.find(last_insn->jt_ptr);
-      if (!--unordered_blocks[iter->second]) {
-        first_block = iter->second;
-        continue;
-      }
-    } else if (BPF_CLASS(last_insn->code) != BPF_RET) {
-      // We encountered an instruction that doesn't change code flow. Try to
-      // pick the next "first_block" from "last_insn->next", if possible.
-      TargetsToBlocks::const_iterator iter;
-      iter = blocks.find(last_insn->next);
-      if (!--unordered_blocks[iter->second]) {
-        first_block = iter->second;
-        continue;
-      } else {
-        // Our basic block is supposed to be followed by "last_insn->next",
-        // but dependencies prevent this from happening. Insert a BPF_JA
-        // instruction to correct the code flow.
-        Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, last_insn->next);
-        first_block->instructions.push_back(ja);
-        last_insn->next = ja;
-      }
-    }
-    if (heads.empty()) {
-      if (unordered_blocks.size() != basic_blocks->size()) {
-        SANDBOX_DIE("Internal compiler error; cyclic graph detected");
-      }
-      return;
-    }
-    // Proceed by picking an arbitrary node from the set of basic blocks that
-    // do not have any incoming branches.
-    first_block = *heads.begin();
-    heads.erase(heads.begin());
+  // Alternatively, look for an equivalent instruction within range.
+  if (Offset(equivalent_.at(target)) <= range) {
+    return equivalent_.at(target);
   }
-}
 
-void CodeGen::ComputeRelativeJumps(BasicBlocks* basic_blocks,
-                                   const TargetsToBlocks& targets_to_blocks) {
-  // While we previously used pointers in jt_ptr and jf_ptr to link jump
-  // instructions to their targets, we now convert these jumps to relative
-  // jumps that are suitable for loading the BPF program into the kernel.
-  int offset = 0;
-
-  // Since we just completed a toposort, all jump targets are guaranteed to
-  // go forward. This means, iterating over the basic blocks in reverse makes
-  // it trivial to compute the correct offsets.
-  BasicBlock* bb = NULL;
-  BasicBlock* last_bb = NULL;
-  for (BasicBlocks::reverse_iterator iter = basic_blocks->rbegin();
-       iter != basic_blocks->rend();
-       ++iter) {
-    last_bb = bb;
-    bb = *iter;
-    Instruction* insn = bb->instructions.back();
-    if (BPF_CLASS(insn->code) == BPF_JMP) {
-      // Basic block ended in a jump instruction. We can now compute the
-      // appropriate offsets.
-      if (BPF_OP(insn->code) == BPF_JA) {
-        // "Always" jumps use the 32bit "k" field for the offset, instead
-        // of the 8bit "jt" and "jf" fields.
-        int jmp = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
-        insn->k = jmp;
-        insn->jt = insn->jf = 0;
-      } else {
-        // The offset computations for conditional jumps are just the same
-        // as for "always" jumps.
-        int jt = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
-        int jf = offset - targets_to_blocks.find(insn->jf_ptr)->second->offset;
-
-        // There is an added complication, because conditional relative jumps
-        // can only jump at most 255 instructions forward. If we have to jump
-        // further, insert an extra "always" jump.
-        Instructions::size_type jmp = bb->instructions.size();
-        if (jt > 255 || (jt == 255 && jf > 255)) {
-          Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jt_ptr);
-          bb->instructions.push_back(ja);
-          ja->k = jt;
-          ja->jt = ja->jf = 0;
-
-          // The newly inserted "always" jump, of course, requires us to adjust
-          // the jump targets in the original conditional jump.
-          jt = 0;
-          ++jf;
-        }
-        if (jf > 255) {
-          Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jf_ptr);
-          bb->instructions.insert(bb->instructions.begin() + jmp, ja);
-          ja->k = jf;
-          ja->jt = ja->jf = 0;
-
-          // Again, we have to adjust the jump targets in the original
-          // conditional jump.
-          ++jt;
-          jf = 0;
-        }
-
-        // Now we can finally set the relative jump targets in the conditional
-        // jump instruction. Afterwards, we must no longer access the jt_ptr
-        // and jf_ptr fields.
-        insn->jt = jt;
-        insn->jf = jf;
-      }
-    } else if (BPF_CLASS(insn->code) != BPF_RET &&
-               targets_to_blocks.find(insn->next)->second != last_bb) {
-      SANDBOX_DIE("Internal compiler error; invalid basic block encountered");
-    }
-
-    // Proceed to next basic block.
-    offset += bb->instructions.size();
-    bb->offset = offset;
-  }
-  return;
+  // Otherwise, fall back to emitting a jump instruction.
+  Node jump = Append(BPF_JMP | BPF_JA, Offset(target), 0, 0);
+  equivalent_.at(target) = jump;
+  return jump;
 }
 
-void CodeGen::ConcatenateBasicBlocks(const BasicBlocks& basic_blocks,
-                                     Program* program) {
-  // Our basic blocks have been sorted and relative jump offsets have been
-  // computed. The last remaining step is for all the instructions in our
-  // basic blocks to be concatenated into a BPF program.
-  program->clear();
-  for (BasicBlocks::const_iterator bb_iter = basic_blocks.begin();
-       bb_iter != basic_blocks.end();
-       ++bb_iter) {
-    const BasicBlock& bb = **bb_iter;
-    for (Instructions::const_iterator insn_iter = bb.instructions.begin();
-         insn_iter != bb.instructions.end();
-         ++insn_iter) {
-      const Instruction& insn = **insn_iter;
-      program->push_back(
-          (struct sock_filter) {insn.code, insn.jt, insn.jf, insn.k});
-    }
+CodeGen::Node CodeGen::Append(uint16_t code, uint32_t k, size_t jt, size_t jf) {
+  if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) {
+    CHECK_LE(jt, kBranchRange);
+    CHECK_LE(jf, kBranchRange);
+  } else {
+    CHECK_EQ(0U, jt);
+    CHECK_EQ(0U, jf);
   }
-  return;
-}
 
-void CodeGen::Compile(Instruction* instructions, Program* program) {
-  if (compiled_) {
-    SANDBOX_DIE(
-        "Cannot call Compile() multiple times. Create a new code "
-        "generator instead");
-  }
-  compiled_ = true;
+  CHECK_LT(program_.size(), static_cast<size_t>(BPF_MAXINSNS));
+  CHECK_EQ(program_.size(), equivalent_.size());
 
-  BranchTargets branch_targets;
-  FindBranchTargets(*instructions, &branch_targets);
-  TargetsToBlocks all_blocks;
-  BasicBlock* first_block =
-      CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks);
-  MergeTails(&all_blocks);
-  BasicBlocks basic_blocks;
-  TopoSortBasicBlocks(first_block, all_blocks, &basic_blocks);
-  ComputeRelativeJumps(&basic_blocks, all_blocks);
-  ConcatenateBasicBlocks(basic_blocks, program);
-  return;
+  Node res = program_.size();
+  program_.push_back(sock_filter{code, jt, jf, k});
+  equivalent_.push_back(res);
+  return res;
+}
+
+size_t CodeGen::Offset(Node target) const {
+  CHECK_LT(target, program_.size()) << "Bogus offset target node";
+  return (program_.size() - 1) - target;
+}
+
+// TODO(mdempsky): Move into a general base::Tuple helper library.
+bool CodeGen::MemoKeyLess::operator()(const MemoKey& lhs,
+                                      const MemoKey& rhs) const {
+#if 0
+  if (get<0>(lhs) != get<0>(rhs))
+    return get<0>(lhs) < get<0>(rhs);
+  if (get<1>(lhs) != get<1>(rhs))
+    return get<1>(lhs) < get<1>(rhs);
+  if (get<2>(lhs) != get<2>(rhs))
+    return get<2>(lhs) < get<2>(rhs);
+  if (get<3>(lhs) != get<3>(rhs))
+    return get<3>(lhs) < get<3>(rhs);
+#endif
+
+  if (lhs.a != rhs.a) { return lhs.a < rhs.a; }
+  if (lhs.b != rhs.b) { return lhs.b < rhs.b; }
+  if (lhs.c != rhs.c) { return lhs.c < rhs.c; }
+  if (lhs.d != rhs.d) { return lhs.d < rhs.d; }
+  return false;
 }
 
 }  // namespace sandbox
--- a/sandbox/linux/seccomp-bpf/codegen.h
+++ b/sandbox/linux/seccomp-bpf/codegen.h
@@ -5,36 +5,32 @@
 #ifndef SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
 #define SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
 
+#include <stddef.h>
 #include <stdint.h>
 
 #include <map>
 #include <vector>
 
+#include "base/macros.h"
+#include "base/tuple.h"
 #include "sandbox/sandbox_export.h"
 
 struct sock_filter;
 
 namespace sandbox {
-struct BasicBlock;
-struct Instruction;
 
-typedef std::vector<Instruction*> Instructions;
-typedef std::vector<BasicBlock*> BasicBlocks;
-typedef std::map<const Instruction*, int> BranchTargets;
-typedef std::map<const Instruction*, BasicBlock*> TargetsToBlocks;
-typedef std::map<const BasicBlock*, int> IncomingBranches;
-
-// The code generator instantiates a basic compiler that can convert a
-// graph of BPF instructions into a well-formed stream of BPF instructions.
-// Most notably, it ensures that jumps are always forward and don't exceed
-// the limit of 255 instructions imposed by the instruction set.
+// The code generator implements a basic assembler that can convert a
+// graph of BPF instructions into a well-formed array of BPF
+// instructions. Most notably, it ensures that jumps are always
+// forward and don't exceed the limit of 255 instructions imposed by
+// the instruction set.
 //
-// Callers would typically create a new CodeGen object and then use it to
-// build a DAG of Instructions. They'll eventually call Compile() to convert
-// this DAG to a Program.
+// Callers would typically create a new CodeGen object and then use it
+// to build a DAG of instruction nodes. They'll eventually call
+// Compile() to convert this DAG to a Program.
 //
 //   CodeGen gen;
-//   Instruction *allow, *branch, *dag;
+//   CodeGen::Node allow, branch, dag;
 //
 //   allow =
 //     gen.MakeInstruction(BPF_RET+BPF_K,
@@ -60,86 +56,66 @@ class SANDBOX_EXPORT CodeGen {
   // program in the kernel.
   typedef std::vector<struct sock_filter> Program;
 
+  // Node represents a node within the instruction DAG being compiled.
+  using Node = Program::size_type;
+
+  // kNullNode represents the "null" node; i.e., the reserved node
+  // value guaranteed to not equal any actual nodes.
+  static const Node kNullNode = -1;
+
   CodeGen();
   ~CodeGen();
 
-  // Create a new instruction. Instructions form a DAG. The instruction objects
-  // are owned by the CodeGen object. They do not need to be explicitly
-  // deleted.
-  // For details on the possible parameters refer to <linux/filter.h>
-  Instruction* MakeInstruction(uint16_t code,
-                               uint32_t k,
-                               Instruction* next = nullptr);
-  Instruction* MakeInstruction(uint16_t code,
-                               uint32_t k,
-                               Instruction* jt,
-                               Instruction* jf);
-
-  // 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.
-  void Compile(Instruction* instructions, Program* program);
+  // MakeInstruction creates a node representing the specified
+  // instruction, or returns and existing equivalent node if one
+  // exists. For details on the possible parameters refer to
+  // https://www.kernel.org/doc/Documentation/networking/filter.txt.
+  // TODO(mdempsky): Reconsider using default arguments here.
+  Node MakeInstruction(uint16_t code,
+                       uint32_t k,
+                       Node jt = kNullNode,
+                       Node jf = kNullNode);
+
+  // Compile linearizes the instruction DAG rooted at |head| into a
+  // program that can be executed by a BPF virtual machine.
+  void Compile(Node head, Program* program);
 
  private:
-  friend class CodeGenUnittestHelper;
+  using MemoKey = Tuple4<uint16_t, uint32_t, Node, Node>;
+  struct MemoKeyLess {
+    bool operator()(const MemoKey& lhs, const MemoKey& rhs) const;
+  };
+
+  // AppendInstruction adds a new instruction, ensuring that |jt| and
+  // |jf| are within range as necessary for |code|.
+  Node AppendInstruction(uint16_t code, uint32_t k, Node jt, Node jf);
+
+  // WithinRange returns a node equivalent to |next| that is at most
+  // |range| instructions away from the (logical) beginning of the
+  // program.
+  Node WithinRange(Node next, size_t range);
+
+  // Append appends a new instruction to the physical end (i.e.,
+  // logical beginning) of |program_|.
+  Node Append(uint16_t code, uint32_t k, size_t jt, size_t jf);
+
+  // Offset returns how many instructions exist in |program_| after |target|.
+  size_t Offset(Node target) const;
+
+  // NOTE: program_ is the compiled program in *reverse*, so that
+  // indices remain stable as we add instructions.
+  Program program_;
+
+  // equivalent_ stores the most recent semantically-equivalent node for each
+  // instruction in program_. A node is defined as semantically-equivalent to N
+  // if it has the same instruction code and constant as N and its successor
+  // nodes (if any) are semantically-equivalent to N's successor nodes, or
+  // if it's an unconditional jump to a node semantically-equivalent to N.
+  std::vector<Node> equivalent_;
+
+  std::map<MemoKey, Node, MemoKeyLess> memos_;
 
-  // Find all the instructions that are the target of BPF_JMPs.
-  void FindBranchTargets(const Instruction& instructions,
-                         BranchTargets* branch_targets);
-
-  // Combine instructions between "head" and "tail" into a new basic block.
-  // Basic blocks are defined as sequences of instructions whose only branch
-  // target is the very first instruction; furthermore, any BPF_JMP or BPF_RET
-  // instruction must be at the very end of the basic block.
-  BasicBlock* MakeBasicBlock(Instruction* head, Instruction* tail);
-
-  // Creates a basic block and adds it to "basic_blocks"; sets "first_block"
-  // if it is still NULL.
-  void AddBasicBlock(Instruction* head,
-                     Instruction* tail,
-                     const BranchTargets& branch_targets,
-                     TargetsToBlocks* basic_blocks,
-                     BasicBlock** first_block);
-
-  // Cuts the DAG of instructions into basic blocks.
-  BasicBlock* CutGraphIntoBasicBlocks(Instruction* instructions,
-                                      const BranchTargets& branch_targets,
-                                      TargetsToBlocks* blocks);
-
-  // Find common tail sequences of basic blocks and coalesce them.
-  void MergeTails(TargetsToBlocks* blocks);
-
-  // For each basic block, compute the number of incoming branches.
-  void ComputeIncomingBranches(BasicBlock* block,
-                               const TargetsToBlocks& targets_to_blocks,
-                               IncomingBranches* incoming_branches);
-
-  // Topologically sort the basic blocks so that all jumps are forward jumps.
-  // This is a requirement for any well-formed BPF program.
-  void TopoSortBasicBlocks(BasicBlock* first_block,
-                           const TargetsToBlocks& blocks,
-                           BasicBlocks* basic_blocks);
-
-  // Convert jt_ptr_ and jf_ptr_ fields in BPF_JMP instructions to valid
-  // jt_ and jf_ jump offsets. This can result in BPF_JA instructions being
-  // inserted, if we need to jump over more than 256 instructions.
-  void ComputeRelativeJumps(BasicBlocks* basic_blocks,
-                            const TargetsToBlocks& targets_to_blocks);
-
-  // Concatenate instructions from all basic blocks into a BPF program that
-  // can be passed to the kernel.
-  void ConcatenateBasicBlocks(const BasicBlocks&, Program* program);
-
-  // We stick all instructions and basic blocks into pools that get destroyed
-  // when the CodeGen object is destroyed. This way, we neither need to worry
-  // about explicitly managing ownership, nor do we need to worry about using
-  // smart pointers in the presence of circular references.
-  Instructions instructions_;
-  BasicBlocks basic_blocks_;
-
-  // Compile() must only ever be called once as it makes destructive changes
-  // to the DAG.
-  bool compiled_;
+  DISALLOW_COPY_AND_ASSIGN(CodeGen);
 };
 
 }  // namespace sandbox
--- a/sandbox/linux/seccomp-bpf/codegen_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/codegen_unittest.cc
@@ -6,116 +6,207 @@
 
 #include <linux/filter.h>
 
-#include <set>
-#include <string>
+#include <map>
+#include <utility>
 #include <vector>
 
-#include "sandbox/linux/seccomp-bpf/basicblock.h"
-#include "sandbox/linux/seccomp-bpf/errorcode.h"
-#include "sandbox/linux/seccomp-bpf/instruction.h"
+#include "base/macros.h"
+#include "base/md5.h"
+#include "base/strings/string_piece.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace sandbox {
+namespace {
 
-// We want to access some of the private methods in the code generator. We
-// do so by defining a "friend" that makes these methods public for us.
-class CodeGenUnittestHelper : public CodeGen {
+// Hash provides an abstraction for building "hash trees" from BPF
+// control flow graphs, and efficiently identifying equivalent graphs.
+//
+// For simplicity, we use MD5, because base happens to provide a
+// convenient API for its use. However, any collision-resistant hash
+// should suffice.
+class Hash {
  public:
-  using CodeGen::CutGraphIntoBasicBlocks;
-  using CodeGen::FindBranchTargets;
-  using CodeGen::MergeTails;
-};
+  static const Hash kZero;
 
-namespace {
+  Hash() : digest_() {}
+
+  Hash(uint16_t code,
+       uint32_t k,
+       const Hash& jt = kZero,
+       const Hash& jf = kZero)
+      : digest_() {
+    base::MD5Context ctx;
+    base::MD5Init(&ctx);
+    HashValue(&ctx, code);
+    HashValue(&ctx, k);
+    HashValue(&ctx, jt);
+    HashValue(&ctx, jf);
+    base::MD5Final(&digest_, &ctx);
+  }
+
+  Hash(const Hash& hash) = default;
+  Hash& operator=(const Hash& rhs) = default;
+
+  friend bool operator==(const Hash& lhs, const Hash& rhs) {
+    return lhs.Base16() == rhs.Base16();
+  }
+  friend bool operator!=(const Hash& lhs, const Hash& rhs) {
+    return !(lhs == rhs);
+  }
 
-enum {
-  NO_FLAGS = 0x0000,
-  HAS_MERGEABLE_TAILS = 0x0001,
+ private:
+  template <typename T>
+  void HashValue(base::MD5Context* ctx, const T& value) {
+    base::MD5Update(ctx,
+                    base::StringPiece(reinterpret_cast<const char*>(&value),
+                                      sizeof(value)));
+  }
+
+  std::string Base16() const {
+    return MD5DigestToBase16(digest_);
+  }
+
+  base::MD5Digest digest_;
 };
 
-using ProgramTestFunc = void (*)(CodeGenUnittestHelper* gen,
-                                 Instruction* head,
-                                 int flags);
+const Hash Hash::kZero;
 
-class ProgramTest : public ::testing::TestWithParam<ProgramTestFunc> {
- protected:
-  ProgramTest() : gen_() {}
+// Sanity check that equality and inequality work on Hash as required.
+TEST(CodeGen, HashSanity) {
+  std::vector<Hash> hashes;
+
+  // Push a bunch of logically distinct hashes.
+  hashes.push_back(Hash::kZero);
+  for (int i = 0; i < 4; ++i) {
+    hashes.push_back(Hash(i & 1, i & 2));
+  }
+  for (int i = 0; i < 16; ++i) {
+    hashes.push_back(Hash(i & 1, i & 2, Hash(i & 4, i & 8)));
+  }
+  for (int i = 0; i < 64; ++i) {
+    hashes.push_back(
+        Hash(i & 1, i & 2, Hash(i & 4, i & 8), Hash(i & 16, i & 32)));
+  }
 
-  // RunTest runs the test function argument.  It should be called at
-  // the end of each program test case.
-  void RunTest(Instruction* head, int flags) { GetParam()(&gen_, head, flags); }
-
-  Instruction* MakeInstruction(uint16_t code,
-                               uint32_t k,
-                               Instruction* next = nullptr) {
-    Instruction* ret = gen_.MakeInstruction(code, k, next);
-    EXPECT_NE(nullptr, ret);
-    EXPECT_EQ(code, ret->code);
-    EXPECT_EQ(k, ret->k);
-    if (code == BPF_JMP + BPF_JA) {
-      // Annoying inconsistency.
-      EXPECT_EQ(nullptr, ret->next);
-      EXPECT_EQ(next, ret->jt_ptr);
-    } else {
-      EXPECT_EQ(next, ret->next);
-      EXPECT_EQ(nullptr, ret->jt_ptr);
+  for (const Hash& a : hashes) {
+    for (const Hash& b : hashes) {
+      // Hashes should equal themselves, but not equal all others.
+      if (&a == &b) {
+        EXPECT_EQ(a, b);
+      } else {
+        EXPECT_NE(a, b);
+      }
     }
-    EXPECT_EQ(nullptr, ret->jf_ptr);
-    return ret;
   }
+}
+
+// ProgramTest provides a fixture for writing compiling sample
+// programs with CodeGen and verifying the linearized output matches
+// the input DAG.
+class ProgramTest : public ::testing::Test {
+ protected:
+  ProgramTest() : gen_(), node_hashes_() {}
+
+  // MakeInstruction calls CodeGen::MakeInstruction() and associated
+  // the returned address with a hash of the instruction.
+  CodeGen::Node MakeInstruction(uint16_t code,
+                                uint32_t k,
+                                CodeGen::Node jt = CodeGen::kNullNode,
+                                CodeGen::Node jf = CodeGen::kNullNode) {
+    CodeGen::Node res = gen_.MakeInstruction(code, k, jt, jf);
+    EXPECT_NE(CodeGen::kNullNode, res);
+
+    Hash digest(code, k, Lookup(jt), Lookup(jf));
+    auto it = node_hashes_.insert(std::make_pair(res, digest));
+    EXPECT_EQ(digest, it.first->second);
+
+    return res;
+  }
+
+  // RunTest compiles the program and verifies that the output matches
+  // what is expected.  It should be called at the end of each program
+  // test case.
+  void RunTest(CodeGen::Node head) {
+    // Compile the program
+    CodeGen::Program program;
+    gen_.Compile(head, &program);
+
+    // Walk the program backwards, and compute the hash for each instruction.
+    std::vector<Hash> prog_hashes(program.size());
+    for (size_t i = program.size(); i > 0; --i) {
+      const sock_filter& insn = program.at(i - 1);
+      Hash& hash = prog_hashes.at(i - 1);
+
+      if (BPF_CLASS(insn.code) == BPF_JMP) {
+        if (BPF_OP(insn.code) == BPF_JA) {
+          // The compiler adds JA instructions as needed, so skip them.
+          hash = prog_hashes.at(i + insn.k);
+        } else {
+          hash = Hash(insn.code, insn.k, prog_hashes.at(i + insn.jt),
+                      prog_hashes.at(i + insn.jf));
+        }
+      } else if (BPF_CLASS(insn.code) == BPF_RET) {
+        hash = Hash(insn.code, insn.k);
+      } else {
+        hash = Hash(insn.code, insn.k, prog_hashes.at(i));
+      }
+    }
 
-  Instruction* MakeInstruction(uint16_t code,
-                               uint32_t k,
-                               Instruction* jt,
-                               Instruction* jf) {
-    Instruction* ret = gen_.MakeInstruction(code, k, jt, jf);
-    EXPECT_NE(nullptr, ret);
-    EXPECT_EQ(code, ret->code);
-    EXPECT_EQ(k, ret->k);
-    EXPECT_EQ(nullptr, ret->next);
-    EXPECT_EQ(jt, ret->jt_ptr);
-    EXPECT_EQ(jf, ret->jf_ptr);
-    return ret;
+    EXPECT_EQ(Lookup(head), prog_hashes.at(0));
   }
 
  private:
-  CodeGenUnittestHelper gen_;
+  const Hash& Lookup(CodeGen::Node next) const {
+    if (next == CodeGen::kNullNode) {
+      return Hash::kZero;
+    }
+    auto it = node_hashes_.find(next);
+    if (it == node_hashes_.end()) {
+      ADD_FAILURE() << "No hash found for node " << next;
+      return Hash::kZero;
+    }
+    return it->second;
+  }
+
+  CodeGen gen_;
+  std::map<CodeGen::Node, Hash> node_hashes_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProgramTest);
 };
 
-TEST_P(ProgramTest, OneInstruction) {
+TEST_F(ProgramTest, OneInstruction) {
   // Create the most basic valid BPF program:
   //    RET 0
-  Instruction* head = MakeInstruction(BPF_RET + BPF_K, 0);
-  RunTest(head, NO_FLAGS);
+  CodeGen::Node head = MakeInstruction(BPF_RET + BPF_K, 0);
+  RunTest(head);
 }
 
-TEST_P(ProgramTest, SimpleBranch) {
+TEST_F(ProgramTest, SimpleBranch) {
   // Create a program with a single branch:
   //    JUMP if eq 42 then $0 else $1
   // 0: RET 1
   // 1: RET 0
-  Instruction* head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
-                                      42,
-                                      MakeInstruction(BPF_RET + BPF_K, 1),
-                                      MakeInstruction(BPF_RET + BPF_K, 0));
-  RunTest(head, NO_FLAGS);
+  CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42,
+                                       MakeInstruction(BPF_RET + BPF_K, 1),
+                                       MakeInstruction(BPF_RET + BPF_K, 0));
+  RunTest(head);
 }
 
-TEST_P(ProgramTest, AtypicalBranch) {
+TEST_F(ProgramTest, AtypicalBranch) {
   // Create a program with a single branch:
   //    JUMP if eq 42 then $0 else $0
   // 0: RET 0
 
-  Instruction* ret = MakeInstruction(BPF_RET + BPF_K, 0);
-  Instruction* head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
+  CodeGen::Node ret = MakeInstruction(BPF_RET + BPF_K, 0);
+  CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
 
   // N.B.: As the instructions in both sides of the branch are already
   //       the same object, we do not actually have any "mergeable" branches.
   //       This needs to be reflected in our choice of "flags".
-  RunTest(head, NO_FLAGS);
+  RunTest(head);
 }
 
-TEST_P(ProgramTest, Complex) {
+TEST_F(ProgramTest, Complex) {
   // Creates a basic BPF program that we'll use to test some of the code:
   //    JUMP if eq 42 the $0 else $1     (insn6)
   // 0: LD 23                            (insn5)
@@ -125,18 +216,18 @@ TEST_P(ProgramTest, Complex) {
   //    RET 42                           (insn0)
   // 4: LD 42                            (insn3)
   //    RET 42                           (insn3+)
-  Instruction* insn0 = MakeInstruction(BPF_RET + BPF_K, 42);
-  Instruction* insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0);
-  Instruction* insn2 = MakeInstruction(BPF_JMP + BPF_JA, 0, insn1);
-
-  // We explicitly duplicate instructions so that MergeTails() can coalesce
-  // them later.
-  Instruction* insn3 = MakeInstruction(
-      BPF_LD + BPF_W + BPF_ABS, 42, MakeInstruction(BPF_RET + BPF_K, 42));
+  CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42);
+  CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0);
+  CodeGen::Node insn2 = insn1;  // Implicit JUMP
+
+  // We explicitly duplicate instructions to test that they're merged.
+  CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42,
+                                        MakeInstruction(BPF_RET + BPF_K, 42));
+  EXPECT_EQ(insn2, insn3);
 
-  Instruction* insn4 =
+  CodeGen::Node insn4 =
       MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3);
-  Instruction* insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4);
+  CodeGen::Node insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, insn4);
 
   // Force a basic block that ends in neither a jump instruction nor a return
   // instruction. It only contains "insn5". This exercises one of the less
@@ -144,13 +235,13 @@ TEST_P(ProgramTest, Complex) {
   // This also gives us a diamond-shaped pattern in our graph, which stresses
   // another aspect of the topo-sort algorithm (namely, the ability to
   // correctly count the incoming branches for subtrees that are not disjunct).
-  Instruction* insn6 =
+  CodeGen::Node insn6 =
       MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4);
 
-  RunTest(insn6, HAS_MERGEABLE_TAILS);
+  RunTest(insn6);
 }
 
-TEST_P(ProgramTest, ConfusingTails) {
+TEST_F(ProgramTest, ConfusingTails) {
   // This simple program demonstrates https://crbug.com/351103/
   // The two "LOAD 0" instructions are blocks of their own. MergeTails() could
   // be tempted to merge them since they are the same. However, they are
@@ -169,19 +260,19 @@ TEST_P(ProgramTest, ConfusingTails) {
   //  6) RET 0
   //  7) RET 1
 
-  Instruction* i7 = MakeInstruction(BPF_RET + BPF_K, 1);
-  Instruction* i6 = MakeInstruction(BPF_RET + BPF_K, 0);
-  Instruction* i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
-  Instruction* i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
-  Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
-  Instruction* i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
-  Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
-  Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+  CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+  CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+  CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+  CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+  CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+  CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+  CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+  CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
 
-  RunTest(i0, NO_FLAGS);
+  RunTest(i0);
 }
 
-TEST_P(ProgramTest, ConfusingTailsBasic) {
+TEST_F(ProgramTest, ConfusingTailsBasic) {
   // Without the fix for https://crbug.com/351103/, (see
   // SampleProgramConfusingTails()), this would generate a cyclic graph and
   // crash as the two "LOAD 0" instructions would get merged.
@@ -193,17 +284,17 @@ TEST_P(ProgramTest, ConfusingTailsBasic)
   // 4) LOAD 0  // System call number
   // 5) RET 1
 
-  Instruction* i5 = MakeInstruction(BPF_RET + BPF_K, 1);
-  Instruction* i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
-  Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
-  Instruction* i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
-  Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
-  Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+  CodeGen::Node i5 = MakeInstruction(BPF_RET + BPF_K, 1);
+  CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+  CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+  CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+  CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+  CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
 
-  RunTest(i0, NO_FLAGS);
+  RunTest(i0);
 }
 
-TEST_P(ProgramTest, ConfusingTailsMergeable) {
+TEST_F(ProgramTest, ConfusingTailsMergeable) {
   // This is similar to SampleProgramConfusingTails(), except that
   // instructions 2 and 4 are now RET instructions.
   // In PointerCompare(), this exercises the path where two blocks are of the
@@ -219,295 +310,94 @@ TEST_P(ProgramTest, ConfusingTailsMergea
   // 6) RET 0
   // 7) RET 1
 
-  Instruction* i7 = MakeInstruction(BPF_RET + BPF_K, 1);
-  Instruction* i6 = MakeInstruction(BPF_RET + BPF_K, 0);
-  Instruction* i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
-  Instruction* i4 = MakeInstruction(BPF_RET + BPF_K, 42);
-  Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
-  Instruction* i2 = MakeInstruction(BPF_RET + BPF_K, 42);
-  Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
-  Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
-
-  RunTest(i0, HAS_MERGEABLE_TAILS);
-}
-
-void MakeInstruction(CodeGenUnittestHelper* codegen,
-                     Instruction* program, int) {
-  // Nothing to do here
-}
-
-void FindBranchTargets(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
-  BranchTargets branch_targets;
-  codegen->FindBranchTargets(*prg, &branch_targets);
-
-  // Verifying the general properties that should be true for every
-  // well-formed BPF program.
-  // Perform a depth-first traversal of the BPF program an verify that all
-  // targets of BPF_JMP instructions are represented in the "branch_targets".
-  // At the same time, compute a set of both the branch targets and all the
-  // instructions in the program.
-  std::vector<Instruction*> stack;
-  std::set<Instruction*> all_instructions;
-  std::set<Instruction*> target_instructions;
-  BranchTargets::const_iterator end = branch_targets.end();
-  for (Instruction* insn = prg;;) {
-    all_instructions.insert(insn);
-    if (BPF_CLASS(insn->code) == BPF_JMP) {
-      target_instructions.insert(insn->jt_ptr);
-      ASSERT_TRUE(insn->jt_ptr != NULL);
-      ASSERT_TRUE(branch_targets.find(insn->jt_ptr) != end);
-      if (BPF_OP(insn->code) != BPF_JA) {
-        target_instructions.insert(insn->jf_ptr);
-        ASSERT_TRUE(insn->jf_ptr != NULL);
-        ASSERT_TRUE(branch_targets.find(insn->jf_ptr) != end);
-        stack.push_back(insn->jf_ptr);
-      }
-      insn = insn->jt_ptr;
-    } else if (BPF_CLASS(insn->code) == BPF_RET) {
-      ASSERT_TRUE(insn->next == NULL);
-      if (stack.empty()) {
-        break;
-      }
-      insn = stack.back();
-      stack.pop_back();
-    } else {
-      ASSERT_TRUE(insn->next != NULL);
-      insn = insn->next;
-    }
+  CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+  CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+  CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+  CodeGen::Node i4 = MakeInstruction(BPF_RET + BPF_K, 42);
+  CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+  CodeGen::Node i2 = MakeInstruction(BPF_RET + BPF_K, 42);
+  CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+  CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+  RunTest(i0);
+}
+
+TEST_F(ProgramTest, InstructionFolding) {
+  // Check that simple instructions are folded as expected.
+  CodeGen::Node a = MakeInstruction(BPF_RET + BPF_K, 0);
+  EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0));
+  CodeGen::Node b = MakeInstruction(BPF_RET + BPF_K, 1);
+  EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0));
+  EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1));
+  EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1));
+
+  // Check that complex sequences are folded too.
+  CodeGen::Node c =
+      MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0,
+                      MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b));
+  EXPECT_EQ(c, MakeInstruction(
+                   BPF_LD + BPF_W + BPF_ABS, 0,
+                   MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b)));
+
+  RunTest(c);
+}
+
+TEST_F(ProgramTest, FarBranches) {
+  // BPF instructions use 8-bit fields for branch offsets, which means
+  // branch targets must be within 255 instructions of the branch
+  // instruction. CodeGen abstracts away this detail by inserting jump
+  // instructions as needed, which we test here by generating programs
+  // that should trigger any interesting boundary conditions.
+
+  // Populate with 260 initial instruction nodes.
+  std::vector<CodeGen::Node> nodes;
+  nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0));
+  for (size_t i = 1; i < 260; ++i) {
+    nodes.push_back(
+        MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back()));
   }
-  ASSERT_TRUE(target_instructions.size() == branch_targets.size());
 
-  // We can now subtract the set of the branch targets from the set of all
-  // instructions. This gives us a set with the instructions that nobody
-  // ever jumps to. Verify that they are no included in the
-  // "branch_targets" that FindBranchTargets() computed for us.
-  Instructions non_target_instructions(all_instructions.size() -
-                                       target_instructions.size());
-  set_difference(all_instructions.begin(),
-                 all_instructions.end(),
-                 target_instructions.begin(),
-                 target_instructions.end(),
-                 non_target_instructions.begin());
-  for (Instructions::const_iterator iter = non_target_instructions.begin();
-       iter != non_target_instructions.end();
-       ++iter) {
-    ASSERT_TRUE(branch_targets.find(*iter) == end);
-  }
-}
-
-void CutGraphIntoBasicBlocks(CodeGenUnittestHelper* codegen,
-                             Instruction* prg,
-                             int) {
-  BranchTargets branch_targets;
-  codegen->FindBranchTargets(*prg, &branch_targets);
-  TargetsToBlocks all_blocks;
-  BasicBlock* first_block =
-      codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
-  ASSERT_TRUE(first_block != NULL);
-  ASSERT_TRUE(first_block->instructions.size() > 0);
-  Instruction* first_insn = first_block->instructions[0];
-
-  // Basic blocks are supposed to start with a branch target and end with
-  // either a jump or a return instruction. It can also end, if the next
-  // instruction forms the beginning of a new basic block. There should be
-  // no other jumps or return instructions in the middle of a basic block.
-  for (TargetsToBlocks::const_iterator bb_iter = all_blocks.begin();
-       bb_iter != all_blocks.end();
-       ++bb_iter) {
-    BasicBlock* bb = bb_iter->second;
-    ASSERT_TRUE(bb != NULL);
-    ASSERT_TRUE(bb->instructions.size() > 0);
-    Instruction* insn = bb->instructions[0];
-    ASSERT_TRUE(insn == first_insn ||
-                branch_targets.find(insn) != branch_targets.end());
-    for (Instructions::const_iterator insn_iter = bb->instructions.begin();;) {
-      insn = *insn_iter;
-      if (++insn_iter != bb->instructions.end()) {
-        ASSERT_TRUE(BPF_CLASS(insn->code) != BPF_JMP);
-        ASSERT_TRUE(BPF_CLASS(insn->code) != BPF_RET);
-      } else {
-        ASSERT_TRUE(BPF_CLASS(insn->code) == BPF_JMP ||
-                    BPF_CLASS(insn->code) == BPF_RET ||
-                    branch_targets.find(insn->next) != branch_targets.end());
-        break;
-      }
-      ASSERT_TRUE(branch_targets.find(*insn_iter) == branch_targets.end());
+  // Exhaustively test branch offsets near BPF's limits.
+  for (size_t jt = 250; jt < 260; ++jt) {
+    for (size_t jf = 250; jf < 260; ++jf) {
+      nodes.push_back(MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0,
+                                      nodes.rbegin()[jt], nodes.rbegin()[jf]));
+      RunTest(nodes.back());
     }
   }
 }
 
-void MergeTails(CodeGenUnittestHelper* codegen, Instruction* prg, int flags) {
-  BranchTargets branch_targets;
-  codegen->FindBranchTargets(*prg, &branch_targets);
-  TargetsToBlocks all_blocks;
-  BasicBlock* first_block =
-      codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks);
-
-  // The shape of our graph and thus the function of our program should
-  // still be unchanged after we run MergeTails(). We verify this by
-  // serializing the graph and verifying that it is still the same.
-  // We also verify that at least some of the edges changed because of
-  // tail merging.
-  std::string graph[2];
-  std::string edges[2];
-
-  // The loop executes twice. After the first run, we call MergeTails() on
-  // our graph.
-  for (int i = 0;;) {
-    // Traverse the entire program in depth-first order.
-    std::vector<BasicBlock*> stack;
-    for (BasicBlock* bb = first_block;;) {
-      // Serialize the instructions in this basic block. In general, we only
-      // need to serialize "code" and "k"; except for a BPF_JA instruction
-      // where "k" isn't set.
-      // The stream of instructions should be unchanged after MergeTails().
-      for (Instructions::const_iterator iter = bb->instructions.begin();
-           iter != bb->instructions.end();
-           ++iter) {
-        graph[i].append(reinterpret_cast<char*>(&(*iter)->code),
-                        sizeof((*iter)->code));
-        if (BPF_CLASS((*iter)->code) != BPF_JMP ||
-            BPF_OP((*iter)->code) != BPF_JA) {
-          graph[i].append(reinterpret_cast<char*>(&(*iter)->k),
-                          sizeof((*iter)->k));
-        }
-      }
-
-      // Also serialize the addresses the basic blocks as we encounter them.
-      // This will change as basic blocks are coalesed by MergeTails().
-      edges[i].append(reinterpret_cast<char*>(&bb), sizeof(bb));
-
-      // Depth-first traversal of the graph. We only ever need to look at the
-      // very last instruction in the basic block, as that is the only one that
-      // can change code flow.
-      Instruction* insn = bb->instructions.back();
-      if (BPF_CLASS(insn->code) == BPF_JMP) {
-        // For jump instructions, we need to remember the "false" branch while
-        // traversing the "true" branch. This is not necessary for BPF_JA which
-        // only has a single branch.
-        if (BPF_OP(insn->code) != BPF_JA) {
-          stack.push_back(all_blocks[insn->jf_ptr]);
-        }
-        bb = all_blocks[insn->jt_ptr];
-      } else if (BPF_CLASS(insn->code) == BPF_RET) {
-        // After a BPF_RET, see if we need to back track.
-        if (stack.empty()) {
-          break;
-        }
-        bb = stack.back();
-        stack.pop_back();
-      } else {
-        // For "normal" instructions, just follow to the next basic block.
-        bb = all_blocks[insn->next];
-      }
-    }
-
-    // Our loop runs exactly two times.
-    if (++i > 1) {
-      break;
-    }
-    codegen->MergeTails(&all_blocks);
-  }
-  ASSERT_TRUE(graph[0] == graph[1]);
-  if (flags & HAS_MERGEABLE_TAILS) {
-    ASSERT_TRUE(edges[0] != edges[1]);
-  } else {
-    ASSERT_TRUE(edges[0] == edges[1]);
+TEST_F(ProgramTest, JumpReuse) {
+  // As a code size optimization, we try to reuse jumps when possible
+  // instead of emitting new ones. Here we make sure that optimization
+  // is working as intended.
+  //
+  // NOTE: To simplify testing, we rely on implementation details
+  // about what CodeGen::Node values indicate (i.e., vector indices),
+  // but CodeGen users should treat them as opaque values.
+
+  // Populate with 260 initial instruction nodes.
+  std::vector<CodeGen::Node> nodes;
+  nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0));
+  for (size_t i = 1; i < 260; ++i) {
+    nodes.push_back(
+        MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back()));
   }
-}
 
-void CompileAndCompare(CodeGenUnittestHelper* codegen, Instruction* prg, int) {
-  // TopoSortBasicBlocks() has internal checks that cause it to fail, if it
-  // detects a problem. Typically, if anything goes wrong, this looks to the
-  // TopoSort algorithm as if there had been cycles in the input data.
-  // This provides a pretty good unittest.
-  // We hand-crafted the program returned by SampleProgram() to exercise
-  // several of the more interesting code-paths. See comments in
-  // SampleProgram() for details.
-  // In addition to relying on the internal consistency checks in the compiler,
-  // we also serialize the graph and the resulting BPF program and compare
-  // them. With the exception of BPF_JA instructions that might have been
-  // inserted, both instruction streams should be equivalent.
-  // As Compile() modifies the instructions, we have to serialize the graph
-  // before calling Compile().
-  std::string source;
-  Instructions source_stack;
-  for (const Instruction* insn = prg, *next; insn; insn = next) {
-    if (BPF_CLASS(insn->code) == BPF_JMP) {
-      if (BPF_OP(insn->code) == BPF_JA) {
-        // Do not serialize BPF_JA instructions (see above).
-        next = insn->jt_ptr;
-        continue;
-      } else {
-        source_stack.push_back(insn->jf_ptr);
-        next = insn->jt_ptr;
-      }
-    } else if (BPF_CLASS(insn->code) == BPF_RET) {
-      if (source_stack.empty()) {
-        next = NULL;
-      } else {
-        next = source_stack.back();
-        source_stack.pop_back();
-      }
-    } else {
-      next = insn->next;
-    }
-    // Only serialize "code" and "k". That's all the information we need to
-    // compare. The rest of the information is encoded in the order of
-    // instructions.
-    source.append(reinterpret_cast<const char*>(&insn->code),
-                  sizeof(insn->code));
-    source.append(reinterpret_cast<const char*>(&insn->k), sizeof(insn->k));
-  }
-
-  // Compile the program
-  CodeGen::Program bpf;
-  codegen->Compile(prg, &bpf);
-
-  // Serialize the resulting BPF instructions.
-  std::string assembly;
-  std::vector<int> assembly_stack;
-  for (int idx = 0; idx >= 0;) {
-    ASSERT_TRUE(idx < (int)bpf.size());
-    struct sock_filter& insn = bpf[idx];
-    if (BPF_CLASS(insn.code) == BPF_JMP) {
-      if (BPF_OP(insn.code) == BPF_JA) {
-        // Do not serialize BPF_JA instructions (see above).
-        idx += insn.k + 1;
-        continue;
-      } else {
-        assembly_stack.push_back(idx + insn.jf + 1);
-        idx += insn.jt + 1;
-      }
-    } else if (BPF_CLASS(insn.code) == BPF_RET) {
-      if (assembly_stack.empty()) {
-        idx = -1;
-      } else {
-        idx = assembly_stack.back();
-        assembly_stack.pop_back();
-      }
-    } else {
-      ++idx;
-    }
-    // Serialize the same information that we serialized before compilation.
-    assembly.append(reinterpret_cast<char*>(&insn.code), sizeof(insn.code));
-    assembly.append(reinterpret_cast<char*>(&insn.k), sizeof(insn.k));
-  }
-  ASSERT_TRUE(source == assembly);
+  // Branching to nodes[0] and nodes[1] should require 3 new
+  // instructions: two far jumps plus the branch itself.
+  CodeGen::Node one =
+      MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, nodes[0], nodes[1]);
+  EXPECT_EQ(nodes.back() + 3, one);  // XXX: Implementation detail!
+  RunTest(one);
+
+  // Branching again to the same target nodes should require only one
+  // new instruction, as we can reuse the previous branch's jumps.
+  CodeGen::Node two =
+      MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, nodes[0], nodes[1]);
+  EXPECT_EQ(one + 1, two);  // XXX: Implementation detail!
+  RunTest(two);
 }
 
-const ProgramTestFunc kProgramTestFuncs[] = {
-    MakeInstruction,
-    FindBranchTargets,
-    CutGraphIntoBasicBlocks,
-    MergeTails,
-    CompileAndCompare,
-};
-
-INSTANTIATE_TEST_CASE_P(CodeGen,
-                        ProgramTest,
-                        ::testing::ValuesIn(kProgramTestFuncs));
-
 }  // namespace
-
 }  // namespace sandbox
--- a/sandbox/linux/seccomp-bpf/errorcode.h
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -21,6 +21,10 @@ class PolicyCompiler;
 // 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
 // field.
+//
+// TODO(mdempsky): Nuke from orbit. The only reason this class still
+// exists is for Verifier, which will eventually be replaced by a true
+// BPF symbolic evaluator and constraint solver.
 class SANDBOX_EXPORT ErrorCode {
  public:
   enum {
--- a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -21,9 +21,9 @@ namespace {
 class DummyPolicy : public bpf_dsl::Policy {
  public:
   DummyPolicy() {}
-  virtual ~DummyPolicy() {}
+  ~DummyPolicy() override {}
 
-  virtual bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override {
+  bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override {
     return bpf_dsl::Allow();
   }
 
@@ -43,7 +43,7 @@ SANDBOX_TEST(ErrorCode, ErrnoConstructor
 
   DummyPolicy dummy_policy;
   bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
-  ErrorCode e3 = compiler.Trap(NULL, NULL);
+  ErrorCode e3 = compiler.Trap(NULL, NULL, true /* safe */);
   SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION)  == SECCOMP_RET_TRAP);
 
   uint16_t data = 0xdead;
@@ -61,12 +61,12 @@ SANDBOX_DEATH_TEST(ErrorCode,
 SANDBOX_TEST(ErrorCode, Trap) {
   DummyPolicy dummy_policy;
   bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
-  ErrorCode e0 = compiler.Trap(NULL, "a");
-  ErrorCode e1 = compiler.Trap(NULL, "b");
+  ErrorCode e0 = compiler.Trap(NULL, "a", true /* safe */);
+  ErrorCode e1 = compiler.Trap(NULL, "b", true /* safe */);
   SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 ==
                  (e1.err() & SECCOMP_RET_DATA));
 
-  ErrorCode e2 = compiler.Trap(NULL, "a");
+  ErrorCode e2 = compiler.Trap(NULL, "a", true /* safe */);
   SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) ==
                  (e2.err() & SECCOMP_RET_DATA));
 }
@@ -83,9 +83,9 @@ SANDBOX_TEST(ErrorCode, Equals) {
 
   DummyPolicy dummy_policy;
   bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
-  ErrorCode e4 = compiler.Trap(NULL, "a");
-  ErrorCode e5 = compiler.Trap(NULL, "b");
-  ErrorCode e6 = compiler.Trap(NULL, "a");
+  ErrorCode e4 = compiler.Trap(NULL, "a", true /* safe */);
+  ErrorCode e5 = compiler.Trap(NULL, "b", true /* safe */);
+  ErrorCode e6 = compiler.Trap(NULL, "a", true /* safe */);
   SANDBOX_ASSERT(!e1.Equals(e4));
   SANDBOX_ASSERT(!e3.Equals(e4));
   SANDBOX_ASSERT(!e5.Equals(e4));
@@ -105,9 +105,9 @@ SANDBOX_TEST(ErrorCode, LessThan) {
 
   DummyPolicy dummy_policy;
   bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
-  ErrorCode e4 = compiler.Trap(NULL, "a");
-  ErrorCode e5 = compiler.Trap(NULL, "b");
-  ErrorCode e6 = compiler.Trap(NULL, "a");
+  ErrorCode e4 = compiler.Trap(NULL, "a", true /* safe */);
+  ErrorCode e5 = compiler.Trap(NULL, "b", true /* safe */);
+  ErrorCode e6 = compiler.Trap(NULL, "a", true /* safe */);
   SANDBOX_ASSERT(e1.LessThan(e4));
   SANDBOX_ASSERT(e3.LessThan(e4));
   SANDBOX_ASSERT(e4.LessThan(e5));
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -11,24 +11,18 @@
 #endif
 
 #include <errno.h>
-#include <fcntl.h>
 #include <linux/filter.h>
-#include <signal.h>
-#include <string.h>
 #include <sys/prctl.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
 #include <sys/types.h>
-#include <sys/wait.h>
-#include <time.h>
 #include <unistd.h>
 
 #include "base/compiler_specific.h"
+#include "base/files/scoped_file.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/posix/eintr_wrapper.h"
-#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "base/third_party/valgrind/valgrind.h"
 #include "sandbox/linux/bpf_dsl/dump_bpf.h"
 #include "sandbox/linux/bpf_dsl/policy.h"
 #include "sandbox/linux/bpf_dsl/policy_compiler.h"
@@ -41,354 +35,96 @@
 #include "sandbox/linux/seccomp-bpf/trap.h"
 #include "sandbox/linux/seccomp-bpf/verifier.h"
 #include "sandbox/linux/services/linux_syscalls.h"
-
-using sandbox::bpf_dsl::Allow;
-using sandbox::bpf_dsl::Error;
-using sandbox::bpf_dsl::ResultExpr;
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
 
 namespace sandbox {
 
 namespace {
 
-const int kExpectedExitCode = 100;
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
 
-#if !defined(NDEBUG)
-void WriteFailedStderrSetupMessage(int out_fd) {
-  const char* error_string = strerror(errno);
-  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))) {
-  }
+bool IsSingleThreaded(int proc_task_fd) {
+  return ThreadHelpers::IsSingleThreaded(proc_task_fd);
 }
-#endif  // !defined(NDEBUG)
 
-// We define a really simple sandbox policy. It is just good enough for us
-// to tell that the sandbox has actually been activated.
-class ProbePolicy : public bpf_dsl::Policy {
- public:
-  ProbePolicy() {}
-  virtual ~ProbePolicy() {}
-
-  virtual ResultExpr EvaluateSyscall(int sysnum) const override {
-    switch (sysnum) {
-      case __NR_getpid:
-        // Return EPERM so that we can check that the filter actually ran.
-        return Error(EPERM);
-      case __NR_exit_group:
-        // Allow exit() with a non-default return code.
-        return Allow();
-      default:
-        // Make everything else fail in an easily recognizable way.
-        return Error(EINVAL);
-    }
-  }
+// Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via
+// prctl().
+bool KernelSupportsSeccompBPF() {
+  errno = 0;
+  const int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(ProbePolicy);
-};
-
-void ProbeProcess(void) {
-  if (syscall(__NR_getpid) < 0 && errno == EPERM) {
-    syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
+  if (rv == -1 && EFAULT == errno) {
+    return true;
   }
+  return false;
 }
 
-class AllowAllPolicy : public bpf_dsl::Policy {
- public:
-  AllowAllPolicy() {}
-  virtual ~AllowAllPolicy() {}
-
-  virtual ResultExpr EvaluateSyscall(int sysnum) const override {
-    DCHECK(SandboxBPF::IsValidSyscallNumber(sysnum));
-    return Allow();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
-};
-
-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
-  // we may get SIGKILL-ed. Detect this!
-  if (time(&current_time) != static_cast<time_t>(-1)) {
-    syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
-  }
-}
+// Check if the kernel supports seccomp-filter via the seccomp system call
+// and the TSYNC feature to enable seccomp on all threads.
+bool KernelSupportsSeccompTsync() {
+  errno = 0;
+  const int rv =
+      sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, nullptr);
 
-bool IsSingleThreaded(int proc_fd) {
-  if (proc_fd < 0) {
-    // Cannot determine whether program is single-threaded. Hope for
-    // the best...
+  if (rv == -1 && errno == EFAULT) {
     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 || IGNORE_EINTR(close(task))) {
-    if (task >= 0) {
-      if (IGNORE_EINTR(close(task))) {
-      }
-    }
+  } else {
+    // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
+    CHECK_EQ(-1, rv);
+    CHECK(ENOSYS == errno || EINVAL == errno);
     return false;
   }
-  return true;
 }
 
 }  // namespace
 
-SandboxBPF::SandboxBPF()
-    : quiet_(false), proc_fd_(-1), sandbox_has_started_(false), policy_() {
+SandboxBPF::SandboxBPF(bpf_dsl::Policy* policy)
+    : proc_task_fd_(), sandbox_has_started_(false), policy_(policy) {
 }
 
 SandboxBPF::~SandboxBPF() {
 }
 
-bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
-  return SyscallSet::IsValid(sysnum);
-}
-
-bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(),
-                                     scoped_ptr<bpf_dsl::Policy> policy) {
-  // Block all signals before forking a child process. This prevents an
-  // attacker from manipulating our test by sending us an unexpected signal.
-  sigset_t old_mask, new_mask;
-  if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
-    SANDBOX_DIE("sigprocmask() failed");
-  }
-  int fds[2];
-  if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) {
-    SANDBOX_DIE("pipe() failed");
-  }
-
-  if (fds[0] <= 2 || fds[1] <= 2) {
-    SANDBOX_DIE("Process started without standard file descriptors");
-  }
-
-  // This code is using fork() and should only ever run single-threaded.
-  // Most of the code below is "async-signal-safe" and only minor changes
-  // would be needed to support threads.
-  DCHECK(IsSingleThreaded(proc_fd_));
-  pid_t pid = fork();
-  if (pid < 0) {
-    // Die if we cannot fork(). We would probably fail a little later
-    // anyway, as the machine is likely very close to running out of
-    // memory.
-    // 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, &old_mask, NULL);  // OK, if it fails
-    SANDBOX_DIE("fork() failed unexpectedly");
-  }
-
-  // In the child process
-  if (!pid) {
-    // Test a very simple sandbox policy to verify that we can
-    // successfully turn on sandboxing.
-    Die::EnableSimpleExit();
-
-    errno = 0;
-    if (IGNORE_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
-      // another file, which could be backed by a file system that could cause
-      // dup2 to fail while trying to close stderr. It's important that we do
-      // not fail on trying to close stderr.
-      // 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 !defined(NDEBUG)
-      // In DEBUG builds, we still want to get a report.
-      WriteFailedStderrSetupMessage(fds[1]);
-      SANDBOX_DIE(NULL);
-#endif
-    }
-    if (IGNORE_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
-    }
-
-    SetSandboxPolicy(policy.release());
-    if (!StartSandbox(PROCESS_SINGLE_THREADED)) {
-      SANDBOX_DIE(NULL);
-    }
-
-    // Run our code in the sandbox.
-    code_in_sandbox();
-
-    // code_in_sandbox() is not supposed to return here.
-    SANDBOX_DIE(NULL);
-  }
-
-  // In the parent process.
-  if (IGNORE_EINTR(close(fds[1]))) {
-    SANDBOX_DIE("close() failed");
-  }
-  if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) {
-    SANDBOX_DIE("sigprocmask() failed");
-  }
-  int status;
-  if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) {
-    SANDBOX_DIE("waitpid() failed unexpectedly");
-  }
-  bool rc = WIFEXITED(status) && WEXITSTATUS(status) == kExpectedExitCode;
-
-  // If we fail to support sandboxing, there might be an additional
-  // error message. If so, this was an entirely unexpected and fatal
-  // failure. We should report the failure and somebody must fix
-  // things. This is probably a security-critical bug in the sandboxing
-  // code.
-  if (!rc) {
-    char buf[4096];
-    ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1));
-    if (len > 0) {
-      while (len > 1 && buf[len - 1] == '\n') {
-        --len;
-      }
-      buf[len] = '\000';
-      SANDBOX_DIE(buf);
-    }
-  }
-  if (IGNORE_EINTR(close(fds[0]))) {
-    SANDBOX_DIE("close() failed");
-  }
-
-  return rc;
-}
-
-bool SandboxBPF::KernelSupportSeccompBPF() {
-  return RunFunctionInPolicy(ProbeProcess,
-                             scoped_ptr<bpf_dsl::Policy>(new ProbePolicy())) &&
-         RunFunctionInPolicy(TryVsyscallProcess,
-                             scoped_ptr<bpf_dsl::Policy>(new AllowAllPolicy()));
-}
-
 // static
-SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) {
-  // It the sandbox is currently active, we clearly must have support for
-  // sandboxing.
-  if (status_ == STATUS_ENABLED) {
-    return status_;
-  }
-
-  // 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)) {
-      status_ = STATUS_UNAVAILABLE;
-    }
-    return status_;
-  }
-
-  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-
-    // threaded.
-    // In other words, if a single-threaded process is currently in the
-    // STATUS_UNAVAILABLE state, it is safe to assume that sandboxing is
-    // actually available.
-    status_ = STATUS_AVAILABLE;
-    return status_;
+bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) {
+  // Never pretend to support seccomp with Valgrind, as it
+  // throws the tool off.
+  if (IsRunningOnValgrind()) {
+    return false;
   }
 
-  // If we have not previously checked for availability of the sandbox or if
-  // we otherwise don't believe to have a good cached value, we have to
-  // perform a thorough check now.
-  if (status_ == STATUS_UNKNOWN) {
-    // 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()".
-    SandboxBPF 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)) {
-      status_ = STATUS_UNAVAILABLE;
-    }
-  }
-  return status_;
+  switch (level) {
+    case SeccompLevel::SINGLE_THREADED:
+      return KernelSupportsSeccompBPF();
+    case SeccompLevel::MULTI_THREADED:
+      return KernelSupportsSeccompTsync();
+  }
+  NOTREACHED();
+  return false;
 }
 
-// static
-SandboxBPF::SandboxStatus
-SandboxBPF::SupportsSeccompThreadFilterSynchronization() {
-  // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across
-  // the thread group are all handled atomically by this syscall.
-  const int rv = syscall(
-      __NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL);
+bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
+  DCHECK(policy_);
+  CHECK(seccomp_level == SeccompLevel::SINGLE_THREADED ||
+        seccomp_level == SeccompLevel::MULTI_THREADED);
 
-  if (rv == -1 && errno == EFAULT) {
-    return STATUS_AVAILABLE;
-  } else {
-    // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
-    CHECK_EQ(-1, rv);
-    CHECK(ENOSYS == errno || EINVAL == errno);
-    return STATUS_UNSUPPORTED;
-  }
-}
-
-void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; }
-
-bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) {
-  CHECK(thread_state == PROCESS_SINGLE_THREADED ||
-        thread_state == PROCESS_MULTI_THREADED);
-
-  if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
-    SANDBOX_DIE(
-        "Trying to start sandbox, even though it is known to be "
-        "unavailable");
-    return false;
-  } else if (sandbox_has_started_) {
+  if (sandbox_has_started_) {
     SANDBOX_DIE(
         "Cannot repeatedly start sandbox. Create a separate Sandbox "
         "object instead.");
     return false;
   }
-  if (proc_fd_ < 0) {
-    proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY);
-  }
-  if (proc_fd_ < 0) {
-    // For now, continue in degraded mode, if we can't access /proc.
-    // In the future, we might want to tighten this requirement.
-  }
 
-  bool supports_tsync =
-      SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE;
+  const bool supports_tsync = KernelSupportsSeccompTsync();
 
-  if (thread_state == PROCESS_SINGLE_THREADED) {
-    if (!IsSingleThreaded(proc_fd_)) {
+  if (seccomp_level == SeccompLevel::SINGLE_THREADED) {
+    if (!IsSingleThreaded(proc_task_fd_.get())) {
       SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded");
       return false;
     }
-  } else if (thread_state == PROCESS_MULTI_THREADED) {
-    if (IsSingleThreaded(proc_fd_)) {
+  } else if (seccomp_level == SeccompLevel::MULTI_THREADED) {
+    if (IsSingleThreaded(proc_task_fd_.get())) {
       SANDBOX_DIE("Cannot start sandbox; "
                   "process may be single-threaded when reported as not");
       return false;
@@ -403,30 +139,65 @@ bool SandboxBPF::StartSandbox(SandboxThr
   // We no longer need access to any files in /proc. We want to do this
   // before installing the filters, just in case that our policy denies
   // close().
-  if (proc_fd_ >= 0) {
-    if (IGNORE_EINTR(close(proc_fd_))) {
-      SANDBOX_DIE("Failed to close file descriptor for /proc");
-      return false;
-    }
-    proc_fd_ = -1;
+  if (proc_task_fd_.is_valid()) {
+    proc_task_fd_.reset();
   }
 
   // Install the filters.
-  InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED);
-
-  // We are now inside the sandbox.
-  status_ = STATUS_ENABLED;
+  InstallFilter(supports_tsync ||
+                seccomp_level == SeccompLevel::MULTI_THREADED);
 
   return true;
 }
 
-// Don't take a scoped_ptr here, polymorphism make their use awkward.
-void SandboxBPF::SetSandboxPolicy(bpf_dsl::Policy* policy) {
-  DCHECK(!policy_);
-  if (sandbox_has_started_) {
-    SANDBOX_DIE("Cannot change policy after sandbox has started");
+void SandboxBPF::SetProcTaskFd(base::ScopedFD proc_task_fd) {
+  proc_task_fd_.swap(proc_task_fd);
+}
+
+// static
+bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
+  return SyscallSet::IsValid(sysnum);
+}
+
+// static
+bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) {
+  return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno);
+}
+
+// static
+intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
+  return Syscall::Call(
+      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]));
+}
+
+scoped_ptr<CodeGen::Program> SandboxBPF::AssembleFilter(
+    bool force_verification) {
+#if !defined(NDEBUG)
+  force_verification = true;
+#endif
+  DCHECK(policy_);
+  bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry());
+  scoped_ptr<CodeGen::Program> program = compiler.Compile();
+
+  // Make sure compilation resulted in a 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.
+  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.
+
+    const char* err = NULL;
+    if (!Verifier::VerifyBPF(&compiler, *program, *policy_, &err)) {
+      bpf_dsl::DumpBPF::PrintProgram(*program);
+      SANDBOX_DIE(err);
+    }
   }
-  policy_.reset(policy);
+
+  return program.Pass();
 }
 
 void SandboxBPF::InstallFilter(bool must_sync_threads) {
@@ -455,69 +226,26 @@ void SandboxBPF::InstallFilter(bool must
   policy_.reset();
 
   if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
-    SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs");
+    SANDBOX_DIE("Kernel refuses to enable no-new-privs");
   }
 
   // Install BPF filter program. If the thread state indicates multi-threading
   // support, then the kernel hass the seccomp system call. Otherwise, fall
   // back on prctl, which requires the process to be single-threaded.
   if (must_sync_threads) {
-    int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
-        SECCOMP_FILTER_FLAG_TSYNC, reinterpret_cast<const char*>(&prog));
+    int rv =
+        sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog);
     if (rv) {
-      SANDBOX_DIE(quiet_ ? NULL :
+      SANDBOX_DIE(
           "Kernel refuses to turn on and synchronize threads for BPF filters");
     }
   } else {
     if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
-      SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters");
+      SANDBOX_DIE("Kernel refuses to turn on BPF filters");
     }
   }
 
   sandbox_has_started_ = true;
 }
 
-scoped_ptr<CodeGen::Program> SandboxBPF::AssembleFilter(
-    bool force_verification) {
-#if !defined(NDEBUG)
-  force_verification = true;
-#endif
-
-  bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry());
-  scoped_ptr<CodeGen::Program> program = compiler.Compile();
-
-  // 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.
-  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.
-
-    const char* err = NULL;
-    if (!Verifier::VerifyBPF(&compiler, *program, *policy_, &err)) {
-      bpf_dsl::DumpBPF::PrintProgram(*program);
-      SANDBOX_DIE(err);
-    }
-  }
-
-  return program.Pass();
-}
-
-bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) {
-  return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno);
-}
-
-intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
-  return Syscall::Call(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]));
-}
-
-SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN;
-
 }  // namespace sandbox
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
-#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
 
 #include <stdint.h>
 
-#include "base/compiler_specific.h"
+#include "base/files/scoped_file.h"
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "sandbox/linux/seccomp-bpf/codegen.h"
@@ -19,67 +19,63 @@ namespace bpf_dsl {
 class Policy;
 }
 
+// This class can be used to apply a syscall sandboxing policy expressed in a
+// bpf_dsl::Policy object to the current process.
+// Syscall sandboxing policies get inherited by subprocesses and, once applied,
+// can never be removed for the lifetime of the process.
 class SANDBOX_EXPORT SandboxBPF {
  public:
-  enum SandboxStatus {
-    STATUS_UNKNOWN,      // Status prior to calling supportsSeccompSandbox()
-    STATUS_UNSUPPORTED,  // The kernel does not appear to support sandboxing
-    STATUS_UNAVAILABLE,  // Currently unavailable but might work again later
-    STATUS_AVAILABLE,    // Sandboxing is available but not currently active
-    STATUS_ENABLED       // The sandbox is now active
+  enum class SeccompLevel {
+    SINGLE_THREADED,
+    MULTI_THREADED,
   };
 
-  // Depending on the level of kernel support, seccomp-bpf may require the
-  // process to be single-threaded in order to enable it. When calling
-  // StartSandbox(), the program should indicate whether or not the sandbox
-  // should try and engage with multi-thread support.
-  enum SandboxThreadState {
-    PROCESS_INVALID,
-    PROCESS_SINGLE_THREADED,  // The program is currently single-threaded.
-    // Note: PROCESS_MULTI_THREADED requires experimental kernel support that
-    // has not been contributed to upstream Linux.
-    PROCESS_MULTI_THREADED,   // The program may be multi-threaded.
-  };
-
-  // Constructors and destructors.
+  // Ownership of |policy| is transfered here to the sandbox object.
+  // nullptr is allowed for unit tests.
+  explicit SandboxBPF(bpf_dsl::Policy* policy);
   // 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.
-  SandboxBPF();
+  // The kernel does not provide any option for unloading a loaded sandbox. The
+  // sandbox remains engaged even when the object is destructed.
   ~SandboxBPF();
 
-  // 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);
+  // Detect if the kernel supports the specified seccomp level.
+  // See StartSandbox() for a description of these.
+  static bool SupportsSeccompSandbox(SeccompLevel level);
+
+  // This is the main public entry point. It sets up the resources needed by
+  // the sandbox, and enters Seccomp mode.
+  // The calling process must provide a |level| to tell the sandbox which type
+  // of kernel support it should engage.
+  // SINGLE_THREADED will only sandbox the calling thread. Since it would be a
+  // security risk, the sandbox will also check that the current process is
+  // single threaded and crash if it isn't the case.
+  // MULTI_THREADED requires more recent kernel support and allows to sandbox
+  // all the threads of the current process. Be mindful of potential races,
+  // with other threads using disallowed system calls either before or after
+  // the sandbox is engaged.
+  //
+  // 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.
+  bool StartSandbox(SeccompLevel level) WARN_UNUSED_RESULT;
 
-  // 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);
-
-  // Determines if the kernel has support for the seccomp() system call to
-  // synchronize BPF filters across a thread group.
-  static SandboxStatus SupportsSeccompThreadFilterSynchronization();
-
-  // 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 "set_proc_fd()".
+  // The sandbox needs to be able to access files in "/proc/self/task/". If
+  // this directory is not accessible when "StartSandbox()" gets called, the
+  // caller must provide an already opened file descriptor by calling
+  // "SetProcTaskFd()".
   // The sandbox becomes the new owner of this file descriptor and will
-  // eventually close it when "StartSandbox()" executes.
-  void set_proc_fd(int proc_fd);
+  // close it when "StartSandbox()" executes or when the sandbox object
+  // disappears.
+  void SetProcTaskFd(base::ScopedFD proc_task_fd);
 
-  // Set the BPF policy as |policy|. Ownership of |policy| is transfered here
-  // to the sandbox object.
-  void SetSandboxPolicy(bpf_dsl::Policy* policy);
+  // Checks whether a particular system call number is valid on the current
+  // architecture.
+  static bool IsValidSyscallNumber(int sysnum);
 
   // UnsafeTraps require some syscalls to always be allowed.
   // This helper function returns true for these calls.
@@ -95,21 +91,6 @@ class SANDBOX_EXPORT SandboxBPF {
   // directly suitable as a return value for a trap handler.
   static intptr_t ForwardSyscall(const struct arch_seccomp_data& args);
 
-  // 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.
-  // The calling process must specify its current SandboxThreadState, as a way
-  // to tell the sandbox which type of kernel support it should engage.
-  // 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.
-  bool StartSandbox(SandboxThreadState thread_state) WARN_UNUSED_RESULT;
-
   // 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
@@ -121,34 +102,11 @@ class SANDBOX_EXPORT SandboxBPF {
   scoped_ptr<CodeGen::Program> AssembleFilter(bool force_verification);
 
  private:
-  // Get a file descriptor pointing to "/proc", if currently available.
-  int proc_fd() { return proc_fd_; }
-
-  // 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)(),
-                           scoped_ptr<bpf_dsl::Policy> policy);
-
-  // 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();
-
   // Assembles and installs a filter based on the policy that has previously
   // been configured with SetSandboxPolicy().
   void InstallFilter(bool must_sync_threads);
 
-  // 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 CodeGen::Program& program);
-
-  static SandboxStatus status_;
-
-  bool quiet_;
-  int proc_fd_;
+  base::ScopedFD proc_task_fd_;
   bool sandbox_has_started_;
   scoped_ptr<bpf_dsl::Policy> policy_;
 
@@ -157,4 +115,4 @@ class SANDBOX_EXPORT SandboxBPF {
 
 }  // namespace sandbox
 
-#endif  // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
+#endif  // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
@@ -31,20 +31,12 @@ void SandboxBPFTestRunner::Run() {
   scoped_ptr<bpf_dsl::Policy> policy =
       bpf_tester_delegate_->GetSandboxBPFPolicy();
 
-  if (sandbox::SandboxBPF::SupportsSeccompSandbox(-1) ==
-      sandbox::SandboxBPF::STATUS_AVAILABLE) {
-    // Ensure the the sandbox is actually available at this time
-    int proc_fd;
-    SANDBOX_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0);
-    SANDBOX_ASSERT(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) ==
-                   sandbox::SandboxBPF::STATUS_AVAILABLE);
-
+  if (sandbox::SandboxBPF::SupportsSeccompSandbox(
+          SandboxBPF::SeccompLevel::SINGLE_THREADED)) {
     // Initialize and then start the sandbox with our custom policy
-    sandbox::SandboxBPF sandbox;
-    sandbox.set_proc_fd(proc_fd);
-    sandbox.SetSandboxPolicy(policy.release());
-    SANDBOX_ASSERT(
-        sandbox.StartSandbox(sandbox::SandboxBPF::PROCESS_SINGLE_THREADED));
+    sandbox::SandboxBPF sandbox(policy.release());
+    SANDBOX_ASSERT(sandbox.StartSandbox(
+        sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED));
 
     // Run the actual test.
     bpf_tester_delegate_->RunTestFunction();
@@ -58,8 +50,7 @@ void SandboxBPFTestRunner::Run() {
     }
     // Call the compiler and verify the policy. That's the least we can do,
     // if we don't have kernel support.
-    sandbox::SandboxBPF sandbox;
-    sandbox.SetSandboxPolicy(policy.release());
+    sandbox::SandboxBPF sandbox(policy.release());
     sandbox.AssembleFilter(true /* force_verification */);
     sandbox::UnitTests::IgnoreThisTest();
   }
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
@@ -45,11 +45,11 @@ class SandboxBPFTestRunner : public Sand
   // This constructor takes ownership of the |bpf_tester_delegate| object.
   // (It doesn't take a scoped_ptr since they make polymorphism verbose).
   explicit SandboxBPFTestRunner(BPFTesterDelegate* bpf_tester_delegate);
-  virtual ~SandboxBPFTestRunner();
+  ~SandboxBPFTestRunner() override;
 
-  virtual void Run() override;
+  void Run() override;
 
-  virtual bool ShouldCheckForLeaks() const override;
+  bool ShouldCheckForLeaks() const override;
 
  private:
   scoped_ptr<BPFTesterDelegate> bpf_tester_delegate_;
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <iostream>
+
+#include "base/files/scoped_file.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+// NOTE: most tests for the SandboxBPF class are currently in
+// bpf_dsl_more_unittest.cc.
+
+TEST(SandboxBPF, CreateDestroy) {
+  // Give an opportunity to dynamic tools to perform some simple testing.
+  SandboxBPF sandbox(nullptr);
+  SandboxBPF* sandbox_ptr = new SandboxBPF(nullptr);
+  delete sandbox_ptr;
+}
+
+// This test should execute no matter whether we have kernel support. So,
+// we make it a TEST() instead of a BPF_TEST().
+TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupports)) {
+  // We check that we don't crash, but it's ok if the kernel doesn't
+  // support it.
+  bool seccomp_bpf_supported = SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::SINGLE_THREADED);
+  bool seccomp_bpf_tsync_supported = SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::MULTI_THREADED);
+  // We want to log whether or not seccomp BPF is actually supported
+  // since actual test coverage depends on it.
+  std::cout << "Seccomp BPF supported (single thread): "
+            << (seccomp_bpf_supported ? "true." : "false.") << "\n";
+  std::cout << "Seccomp BPF supported (multi thread): "
+            << (seccomp_bpf_tsync_supported ? "true." : "false.") << "\n";
+  std::cout << "Pointer size: " << sizeof(void*) << "\n";
+}
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupportsTwice)) {
+  bool single1 = SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::SINGLE_THREADED);
+  bool single2 = SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::SINGLE_THREADED);
+  ASSERT_EQ(single1, single2);
+  bool multi1 = SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::MULTI_THREADED);
+  bool multi2 = SandboxBPF::SupportsSeccompSandbox(
+      SandboxBPF::SeccompLevel::MULTI_THREADED);
+  ASSERT_EQ(multi1, multi2);
+
+  // Multi threaded support implies single threaded support.
+  if (multi1) {
+    ASSERT_TRUE(single1);
+  }
+}
+
+TEST(SandboxBPF, ProcTaskFdDescriptorGetsClosed) {
+  int pipe_fds[2];
+  ASSERT_EQ(0, pipe(pipe_fds));
+  base::ScopedFD read_end(pipe_fds[0]);
+  base::ScopedFD write_end(pipe_fds[1]);
+
+  {
+    SandboxBPF sandbox(nullptr);
+    sandbox.SetProcTaskFd(write_end.Pass());
+  }
+
+  ASSERT_EQ(0, fcntl(read_end.get(), F_SETFL, O_NONBLOCK));
+  char c;
+  // Check that the sandbox closed the write_end (read will EOF instead of
+  // returning EWOULDBLOCK).
+  ASSERT_EQ(0, read(read_end.get(), &c, 1));
+}
+
+}  // namespace
+}  // sandbox
--- a/sandbox/linux/seccomp-bpf/syscall.cc
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -279,8 +279,8 @@ intptr_t Syscall::Call(int nr,
   // 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);
+  static_assert(sizeof(void*) == sizeof(intptr_t),
+                "pointer types and intptr_t must be exactly the same size");
 
   // TODO(nedeljko): Enable use of more than six parameters on architectures
   //                 where that makes sense.
--- a/sandbox/linux/seccomp-bpf/syscall_iterator.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -14,10 +14,11 @@ namespace {
 
 #if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
 // This is true for Mips O32 ABI.
-COMPILE_ASSERT(MIN_SYSCALL == __NR_Linux, min_syscall_should_be_4000);
+static_assert(MIN_SYSCALL == __NR_Linux, "min syscall number should be 4000");
 #else
 // This true for supported architectures (Intel and ARM EABI).
-COMPILE_ASSERT(MIN_SYSCALL == 0u, min_syscall_should_always_be_zero);
+static_assert(MIN_SYSCALL == 0u,
+              "min syscall should always be zero");
 #endif
 
 // SyscallRange represents an inclusive range of system call numbers.
--- a/sandbox/linux/seccomp-bpf/syscall_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -108,9 +108,9 @@ intptr_t CopySyscallArgsToAux(const stru
 class CopyAllArgsOnUnamePolicy : public bpf_dsl::Policy {
  public:
   explicit CopyAllArgsOnUnamePolicy(std::vector<uint64_t>* aux) : aux_(aux) {}
-  virtual ~CopyAllArgsOnUnamePolicy() {}
+  ~CopyAllArgsOnUnamePolicy() override {}
 
-  virtual ResultExpr EvaluateSyscall(int sysno) const override {
+  ResultExpr EvaluateSyscall(int sysno) const override {
     DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
     if (sysno == __NR_uname) {
       return Trap(CopySyscallArgsToAux, aux_);
--- a/sandbox/linux/seccomp-bpf/trap.h
+++ b/sandbox/linux/seccomp-bpf/trap.h
@@ -26,9 +26,9 @@ namespace sandbox {
 //   true. Threads are incompatible with the seccomp sandbox anyway.
 class SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry {
  public:
-  virtual uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override;
+  uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override;
 
-  virtual bool EnableUnsafeTraps() override;
+  bool EnableUnsafeTraps() override;
 
   // Registry returns the trap registry used by Trap's SIGSYS handler,
   // creating it if necessary.
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -4,13 +4,10 @@
 
 #include "sandbox/linux/services/credentials.h"
 
-#include <dirent.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <sys/capability.h>
-#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -24,6 +21,7 @@
 #include "base/template_util.h"
 #include "base/third_party/valgrind/valgrind.h"
 #include "base/threading/thread.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 
 namespace {
 
@@ -60,16 +58,8 @@ struct FILECloser {
 // TODO(jln): fix base/.
 typedef scoped_ptr<FILE, FILECloser> ScopedFILE;
 
-struct DIRCloser {
-  void operator()(DIR* d) const {
-    DCHECK(d);
-    PCHECK(0 == closedir(d));
-  }
-};
-
-typedef scoped_ptr<DIR, DIRCloser> ScopedDIR;
-
-COMPILE_ASSERT((base::is_same<uid_t, gid_t>::value), UidAndGidAreSameType);
+static_assert((base::is_same<uid_t, gid_t>::value),
+              "uid_t and gid_t should be the same type");
 // generic_id_t can be used for either uid_t or gid_t.
 typedef uid_t generic_id_t;
 
@@ -111,8 +101,8 @@ void ChrootToThreadFdInfo(base::Platform
   DCHECK(result);
   *result = false;
 
-  COMPILE_ASSERT((base::is_same<base::PlatformThreadId, int>::value),
-                 TidIsAnInt);
+  static_assert((base::is_same<base::PlatformThreadId, int>::value),
+                "platform thread id should be an int");
   const std::string current_thread_fdinfo = "/proc/" +
       base::IntToString(tid) + "/fdinfo/";
 
@@ -167,101 +157,16 @@ void CheckCloneNewUserErrno(int error) {
 
 namespace sandbox {
 
-Credentials::Credentials() {
-}
-
-Credentials::~Credentials() {
-}
-
-int Credentials::CountOpenFds(int proc_fd) {
-  DCHECK_LE(0, proc_fd);
-  int proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
-  PCHECK(0 <= proc_self_fd);
-
-  // Ownership of proc_self_fd is transferred here, it must not be closed
-  // or modified afterwards except via dir.
-  ScopedDIR dir(fdopendir(proc_self_fd));
-  CHECK(dir);
-
-  int count = 0;
-  struct dirent e;
-  struct dirent* de;
-  while (!readdir_r(dir.get(), &e, &de) && de) {
-    if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
-      continue;
-    }
-
-    int fd_num;
-    CHECK(base::StringToInt(e.d_name, &fd_num));
-    if (fd_num == proc_fd || fd_num == proc_self_fd) {
-      continue;
-    }
-
-    ++count;
-  }
-  return count;
-}
-
-bool Credentials::HasOpenDirectory(int proc_fd) {
-  int proc_self_fd = -1;
-  if (proc_fd >= 0) {
-    proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
-  } else {
-    proc_self_fd = openat(AT_FDCWD, "/proc/self/fd", O_DIRECTORY | O_RDONLY);
-    if (proc_self_fd < 0) {
-      // If this process has been chrooted (eg into /proc/self/fdinfo) then
-      // the new root dir will not have directory listing permissions for us
-      // (hence EACCES).  And if we do have this permission, then /proc won't
-      // exist anyway (hence ENOENT).
-      DPCHECK(errno == EACCES || errno == ENOENT)
-        << "Unexpected failure when trying to open /proc/self/fd: ("
-        << errno << ") " << strerror(errno);
-
-      // If not available, guess false.
-      return false;
-    }
-  }
-  PCHECK(0 <= proc_self_fd);
-
-  // Ownership of proc_self_fd is transferred here, it must not be closed
-  // or modified afterwards except via dir.
-  ScopedDIR dir(fdopendir(proc_self_fd));
-  CHECK(dir);
-
-  struct dirent e;
-  struct dirent* de;
-  while (!readdir_r(dir.get(), &e, &de) && de) {
-    if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
-      continue;
-    }
-
-    int fd_num;
-    CHECK(base::StringToInt(e.d_name, &fd_num));
-    if (fd_num == proc_fd || fd_num == proc_self_fd) {
-      continue;
-    }
-
-    struct stat s;
-    // It's OK to use proc_self_fd here, fstatat won't modify it.
-    CHECK(fstatat(proc_self_fd, e.d_name, &s, 0) == 0);
-    if (S_ISDIR(s.st_mode)) {
-      return true;
-    }
-  }
-
-  // No open unmanaged directories found.
-  return false;
-}
-
 bool Credentials::DropAllCapabilities() {
   ScopedCap cap(cap_init());
   CHECK(cap);
   PCHECK(0 == cap_set_proc(cap.get()));
+  CHECK(!HasAnyCapability());
   // We never let this function fail.
   return true;
 }
 
-bool Credentials::HasAnyCapability() const {
+bool Credentials::HasAnyCapability() {
   ScopedCap current_cap(cap_get_proc());
   CHECK(current_cap);
   ScopedCap empty_cap(cap_init());
@@ -269,7 +174,7 @@ bool Credentials::HasAnyCapability() con
   return cap_compare(current_cap.get(), empty_cap.get()) != 0;
 }
 
-scoped_ptr<std::string> Credentials::GetCurrentCapString() const {
+scoped_ptr<std::string> Credentials::GetCurrentCapString() {
   ScopedCap current_cap(cap_get_proc());
   CHECK(current_cap);
   ScopedCapText cap_text(cap_to_text(current_cap.get(), NULL));
@@ -286,7 +191,7 @@ bool Credentials::SupportsNewUserNS() {
   }
 
   // This is roughly a fork().
-  const pid_t pid = syscall(__NR_clone, CLONE_NEWUSER | SIGCHLD, 0, 0, 0);
+  const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0);
 
   if (pid == -1) {
     CheckCloneNewUserErrno(errno);
@@ -338,9 +243,6 @@ bool Credentials::MoveToNewUserNS() {
 }
 
 bool Credentials::DropFileSystemAccess() {
-  // Chrooting to a safe empty dir will only be safe if no directory file
-  // descriptor is available to the process.
-  DCHECK(!HasOpenDirectory(-1));
   return ChrootToSafeEmptyDir();
 }
 
--- a/sandbox/linux/services/credentials.h
+++ b/sandbox/linux/services/credentials.h
@@ -14,6 +14,7 @@
 #include <string>
 
 #include "base/basictypes.h"
+#include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
 #include "sandbox/sandbox_export.h"
 
@@ -24,38 +25,16 @@ namespace sandbox {
 // implemented by the Linux kernel.
 class SANDBOX_EXPORT Credentials {
  public:
-  Credentials();
-  ~Credentials();
-
-  // Returns the number of file descriptors in the current process's FD
-  // table, excluding |proc_fd|, which should be a file descriptor for
-  // /proc.
-  int CountOpenFds(int proc_fd);
-
-  // Checks whether the current process has any directory file descriptor open.
-  // Directory file descriptors are "capabilities" that would let a process use
-  // system calls such as openat() to bypass restrictions such as
-  // DropFileSystemAccess().
-  // Sometimes it's useful to call HasOpenDirectory() after file system access
-  // has been dropped. In this case, |proc_fd| should be a file descriptor to
-  // /proc. The file descriptor in |proc_fd| will be ignored by
-  // HasOpenDirectory() and remains owned by the caller. It is very important
-  // for the caller to close it.
-  // If /proc is available, |proc_fd| can be passed as -1.
-  // If |proc_fd| is -1 and /proc is not available, this function will return
-  // false.
-  bool HasOpenDirectory(int proc_fd);
-
   // Drop all capabilities in the effective, inheritable and permitted sets for
   // the current process.
-  bool DropAllCapabilities();
+  static bool DropAllCapabilities() WARN_UNUSED_RESULT;
   // Return true iff there is any capability in any of the capabilities sets
   // of the current process.
-  bool HasAnyCapability() const;
+  static bool HasAnyCapability();
   // Returns the capabilities of the current process in textual form, as
   // documented in libcap2's cap_to_text(3). This is mostly useful for
   // debugging and tests.
-  scoped_ptr<std::string> GetCurrentCapString() const;
+  static scoped_ptr<std::string> GetCurrentCapString();
 
   // Returns whether the kernel supports CLONE_NEWUSER and whether it would be
   // possible to immediately move to a new user namespace. There is no point
@@ -70,7 +49,7 @@ class SANDBOX_EXPORT Credentials {
   // change.
   // If this call succeeds, the current process will be granted a full set of
   // capabilities in the new namespace.
-  bool MoveToNewUserNS();
+  static bool MoveToNewUserNS() WARN_UNUSED_RESULT;
 
   // Remove the ability of the process to access the file system. File
   // descriptors which are already open prior to calling this API remain
@@ -79,12 +58,14 @@ class SANDBOX_EXPORT Credentials {
   // CAP_SYS_CHROOT can be acquired by using the MoveToNewUserNS() API.
   // Make sure to call DropAllCapabilities() after this call to prevent
   // escapes.
-  // To be secure, it's very important for this API to not be called while the
-  // process has any directory file descriptor open.
-  bool DropFileSystemAccess();
+  // To be secure, the caller must ensure that any directory file descriptors
+  // are closed (for example, by checking the result of
+  // ProcUtil::HasOpenDirectory with a file descriptor for /proc, then closing
+  // that file descriptor). Otherwise it may be possible to escape the chroot.
+  static bool DropFileSystemAccess() WARN_UNUSED_RESULT;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(Credentials);
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Credentials);
 };
 
 }  // namespace sandbox.
--- a/sandbox/linux/services/credentials_unittest.cc
+++ b/sandbox/linux/services/credentials_unittest.cc
@@ -48,82 +48,20 @@ bool WorkingDirectoryIsRoot() {
   return true;
 }
 
-// Give dynamic tools a simple thing to test.
-TEST(Credentials, CreateAndDestroy) {
-  {
-    Credentials cred1;
-    (void) cred1;
-  }
-  scoped_ptr<Credentials> cred2(new Credentials);
-}
-
-TEST(Credentials, CountOpenFds) {
-  base::ScopedFD proc_fd(open("/proc", O_RDONLY | O_DIRECTORY));
-  ASSERT_TRUE(proc_fd.is_valid());
-  Credentials creds;
-  int fd_count = creds.CountOpenFds(proc_fd.get());
-  int fd = open("/dev/null", O_RDONLY);
-  ASSERT_LE(0, fd);
-  EXPECT_EQ(fd_count + 1, creds.CountOpenFds(proc_fd.get()));
-  ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
-  EXPECT_EQ(fd_count, creds.CountOpenFds(proc_fd.get()));
-}
-
-TEST(Credentials, HasOpenDirectory) {
-  Credentials creds;
-  // No open directory should exist at startup.
-  EXPECT_FALSE(creds.HasOpenDirectory(-1));
-  {
-    // Have a "/dev" file descriptor around.
-    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    base::ScopedFD dev_fd_closer(dev_fd);
-    EXPECT_TRUE(creds.HasOpenDirectory(-1));
-  }
-  EXPECT_FALSE(creds.HasOpenDirectory(-1));
-}
-
-TEST(Credentials, HasOpenDirectoryWithFD) {
-  Credentials creds;
-
-  int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
-  base::ScopedFD proc_fd_closer(proc_fd);
-  ASSERT_LE(0, proc_fd);
-
-  // Don't pass |proc_fd|, an open directory (proc_fd) should
-  // be detected.
-  EXPECT_TRUE(creds.HasOpenDirectory(-1));
-  // Pass |proc_fd| and no open directory should be detected.
-  EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
-
-  {
-    // Have a "/dev" file descriptor around.
-    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    base::ScopedFD dev_fd_closer(dev_fd);
-    EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
-  }
-
-  // The "/dev" file descriptor should now be closed, |proc_fd| is the only
-  // directory file descriptor open.
-  EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
-}
-
 SANDBOX_TEST(Credentials, DropAllCaps) {
-  Credentials creds;
-  CHECK(creds.DropAllCapabilities());
-  CHECK(!creds.HasAnyCapability());
+  CHECK(Credentials::DropAllCapabilities());
+  CHECK(!Credentials::HasAnyCapability());
 }
 
 SANDBOX_TEST(Credentials, GetCurrentCapString) {
-  Credentials creds;
-  CHECK(creds.DropAllCapabilities());
+  CHECK(Credentials::DropAllCapabilities());
   const char kNoCapabilityText[] = "=";
-  CHECK(*creds.GetCurrentCapString() == kNoCapabilityText);
+  CHECK(*Credentials::GetCurrentCapString() == kNoCapabilityText);
 }
 
 SANDBOX_TEST(Credentials, MoveToNewUserNS) {
-  Credentials creds;
-  creds.DropAllCapabilities();
-  bool moved_to_new_ns = creds.MoveToNewUserNS();
+  CHECK(Credentials::DropAllCapabilities());
+  bool moved_to_new_ns = Credentials::MoveToNewUserNS();
   fprintf(stdout,
           "Unprivileged CLONE_NEWUSER supported: %s\n",
           moved_to_new_ns ? "true." : "false.");
@@ -134,28 +72,26 @@ SANDBOX_TEST(Credentials, MoveToNewUserN
     fflush(stdout);
     return;
   }
-  CHECK(creds.HasAnyCapability());
-  creds.DropAllCapabilities();
-  CHECK(!creds.HasAnyCapability());
+  CHECK(Credentials::HasAnyCapability());
+  CHECK(Credentials::DropAllCapabilities());
+  CHECK(!Credentials::HasAnyCapability());
 }
 
 SANDBOX_TEST(Credentials, SupportsUserNS) {
-  Credentials creds;
-  creds.DropAllCapabilities();
+  CHECK(Credentials::DropAllCapabilities());
   bool user_ns_supported = Credentials::SupportsNewUserNS();
-  bool moved_to_new_ns = creds.MoveToNewUserNS();
+  bool moved_to_new_ns = Credentials::MoveToNewUserNS();
   CHECK_EQ(user_ns_supported, moved_to_new_ns);
 }
 
 SANDBOX_TEST(Credentials, UidIsPreserved) {
-  Credentials creds;
-  creds.DropAllCapabilities();
+  CHECK(Credentials::DropAllCapabilities());
   uid_t old_ruid, old_euid, old_suid;
   gid_t old_rgid, old_egid, old_sgid;
   PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
   PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
   // Probably missing kernel support.
-  if (!creds.MoveToNewUserNS()) return;
+  if (!Credentials::MoveToNewUserNS()) return;
   uid_t new_ruid, new_euid, new_suid;
   PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
   CHECK(old_ruid == new_ruid);
@@ -169,27 +105,25 @@ SANDBOX_TEST(Credentials, UidIsPreserved
   CHECK(old_sgid == new_sgid);
 }
 
-bool NewUserNSCycle(Credentials* creds) {
-  DCHECK(creds);
-  if (!creds->MoveToNewUserNS() ||
-      !creds->HasAnyCapability() ||
-      !creds->DropAllCapabilities() ||
-      creds->HasAnyCapability()) {
+bool NewUserNSCycle() {
+  if (!Credentials::MoveToNewUserNS() ||
+      !Credentials::HasAnyCapability() ||
+      !Credentials::DropAllCapabilities() ||
+      Credentials::HasAnyCapability()) {
     return false;
   }
   return true;
 }
 
 SANDBOX_TEST(Credentials, NestedUserNS) {
-  Credentials creds;
-  CHECK(creds.DropAllCapabilities());
+  CHECK(Credentials::DropAllCapabilities());
   // Probably missing kernel support.
-  if (!creds.MoveToNewUserNS()) return;
-  creds.DropAllCapabilities();
+  if (!Credentials::MoveToNewUserNS()) return;
+  CHECK(Credentials::DropAllCapabilities());
   // As of 3.12, the kernel has a limit of 32. See create_user_ns().
   const int kNestLevel = 10;
   for (int i = 0; i < kNestLevel; ++i) {
-    CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration "
+    CHECK(NewUserNSCycle()) << "Creating new user NS failed at iteration "
                                   << i << ".";
   }
 }
@@ -203,11 +137,10 @@ TEST(Credentials, CanDetectRoot) {
 }
 
 SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(DropFileSystemAccessIsSafe)) {
-  Credentials creds;
-  CHECK(creds.DropAllCapabilities());
+  CHECK(Credentials::DropAllCapabilities());
   // Probably missing kernel support.
-  if (!creds.MoveToNewUserNS()) return;
-  CHECK(creds.DropFileSystemAccess());
+  if (!Credentials::MoveToNewUserNS()) return;
+  CHECK(Credentials::DropFileSystemAccess());
   CHECK(!DirectoryExists("/proc"));
   CHECK(WorkingDirectoryIsRoot());
   // We want the chroot to never have a subdirectory. A subdirectory
@@ -218,17 +151,16 @@ SANDBOX_TEST(Credentials, DISABLE_ON_LSA
 // Check that after dropping filesystem access and dropping privileges
 // it is not possible to regain capabilities.
 SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(CannotRegainPrivileges)) {
-  Credentials creds;
-  CHECK(creds.DropAllCapabilities());
+  CHECK(Credentials::DropAllCapabilities());
   // Probably missing kernel support.
-  if (!creds.MoveToNewUserNS()) return;
-  CHECK(creds.DropFileSystemAccess());
-  CHECK(creds.DropAllCapabilities());
+  if (!Credentials::MoveToNewUserNS()) return;
+  CHECK(Credentials::DropFileSystemAccess());
+  CHECK(Credentials::DropAllCapabilities());
 
   // The kernel should now prevent us from regaining capabilities because we
   // are in a chroot.
   CHECK(!Credentials::SupportsNewUserNS());
-  CHECK(!creds.MoveToNewUserNS());
+  CHECK(!Credentials::MoveToNewUserNS());
 }
 
 }  // namespace.
--- a/sandbox/linux/services/linux_syscalls.h
+++ b/sandbox/linux/services/linux_syscalls.h
@@ -21,10 +21,14 @@
 #include "sandbox/linux/services/arm_linux_syscalls.h"
 #endif
 
-#if defined(__mips__) && defined(_ABIO32)
+#if defined(__mips__) && (_MIPS_SIM == _ABIO32)
 #include "sandbox/linux/services/mips_linux_syscalls.h"
 #endif
 
+#if defined(__mips__) && (_MIPS_SIM == _ABI64)
+#include "sandbox/linux/services/mips64_linux_syscalls.h"
+#endif
+
 #if defined(__aarch64__)
 #include "sandbox/linux/services/arm64_linux_syscalls.h"
 #endif
--- /dev/null
+++ b/sandbox/linux/services/mips64_linux_syscalls.h
@@ -0,0 +1,1266 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's calls.S.
+#ifndef SANDBOX_LINUX_SERVICES_MIPS64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_MIPS64_LINUX_SYSCALLS_H_
+
+#if !defined(__mips__) || (_MIPS_SIM != _ABI64)
+#error "Including header on wrong architecture"
+#endif
+
+// __NR_Linux, is defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+
+#if !defined(__NR_read)
+#define __NR_read (__NR_Linux + 0)
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write (__NR_Linux + 1)
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open (__NR_Linux + 2)
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close (__NR_Linux + 3)
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat (__NR_Linux + 4)
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat (__NR_Linux + 5)
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat (__NR_Linux + 6)
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll (__NR_Linux + 7)
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek (__NR_Linux + 8)
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap (__NR_Linux + 9)
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect (__NR_Linux + 10)
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap (__NR_Linux + 11)
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk (__NR_Linux + 12)
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction (__NR_Linux + 13)
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask (__NR_Linux + 14)
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl (__NR_Linux + 15)
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 (__NR_Linux + 16)
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 (__NR_Linux + 17)
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv (__NR_Linux + 18)
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev (__NR_Linux + 19)
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access (__NR_Linux + 20)
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe (__NR_Linux + 21)
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect (__NR_Linux + 22)
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield (__NR_Linux + 23)
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap (__NR_Linux + 24)
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync (__NR_Linux + 25)
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore (__NR_Linux + 26)
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise (__NR_Linux + 27)
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget (__NR_Linux + 28)
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat (__NR_Linux + 29)
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl (__NR_Linux + 30)
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup (__NR_Linux + 31)
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 (__NR_Linux + 32)
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause (__NR_Linux + 33)
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep (__NR_Linux + 34)
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer (__NR_Linux + 35)
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer (__NR_Linux + 36)
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm (__NR_Linux + 37)
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid (__NR_Linux + 38)
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile (__NR_Linux + 39)
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket (__NR_Linux + 40)
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect (__NR_Linux + 41)
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept (__NR_Linux + 42)
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto (__NR_Linux + 43)
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom (__NR_Linux + 44)
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg (__NR_Linux + 45)
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg (__NR_Linux + 46)
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown (__NR_Linux + 47)
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind (__NR_Linux + 48)
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen (__NR_Linux + 49)
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname (__NR_Linux + 50)
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername (__NR_Linux + 51)
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair (__NR_Linux + 52)
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt (__NR_Linux + 53)
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt (__NR_Linux + 54)
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone (__NR_Linux + 55)
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork (__NR_Linux + 56)
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve (__NR_Linux + 57)
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit (__NR_Linux + 58)
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 (__NR_Linux + 59)
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill (__NR_Linux + 60)
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname (__NR_Linux + 61)
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget (__NR_Linux + 62)
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop (__NR_Linux + 63)
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl (__NR_Linux + 64)
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt (__NR_Linux + 65)
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget (__NR_Linux + 66)
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd (__NR_Linux + 67)
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv (__NR_Linux + 68)
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl (__NR_Linux + 69)
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl (__NR_Linux + 70)
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock (__NR_Linux + 71)
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync (__NR_Linux + 72)
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync (__NR_Linux + 73)
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate (__NR_Linux + 74)
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate (__NR_Linux + 75)
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents (__NR_Linux + 76)
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd (__NR_Linux + 77)
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir (__NR_Linux + 78)
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir (__NR_Linux + 79)
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename (__NR_Linux + 80)
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir (__NR_Linux + 81)
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir (__NR_Linux + 82)
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat (__NR_Linux + 83)
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link (__NR_Linux + 84)
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink (__NR_Linux + 85)
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink (__NR_Linux + 86)
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink (__NR_Linux + 87)
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod (__NR_Linux + 88)
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod (__NR_Linux + 89)
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown (__NR_Linux + 90)
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown (__NR_Linux + 91)
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown (__NR_Linux + 92)
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask (__NR_Linux + 93)
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday (__NR_Linux + 94)
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit (__NR_Linux + 95)
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage (__NR_Linux + 96)
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo (__NR_Linux + 97)
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times (__NR_Linux + 98)
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace (__NR_Linux + 99)
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid (__NR_Linux + 100)
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog (__NR_Linux + 101)
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid (__NR_Linux + 102)
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid (__NR_Linux + 103)
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid (__NR_Linux + 104)
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid (__NR_Linux + 105)
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid (__NR_Linux + 106)
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid (__NR_Linux + 107)
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid (__NR_Linux + 108)
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp (__NR_Linux + 109)
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid (__NR_Linux + 110)
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid (__NR_Linux + 111)
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid (__NR_Linux + 112)
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups (__NR_Linux + 113)
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups (__NR_Linux + 114)
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid (__NR_Linux + 115)
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid (__NR_Linux + 116)
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid (__NR_Linux + 117)
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid (__NR_Linux + 118)
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid (__NR_Linux + 119)
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid (__NR_Linux + 120)
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid (__NR_Linux + 121)
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid (__NR_Linux + 122)
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget (__NR_Linux + 123)
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset (__NR_Linux + 124)
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending (__NR_Linux + 125)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait (__NR_Linux + 126)
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo (__NR_Linux + 127)
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend (__NR_Linux + 128)
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack (__NR_Linux + 129)
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime (__NR_Linux + 130)
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod (__NR_Linux + 131)
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality (__NR_Linux + 132)
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat (__NR_Linux + 133)
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs (__NR_Linux + 134)
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs (__NR_Linux + 135)
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs (__NR_Linux + 136)
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority (__NR_Linux + 137)
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority (__NR_Linux + 138)
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam (__NR_Linux + 139)
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam (__NR_Linux + 140)
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler (__NR_Linux + 141)
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler (__NR_Linux + 142)
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max (__NR_Linux + 143)
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min (__NR_Linux + 144)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval (__NR_Linux + 145)
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock (__NR_Linux + 146)
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock (__NR_Linux + 147)
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall (__NR_Linux + 148)
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall (__NR_Linux + 149)
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup (__NR_Linux + 150)
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root (__NR_Linux + 151)
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl (__NR_Linux + 152)
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl (__NR_Linux + 153)
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex (__NR_Linux + 154)
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit (__NR_Linux + 155)
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot (__NR_Linux + 156)
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync (__NR_Linux + 157)
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct (__NR_Linux + 158)
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday (__NR_Linux + 159)
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount (__NR_Linux + 160)
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 (__NR_Linux + 161)
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon (__NR_Linux + 162)
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff (__NR_Linux + 163)
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot (__NR_Linux + 164)
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname (__NR_Linux + 165)
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname (__NR_Linux + 166)
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module (__NR_Linux + 167)
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module (__NR_Linux + 168)
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module (__NR_Linux + 169)
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms (__NR_Linux + 170)
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module (__NR_Linux + 171)
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl (__NR_Linux + 172)
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl (__NR_Linux + 173)
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg (__NR_Linux + 174)
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg (__NR_Linux + 175)
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall (__NR_Linux + 176)
+#endif
+
+#if !defined(__NR_reserved177)
+#define __NR_reserved177 (__NR_Linux + 177)
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid (__NR_Linux + 178)
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead (__NR_Linux + 179)
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr (__NR_Linux + 180)
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr (__NR_Linux + 181)
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr (__NR_Linux + 182)
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr (__NR_Linux + 183)
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr (__NR_Linux + 184)
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr (__NR_Linux + 185)
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr (__NR_Linux + 186)
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr (__NR_Linux + 187)
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr (__NR_Linux + 188)
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr (__NR_Linux + 189)
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr (__NR_Linux + 190)
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr (__NR_Linux + 191)
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill (__NR_Linux + 192)
+#endif
+
+#if !defined(__NR_reserved193)
+#define __NR_reserved193 (__NR_Linux + 193)
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex (__NR_Linux + 194)
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity (__NR_Linux + 195)
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity (__NR_Linux + 196)
+#endif
+
+#if !defined(__NR_cacheflush)
+#define __NR_cacheflush (__NR_Linux + 197)
+#endif
+
+#if !defined(__NR_cachectl)
+#define __NR_cachectl (__NR_Linux + 198)
+#endif
+
+#if !defined(__NR_sysmips)
+#define __NR_sysmips (__NR_Linux + 199)
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup (__NR_Linux + 200)
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy (__NR_Linux + 201)
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents (__NR_Linux + 202)
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit (__NR_Linux + 203)
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel (__NR_Linux + 204)
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group (__NR_Linux + 205)
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie (__NR_Linux + 206)
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create (__NR_Linux + 207)
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl (__NR_Linux + 208)
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait (__NR_Linux + 209)
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages (__NR_Linux + 210)
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn (__NR_Linux + 211)
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address (__NR_Linux + 212)
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall (__NR_Linux + 213)
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop (__NR_Linux + 214)
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 (__NR_Linux + 215)
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create (__NR_Linux + 216)
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime (__NR_Linux + 217)
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime (__NR_Linux + 218)
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun (__NR_Linux + 219)
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete (__NR_Linux + 220)
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime (__NR_Linux + 221)
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime (__NR_Linux + 222)
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres (__NR_Linux + 223)
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep (__NR_Linux + 224)
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill (__NR_Linux + 225)
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes (__NR_Linux + 226)
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind (__NR_Linux + 227)
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy (__NR_Linux + 228)
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy (__NR_Linux + 229)
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open (__NR_Linux + 230)
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink (__NR_Linux + 231)
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend (__NR_Linux + 232)
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive (__NR_Linux + 233)
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify (__NR_Linux + 234)
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr (__NR_Linux + 235)
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver (__NR_Linux + 236)
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid (__NR_Linux + 237)
+#endif
+
+/* #define __NR_sys_setaltroot (__NR_Linux + 238) */
+
+#if !defined(__NR_add_key)
+#define __NR_add_key (__NR_Linux + 239)
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key (__NR_Linux + 240)
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl (__NR_Linux + 241)
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area (__NR_Linux + 242)
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init (__NR_Linux + 243)
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch (__NR_Linux + 244)
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch (__NR_Linux + 245)
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages (__NR_Linux + 246)
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat (__NR_Linux + 247)
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat (__NR_Linux + 248)
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat (__NR_Linux + 249)
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat (__NR_Linux + 250)
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat (__NR_Linux + 251)
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat (__NR_Linux + 252)
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat (__NR_Linux + 253)
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat (__NR_Linux + 254)
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat (__NR_Linux + 255)
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat (__NR_Linux + 256)
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat (__NR_Linux + 257)
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat (__NR_Linux + 258)
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat (__NR_Linux + 259)
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 (__NR_Linux + 260)
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll (__NR_Linux + 261)
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare (__NR_Linux + 262)
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice (__NR_Linux + 263)
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range (__NR_Linux + 264)
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee (__NR_Linux + 265)
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice (__NR_Linux + 266)
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages (__NR_Linux + 267)
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list (__NR_Linux + 268)
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list (__NR_Linux + 269)
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load (__NR_Linux + 270)
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu (__NR_Linux + 271)
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait (__NR_Linux + 272)
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set (__NR_Linux + 273)
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get (__NR_Linux + 274)
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat (__NR_Linux + 275)
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd (__NR_Linux + 276)
+#endif
+
+#if !defined(__NR_timerfd)
+#define __NR_timerfd (__NR_Linux + 277)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_Linux + 278)
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate (__NR_Linux + 279)
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create (__NR_Linux + 280)
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime (__NR_Linux + 281)
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime (__NR_Linux + 282)
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 (__NR_Linux + 283)
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 (__NR_Linux + 284)
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 (__NR_Linux + 285)
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 (__NR_Linux + 286)
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 (__NR_Linux + 287)
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 (__NR_Linux + 288)
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv (__NR_Linux + 289)
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev (__NR_Linux + 290)
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo (__NR_Linux + 291)
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open (__NR_Linux + 292)
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 (__NR_Linux + 293)
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg (__NR_Linux + 294)
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init (__NR_Linux + 295)
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark (__NR_Linux + 296)
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 (__NR_Linux + 297)
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at (__NR_Linux + 298)
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at (__NR_Linux + 299)
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime (__NR_Linux + 300)
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs (__NR_Linux + 301)
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg (__NR_Linux + 302)
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns (__NR_Linux + 303)
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv (__NR_Linux + 304)
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev (__NR_Linux + 305)
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp (__NR_Linux + 306)
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module (__NR_Linux + 307)
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 (__NR_Linux + 308)
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr (__NR_Linux + 309)
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr (__NR_Linux + 310)
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 (__NR_Linux + 311)
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp (__NR_Linux + 312)
+#endif
+
+#endif  // SANDBOX_LINUX_SERVICES_MIPS64_LINUX_SYSCALLS_H_
--- a/sandbox/linux/services/mips_linux_syscalls.h
+++ b/sandbox/linux/services/mips_linux_syscalls.h
@@ -6,7 +6,7 @@
 #ifndef SANDBOX_LINUX_SERVICES_MIPS_LINUX_SYSCALLS_H_
 #define SANDBOX_LINUX_SERVICES_MIPS_LINUX_SYSCALLS_H_
 
-#if !defined(__mips__) || !defined(_ABIO32)
+#if !defined(__mips__) || (_MIPS_SIM != _ABIO32)
 #error "Including header on wrong architecture"
 #endif
 
--- /dev/null
+++ b/sandbox/linux/services/proc_util.cc
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/proc_util.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace sandbox {
+namespace {
+
+struct DIRCloser {
+  void operator()(DIR* d) const {
+    DCHECK(d);
+    PCHECK(0 == closedir(d));
+  }
+};
+
+typedef scoped_ptr<DIR, DIRCloser> ScopedDIR;
+
+}  // namespace
+
+int ProcUtil::CountOpenFds(int proc_fd) {
+  DCHECK_LE(0, proc_fd);
+  int proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
+  PCHECK(0 <= proc_self_fd);
+
+  // Ownership of proc_self_fd is transferred here, it must not be closed
+  // or modified afterwards except via dir.
+  ScopedDIR dir(fdopendir(proc_self_fd));
+  CHECK(dir);
+
+  int count = 0;
+  struct dirent e;
+  struct dirent* de;
+  while (!readdir_r(dir.get(), &e, &de) && de) {
+    if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+      continue;
+    }
+
+    int fd_num;
+    CHECK(base::StringToInt(e.d_name, &fd_num));
+    if (fd_num == proc_fd || fd_num == proc_self_fd) {
+      continue;
+    }
+
+    ++count;
+  }
+  return count;
+}
+
+bool ProcUtil::HasOpenDirectory(int proc_fd) {
+  int proc_self_fd = -1;
+  if (proc_fd >= 0) {
+    proc_self_fd = openat(proc_fd, "self/fd", O_DIRECTORY | O_RDONLY);
+  } else {
+    proc_self_fd = openat(AT_FDCWD, "/proc/self/fd", O_DIRECTORY | O_RDONLY);
+    if (proc_self_fd < 0) {
+      // If this process has been chrooted (eg into /proc/self/fdinfo) then
+      // the new root dir will not have directory listing permissions for us
+      // (hence EACCES).  And if we do have this permission, then /proc won't
+      // exist anyway (hence ENOENT).
+      DPCHECK(errno == EACCES || errno == ENOENT)
+        << "Unexpected failure when trying to open /proc/self/fd: ("
+        << errno << ") " << strerror(errno);
+
+      // If not available, guess false.
+      return false;
+    }
+  }
+  PCHECK(0 <= proc_self_fd);
+
+  // Ownership of proc_self_fd is transferred here, it must not be closed
+  // or modified afterwards except via dir.
+  ScopedDIR dir(fdopendir(proc_self_fd));
+  CHECK(dir);
+
+  struct dirent e;
+  struct dirent* de;
+  while (!readdir_r(dir.get(), &e, &de) && de) {
+    if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+      continue;
+    }
+
+    int fd_num;
+    CHECK(base::StringToInt(e.d_name, &fd_num));
+    if (fd_num == proc_fd || fd_num == proc_self_fd) {
+      continue;
+    }
+
+    struct stat s;
+    // It's OK to use proc_self_fd here, fstatat won't modify it.
+    CHECK(fstatat(proc_self_fd, e.d_name, &s, 0) == 0);
+    if (S_ISDIR(s.st_mode)) {
+      return true;
+    }
+  }
+
+  // No open unmanaged directories found.
+  return false;
+}
+
+}  // namespace sandbox
--- /dev/null
+++ b/sandbox/linux/services/proc_util.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
+#define SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+class SANDBOX_EXPORT ProcUtil {
+ public:
+  // Returns the number of file descriptors in the current process's FD
+  // table, excluding |proc_fd|, which should be a file descriptor for
+  // /proc.
+  static int CountOpenFds(int proc_fd);
+
+  // Checks whether the current process has any directory file descriptor open.
+  // Directory file descriptors are "capabilities" that would let a process use
+  // system calls such as openat() to bypass restrictions such as
+  // DropFileSystemAccess().
+  // Sometimes it's useful to call HasOpenDirectory() after file system access
+  // has been dropped. In this case, |proc_fd| should be a file descriptor to
+  // /proc. The file descriptor in |proc_fd| will be ignored by
+  // HasOpenDirectory() and remains owned by the caller. It is very important
+  // for the caller to close it.
+  // If /proc is available, |proc_fd| can be passed as -1.
+  // If |proc_fd| is -1 and /proc is not available, this function will return
+  // false.
+  static bool HasOpenDirectory(int proc_fd);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ProcUtil);
+};
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
--- /dev/null
+++ b/sandbox/linux/services/proc_util_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/proc_util.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/posix/eintr_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+TEST(ProcUtil, CountOpenFds) {
+  base::ScopedFD proc_fd(open("/proc", O_RDONLY | O_DIRECTORY));
+  ASSERT_TRUE(proc_fd.is_valid());
+  int fd_count = ProcUtil::CountOpenFds(proc_fd.get());
+  int fd = open("/dev/null", O_RDONLY);
+  ASSERT_LE(0, fd);
+  EXPECT_EQ(fd_count + 1, ProcUtil::CountOpenFds(proc_fd.get()));
+  ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
+  EXPECT_EQ(fd_count, ProcUtil::CountOpenFds(proc_fd.get()));
+}
+
+TEST(ProcUtil, HasOpenDirectory) {
+  // No open directory should exist at startup.
+  EXPECT_FALSE(ProcUtil::HasOpenDirectory(-1));
+  {
+    // Have a "/dev" file descriptor around.
+    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
+    base::ScopedFD dev_fd_closer(dev_fd);
+    EXPECT_TRUE(ProcUtil::HasOpenDirectory(-1));
+  }
+  EXPECT_FALSE(ProcUtil::HasOpenDirectory(-1));
+}
+
+TEST(ProcUtil, HasOpenDirectoryWithFD) {
+  int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
+  base::ScopedFD proc_fd_closer(proc_fd);
+  ASSERT_LE(0, proc_fd);
+
+  // Don't pass |proc_fd|, an open directory (proc_fd) should
+  // be detected.
+  EXPECT_TRUE(ProcUtil::HasOpenDirectory(-1));
+  // Pass |proc_fd| and no open directory should be detected.
+  EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
+
+  {
+    // Have a "/dev" file descriptor around.
+    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
+    base::ScopedFD dev_fd_closer(dev_fd);
+    EXPECT_TRUE(ProcUtil::HasOpenDirectory(proc_fd));
+  }
+
+  // The "/dev" file descriptor should now be closed, |proc_fd| is the only
+  // directory file descriptor open.
+  EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
+}
+
+}  // namespace sandbox
--- a/sandbox/linux/services/scoped_process.cc
+++ b/sandbox/linux/services/scoped_process.cc
@@ -17,6 +17,7 @@
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
 #include "build/build_config.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 #include "sandbox/linux/services/thread_helpers.h"
 
 namespace sandbox {
@@ -112,7 +113,7 @@ bool ScopedProcess::WaitForClosureToRun(
 // This method allows to assert it is not happening.
 bool ScopedProcess::IsOriginalProcess() {
   // Make a direct syscall to bypass glibc caching of PIDs.
-  int pid = syscall(__NR_getpid);
+  pid_t pid = sys_getpid();
   return pid == process_id_;
 }
 
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/syscall_wrappers.h"
+
+#include <pthread.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+namespace sandbox {
+
+pid_t sys_getpid(void) {
+  return syscall(__NR_getpid);
+}
+
+pid_t sys_gettid(void) {
+  return syscall(__NR_gettid);
+}
+
+namespace {
+
+bool CloneArgumentsValid(unsigned long flags, pid_t* ptid, pid_t* ctid) {
+  const bool clone_tls_used = flags & CLONE_SETTLS;
+  const bool invalid_ctid =
+      (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid;
+  const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid;
+
+  // We do not support CLONE_VM.
+  const bool clone_vm_used = flags & CLONE_VM;
+
+  return !(clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used);
+}
+
+bool IsRunningOnValgrind() {
+  return RUNNING_ON_VALGRIND;
+}
+
+// This function runs on the stack specified on the clone call. It uses longjmp
+// to switch back to the original stack so the child can return from sys_clone.
+int CloneHelper(void* arg) {
+  jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
+  longjmp(*env_ptr, 1);
+
+  // Should not be reached.
+  RAW_CHECK(false);
+  return 1;
+}
+
+// This function is noinline to ensure that stack_buf is below the stack pointer
+// that is saved when setjmp is called below. This is needed because when
+// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
+// upwards. See crbug.com/442912 for more details.
+#if defined(ADDRESS_SANITIZER)
+// Disable AddressSanitizer instrumentation for this function to make sure
+// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
+// Under ASan longjmp() will attempt to clean up the area between the old and
+// new stack pointers and print a warning that may confuse the user.
+__attribute__((no_sanitize_address))
+#endif
+NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
+                                     pid_t* ptid,
+                                     pid_t* ctid,
+                                     jmp_buf* env) {
+  // We use the libc clone wrapper instead of making the syscall
+  // directly because making the syscall may fail to update the libc's
+  // internal pid cache. The libc interface unfortunately requires
+  // specifying a new stack, so we use setjmp/longjmp to emulate
+  // fork-like behavior.
+  char stack_buf[PTHREAD_STACK_MIN];
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+    defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
+  // The stack grows downward.
+  void* stack = stack_buf + sizeof(stack_buf);
+#else
+#error "Unsupported architecture"
+#endif
+  return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
+}
+
+}  // namespace
+
+long sys_clone(unsigned long flags,
+               decltype(nullptr) child_stack,
+               pid_t* ptid,
+               pid_t* ctid,
+               decltype(nullptr) tls) {
+
+  if (!CloneArgumentsValid(flags, ptid, ctid)) {
+    RAW_LOG(FATAL, "Invalid usage of sys_clone");
+  }
+
+  // See kernel/fork.c in Linux. There is different ordering of sys_clone
+  // parameters depending on CONFIG_CLONE_BACKWARDS* configuration options.
+#if defined(ARCH_CPU_X86_64)
+  return syscall(__NR_clone, flags, child_stack, ptid, ctid, tls);
+#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM_FAMILY) || \
+    defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)
+  // CONFIG_CLONE_BACKWARDS defined.
+  return syscall(__NR_clone, flags, child_stack, ptid, tls, ctid);
+#endif
+}
+
+long sys_clone(unsigned long flags) {
+  return sys_clone(flags, nullptr, nullptr, nullptr, nullptr);
+}
+
+pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) {
+  if (!CloneArgumentsValid(flags, ptid, ctid)) {
+    RAW_LOG(FATAL, "Invalid usage of ForkWithFlags");
+  }
+
+  // Valgrind's clone implementation does not support specifiying a child_stack
+  // without CLONE_VM, so we cannot use libc's clone wrapper when running under
+  // Valgrind. As a result, the libc pid cache may be incorrect under Valgrind.
+  // See crbug.com/442817 for more details.
+  if (IsRunningOnValgrind()) {
+    return sys_clone(flags, nullptr, ptid, ctid, nullptr);
+  }
+
+  jmp_buf env;
+  if (setjmp(env) == 0) {
+    return CloneAndLongjmpInChild(flags, ptid, ctid, &env);
+  }
+
+  return 0;
+}
+
+void sys_exit_group(int status) {
+  syscall(__NR_exit_group, status);
+}
+
+int sys_seccomp(unsigned int operation,
+                unsigned int flags,
+                const struct sock_fprog* args) {
+  return syscall(__NR_seccomp, operation, flags, args);
+}
+
+int sys_prlimit64(pid_t pid,
+                  int resource,
+                  const struct rlimit64* new_limit,
+                  struct rlimit64* old_limit) {
+  return syscall(__NR_prlimit64, pid, resource, new_limit, old_limit);
+}
+
+}  // namespace sandbox
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+#define SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+
+#include <sys/types.h>
+
+#include "sandbox/sandbox_export.h"
+
+struct sock_fprog;
+struct rlimit64;
+
+namespace sandbox {
+
+// Provide direct system call wrappers for a few common system calls.
+// These are guaranteed to perform a system call and do not rely on things such
+// as caching the current pid (c.f. getpid()) unless otherwise specified.
+
+SANDBOX_EXPORT pid_t sys_getpid(void);
+
+SANDBOX_EXPORT pid_t sys_gettid(void);
+
+SANDBOX_EXPORT long sys_clone(unsigned long flags);
+
+// |regs| is not supported and must be passed as nullptr. |child_stack| must be
+// nullptr, since otherwise this function cannot safely return. As a
+// consequence, this function does not support CLONE_VM.
+SANDBOX_EXPORT long sys_clone(unsigned long flags,
+                              decltype(nullptr) child_stack,
+                              pid_t* ptid,
+                              pid_t* ctid,
+                              decltype(nullptr) regs);
+
+// A wrapper for clone with fork-like behavior, meaning that it returns the
+// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
+// as in the clone system call (the CLONE_VM flag is not supported).
+//
+// This function uses the libc clone wrapper (which updates libc's pid cache)
+// internally, so callers may expect things like getpid() to work correctly
+// after in both the child and parent. An exception is when this code is run
+// under Valgrind. Valgrind does not support the libc clone wrapper, so the libc
+// pid cache may be incorrect after this function is called under Valgrind.
+SANDBOX_EXPORT pid_t
+ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid);
+
+SANDBOX_EXPORT void sys_exit_group(int status);
+
+// The official system call takes |args| as void*  (in order to be extensible),
+// but add more typing for the cases that are currently used.
+SANDBOX_EXPORT int sys_seccomp(unsigned int operation,
+                               unsigned int flags,
+                               const struct sock_fprog* args);
+
+// Some libcs do not expose a prlimit64 wrapper.
+SANDBOX_EXPORT int sys_prlimit64(pid_t pid,
+                                 int resource,
+                                 const struct rlimit64* new_limit,
+                                 struct rlimit64* old_limit);
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/syscall_wrappers.h"
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+TEST(SyscallWrappers, BasicSyscalls) {
+  EXPECT_EQ(getpid(), sys_getpid());
+}
+
+TEST(SyscallWrappers, CloneBasic) {
+  pid_t child = sys_clone(SIGCHLD);
+  TestUtils::HandlePostForkReturn(child);
+  EXPECT_LT(0, child);
+}
+
+TEST(SyscallWrappers, CloneParentSettid) {
+  pid_t ptid = 0;
+  pid_t child = sys_clone(CLONE_PARENT_SETTID | SIGCHLD, nullptr, &ptid,
+                          nullptr, nullptr);
+  TestUtils::HandlePostForkReturn(child);
+  EXPECT_LT(0, child);
+  EXPECT_EQ(child, ptid);
+}
+
+TEST(SyscallWrappers, CloneChildSettid) {
+  pid_t ctid = 0;
+  pid_t pid =
+      sys_clone(CLONE_CHILD_SETTID | SIGCHLD, nullptr, nullptr, &ctid, nullptr);
+
+  const int kSuccessExit = 0;
+  if (0 == pid) {
+    // In child.
+    if (sys_getpid() == ctid)
+      _exit(kSuccessExit);
+    _exit(1);
+  }
+
+  ASSERT_NE(-1, pid);
+  int status = 0;
+  ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+  ASSERT_TRUE(WIFEXITED(status));
+  EXPECT_EQ(kSuccessExit, WEXITSTATUS(status));
+}
+
+TEST(SyscallWrappers, ForkWithFlagsUpdatesPidCache) {
+  // The libc clone function, which allows ForkWithFlags to keep the pid cache
+  // up to date, does not work on Valgrind.
+  if (IsRunningOnValgrind()) {
+    return;
+  }
+
+  // Warm up the libc pid cache, if there is one.
+  ASSERT_EQ(sys_getpid(), getpid());
+
+  pid_t ctid = 0;
+  pid_t pid = ForkWithFlags(CLONE_CHILD_SETTID | SIGCHLD, nullptr, &ctid);
+
+  const int kSuccessExit = 0;
+  if (0 == pid) {
+    // In child.  Check both the raw getpid syscall and the libc getpid wrapper
+    // (which may rely on a pid cache).
+    if (sys_getpid() == ctid && getpid() == ctid)
+      _exit(kSuccessExit);
+    _exit(1);
+  }
+
+  ASSERT_NE(-1, pid);
+  int status = 0;
+  ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+  ASSERT_TRUE(WIFEXITED(status));
+  EXPECT_EQ(kSuccessExit, WEXITSTATUS(status));
+}
+
+}  // namespace
+
+}  // namespace sandbox
--- a/sandbox/linux/services/thread_helpers.cc
+++ b/sandbox/linux/services/thread_helpers.cc
@@ -43,7 +43,8 @@ bool IsSingleThreadedImpl(int proc_self_
 bool ThreadHelpers::IsSingleThreaded(int proc_self_task) {
   DCHECK_LE(-1, proc_self_task);
   if (-1 == proc_self_task) {
-    const int task_fd = open("/proc/self/task/", O_RDONLY | O_DIRECTORY);
+    const int task_fd =
+        open("/proc/self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
     PCHECK(0 <= task_fd);
     const bool result = IsSingleThreadedImpl(task_fd);
     PCHECK(0 == IGNORE_EINTR(close(task_fd)));
--- a/sandbox/linux/services/unix_domain_socket_unittest.cc
+++ b/sandbox/linux/services/unix_domain_socket_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/posix/unix_domain_socket_linux.h"
 #include "base/process/process_handle.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
 #include "sandbox/linux/tests/unit_tests.h"
 
 // Additional tests for base's UnixDomainSocket to make sure it behaves
@@ -144,14 +145,14 @@ SANDBOX_TEST(UnixDomainSocketTest, Names
 
   CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
 
-  const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+  const pid_t pid = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
   CHECK_NE(-1, pid);
   if (pid == 0) {
     // Child process.
     recv_sock.reset();
 
     // Check that we think we're pid 1 in our new namespace.
-    CHECK_EQ(1, syscall(__NR_getpid));
+    CHECK_EQ(1, sys_getpid());
 
     SendHello(send_sock.get());
     _exit(0);
@@ -178,13 +179,13 @@ SANDBOX_TEST(UnixDomainSocketTest, Doubl
 
   CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
 
-  const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+  const pid_t pid = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
   CHECK_NE(-1, pid);
   if (pid == 0) {
     // Child process.
     recv_sock.reset();
 
-    const pid_t pid2 = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+    const pid_t pid2 = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
     CHECK_NE(-1, pid2);
 
     if (pid2 != 0) {
@@ -195,7 +196,7 @@ SANDBOX_TEST(UnixDomainSocketTest, Doubl
     }
 
     // Check that we think we're pid 1.
-    CHECK_EQ(1, syscall(__NR_getpid));
+    CHECK_EQ(1, sys_getpid());
 
     SendHello(send_sock.get());
     _exit(0);
@@ -244,7 +245,7 @@ SANDBOX_TEST(UnixDomainSocketTest, Impos
 
   CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
 
-  const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
+  const pid_t pid = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
   CHECK_NE(-1, pid);
   if (pid == 0) {
     // Child process.
--- a/sandbox/linux/suid/client/setuid_sandbox_client.cc
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -208,6 +208,11 @@ bool SetuidSandboxClient::ChrootMe() {
   return true;
 }
 
+bool SetuidSandboxClient::CreateNewSession() {
+  // This could fail if the process is already a process group leader.
+  return 0 < setsid();
+}
+
 bool SetuidSandboxClient::CreateInitProcessReaper(
     base::Closure* post_fork_parent_callback) {
   return sandbox::CreateInitProcessReaper(post_fork_parent_callback);
--- a/sandbox/linux/suid/client/setuid_sandbox_client.h
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.h
@@ -38,7 +38,10 @@ namespace sandbox {
 //    If B dies, all the processes in the namespace will die.
 //    B can fork() and the parent can assume the role of init(1), by using
 //    CreateInitProcessReaper().
-// 8. B requests being chroot-ed through ChrootMe() and
+// 8. B uses CreateNewSession() to move to a new session ID and process group.
+//    This prevents a sandboxed process from signaling its process group and
+//    get signals delivered across the PID namespace boundary.
+// 9. B requests being chroot-ed through ChrootMe() and
 //    requests other sandboxing status via the status functions.
 class SANDBOX_EXPORT SetuidSandboxClient {
  public:
@@ -52,6 +55,9 @@ class SANDBOX_EXPORT SetuidSandboxClient
   // to an empty directory.
   // Will only work if we have been launched through the setuid helper.
   bool ChrootMe();
+  // Create a new session and a new process group. This helps isolate processes
+  // outside of the sandbox from processes inside.
+  bool CreateNewSession();
   // When a new PID namespace is created, the process with pid == 1 should
   // assume the role of init.
   // See sandbox/linux/services/init_process_reaper.h for more information
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_channel.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// static
+void BrokerChannel::CreatePair(EndPoint* reader, EndPoint* writer) {
+  DCHECK(reader);
+  DCHECK(writer);
+  int socket_pair[2];
+  // Use SOCK_SEQPACKET, to preserve message boundaries but we also want to be
+  // notified (recvmsg should return and not block) when the connection has
+  // been broken which could mean that the other end has been closed.
+  PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair));
+
+  reader->reset(socket_pair[0]);
+  PCHECK(0 == shutdown(reader->get(), SHUT_WR));
+
+  writer->reset(socket_pair[1]);
+  PCHECK(0 == shutdown(writer->get(), SHUT_RD));
+}
+
+}  // namespace syscall_broker
+
+}  // namespace sandbox
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_channel.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// A small class to create a pipe-like communication channel. It is based on a
+// SOCK_SEQPACKET unix socket, which is connection-based and guaranteed to
+// preserve message boundaries.
+class BrokerChannel {
+ public:
+  typedef base::ScopedFD EndPoint;
+  static void CreatePair(EndPoint* reader, EndPoint* writer);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerChannel);
+};
+
+}  // namespace syscall_broker
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
--- a/sandbox/linux/syscall_broker/broker_client.cc
+++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/pickle.h"
 #include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
 #include "sandbox/linux/syscall_broker/broker_common.h"
 #include "sandbox/linux/syscall_broker/broker_policy.h"
 
@@ -53,7 +54,9 @@ int BrokerClient::PathAndFlagsSyscall(IP
   // IPC.
   if (fast_check_in_client_) {
     if (syscall_type == COMMAND_OPEN &&
-        !broker_policy_.GetFileNameIfAllowedToOpen(pathname, flags, NULL)) {
+        !broker_policy_.GetFileNameIfAllowedToOpen(
+            pathname, flags, NULL /* file_to_open */,
+            NULL /* unlink_after_open */)) {
       return -broker_policy_.denied_errno();
     }
     if (syscall_type == COMMAND_ACCESS &&
@@ -75,12 +78,9 @@ int BrokerClient::PathAndFlagsSyscall(IP
   // 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|.
-  ssize_t msg_len = UnixDomainSocket::SendRecvMsgWithFlags(ipc_channel_,
-                                                           reply_buf,
-                                                           sizeof(reply_buf),
-                                                           recvmsg_flags,
-                                                           &returned_fd,
-                                                           write_pickle);
+  ssize_t msg_len = UnixDomainSocket::SendRecvMsgWithFlags(
+      ipc_channel_.get(), reply_buf, sizeof(reply_buf), recvmsg_flags,
+      &returned_fd, write_pickle);
   if (msg_len <= 0) {
     if (!quiet_failures_for_tests_)
       RAW_LOG(ERROR, "Could not make request to broker process");
@@ -92,7 +92,7 @@ int BrokerClient::PathAndFlagsSyscall(IP
   int return_value = -1;
   // Now deserialize the return value and eventually return the file
   // descriptor.
-  if (read_pickle.ReadInt(&iter, &return_value)) {
+  if (iter.ReadInt(&return_value)) {
     switch (syscall_type) {
       case COMMAND_ACCESS:
         // We should never have a fd to return.
@@ -119,11 +119,11 @@ int BrokerClient::PathAndFlagsSyscall(IP
 }
 
 BrokerClient::BrokerClient(const BrokerPolicy& broker_policy,
-                           int ipc_channel,
+                           BrokerChannel::EndPoint ipc_channel,
                            bool fast_check_in_client,
                            bool quiet_failures_for_tests)
     : broker_policy_(broker_policy),
-      ipc_channel_(ipc_channel),
+      ipc_channel_(ipc_channel.Pass()),
       fast_check_in_client_(fast_check_in_client),
       quiet_failures_for_tests_(quiet_failures_for_tests) {
 }
--- a/sandbox/linux/syscall_broker/broker_client.h
+++ b/sandbox/linux/syscall_broker/broker_client.h
@@ -6,6 +6,7 @@
 #define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
 
 #include "base/macros.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
 #include "sandbox/linux/syscall_broker/broker_common.h"
 
 namespace sandbox {
@@ -30,7 +31,7 @@ class BrokerClient {
   // |fast_check_in_client| should be set to true and
   // |quiet_failures_for_tests| to false unless you are writing tests.
   BrokerClient(const BrokerPolicy& policy,
-               int ipc_channel,
+               BrokerChannel::EndPoint ipc_channel,
                bool fast_check_in_client,
                bool quiet_failures_for_tests);
   ~BrokerClient();
@@ -48,9 +49,12 @@ class BrokerClient {
   // This is async signal safe.
   int Open(const char* pathname, int flags) const;
 
+  // Get the file descriptor used for IPC. This is used for tests.
+  int GetIPCDescriptor() const { return ipc_channel_.get(); }
+
  private:
   const BrokerPolicy& broker_policy_;
-  const int ipc_channel_;
+  const BrokerChannel::EndPoint ipc_channel_;
   const bool fast_check_in_client_;  // Whether to forward a request that we
                                      // know will be denied to the broker. (Used
                                      // for tests).
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission.cc
@@ -0,0 +1,243 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+#include <fcntl.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// Async signal safe
+bool BrokerFilePermission::ValidatePath(const char* path) {
+  if (!path)
+    return false;
+
+  const size_t len = strlen(path);
+  // No empty paths
+  if (len == 0)
+    return false;
+  // Paths must be absolute and not relative
+  if (path[0] != '/')
+    return false;
+  // No trailing / (but "/" is valid)
+  if (len > 1 && path[len - 1] == '/')
+    return false;
+  // No trailing /..
+  if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' &&
+      path[len - 1] == '.')
+    return false;
+  // No /../ anywhere
+  for (size_t i = 0; i < len; i++) {
+    if (path[i] == '/' && (len - i) > 3) {
+      if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+// Async signal safe
+// Calls std::string::c_str(), strncmp and strlen. All these
+// methods are async signal safe in common standard libs.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::MatchPath(const char* requested_filename) const {
+  const char* path = path_.c_str();
+  if ((recursive_ && strncmp(requested_filename, path, strlen(path)) == 0)) {
+    // Note: This prefix match will allow any path under the whitelisted
+    // path, for any number of directory levels. E.g. if the whitelisted
+    // path is /good/ then the following will be permitted by the policy.
+    //   /good/file1
+    //   /good/folder/file2
+    //   /good/folder/folder2/file3
+    // If an attacker could make 'folder' a symlink to ../../ they would have
+    // access to the entire filesystem.
+    // Whitelisting with multiple depths is useful, e.g /proc/ but
+    // the system needs to ensure symlinks can not be created!
+    // That said if an attacker can convert any of the absolute paths
+    // to a symlink they can control any file on the system also.
+    return true;
+  } else if (strcmp(requested_filename, path) == 0) {
+    return true;
+  }
+  return false;
+}
+
+// Async signal safe.
+// External call to std::string::c_str() is
+// called in MatchPath.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::CheckAccess(const char* requested_filename,
+                                       int mode,
+                                       const char** file_to_access) const {
+  // First, check if |mode| is existence, ability to read or ability
+  // to write. We do not support X_OK.
+  if (mode != F_OK && mode & ~(R_OK | W_OK)) {
+    return false;
+  }
+
+  if (!ValidatePath(requested_filename))
+    return false;
+
+  if (!MatchPath(requested_filename)) {
+    return false;
+  }
+  bool allowed = false;
+  switch (mode) {
+    case F_OK:
+      if (allow_read_ || allow_write_)
+        allowed = true;
+      break;
+    case R_OK:
+      if (allow_read_)
+        allowed = true;
+      break;
+    case W_OK:
+      if (allow_write_)
+        allowed = true;
+      break;
+    case R_OK | W_OK:
+      if (allow_read_ && allow_write_)
+        allowed = true;
+      break;
+    default:
+      return false;
+  }
+
+  if (allowed && file_to_access) {
+    if (!recursive_)
+      *file_to_access = path_.c_str();
+    else
+      *file_to_access = requested_filename;
+  }
+  return allowed;
+}
+
+// Async signal safe.
+// External call to std::string::c_str() is
+// called in MatchPath.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::CheckOpen(const char* requested_filename,
+                                     int flags,
+                                     const char** file_to_open,
+                                     bool* unlink_after_open) const {
+  if (!ValidatePath(requested_filename))
+    return false;
+
+  if (!MatchPath(requested_filename)) {
+    return false;
+  }
+
+  // First, check the access mode is valid.
+  const int access_mode = flags & O_ACCMODE;
+  if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
+      access_mode != O_RDWR) {
+    return false;
+  }
+
+  // Check if read is allowed
+  if (!allow_read_ && (access_mode == O_RDONLY || access_mode == O_RDWR)) {
+    return false;
+  }
+
+  // Check if write is allowed
+  if (!allow_write_ && (access_mode == O_WRONLY || access_mode == O_RDWR)) {
+    return false;
+  }
+
+  // Check if file creation is allowed.
+  if (!allow_create_ && (flags & O_CREAT)) {
+    return false;
+  }
+
+  // If O_CREAT is present, ensure O_EXCL
+  if ((flags & O_CREAT) && !(flags & O_EXCL)) {
+    return false;
+  }
+
+  // If this file is to be unlinked, ensure it's created.
+  if (unlink_ && !(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 & kCurrentProcessOpenFlagsMask) {
+    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;
+
+  if (has_unknown_flags)
+    return false;
+
+  if (file_to_open) {
+    if (!recursive_)
+      *file_to_open = path_.c_str();
+    else
+      *file_to_open = requested_filename;
+  }
+  if (unlink_after_open)
+    *unlink_after_open = unlink_;
+
+  return true;
+}
+
+const char* BrokerFilePermission::GetErrorMessageForTests() {
+  static char kInvalidBrokerFileString[] = "Invalid BrokerFilePermission";
+  return kInvalidBrokerFileString;
+}
+
+BrokerFilePermission::BrokerFilePermission(const std::string& path,
+                                           bool recursive,
+                                           bool unlink,
+                                           bool allow_read,
+                                           bool allow_write,
+                                           bool allow_create)
+    : path_(path),
+      recursive_(recursive),
+      unlink_(unlink),
+      allow_read_(allow_read),
+      allow_write_(allow_write),
+      allow_create_(allow_create) {
+  // Validate this permission and die if invalid!
+
+  // Must have enough length for a '/'
+  CHECK(path_.length() > 0) << GetErrorMessageForTests();
+  // Whitelisted paths must be absolute.
+  CHECK(path_[0] == '/') << GetErrorMessageForTests();
+
+  // Don't allow unlinking on creation without create permission
+  if (unlink_) {
+    CHECK(allow_create) << GetErrorMessageForTests();
+  }
+  const char last_char = *(path_.rbegin());
+  // Recursive paths must have a trailing slash
+  if (recursive_) {
+    CHECK(last_char == '/') << GetErrorMessageForTests();
+  } else {
+    CHECK(last_char != '/') << GetErrorMessageForTests();
+  }
+}
+
+}  // namespace syscall_broker
+
+}  // namespace sandbox
\ No newline at end of file
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission.h
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// BrokerFilePermission defines a path for whitelisting.
+// Pick the correct static factory method to create a permission.
+// CheckOpen and CheckAccess are async signal safe.
+// Constuction and Destruction are not async signal safe.
+// |path| is the path to be whitelisted.
+class SANDBOX_EXPORT BrokerFilePermission {
+ public:
+  ~BrokerFilePermission() {}
+  BrokerFilePermission(const BrokerFilePermission&) = default;
+  BrokerFilePermission& operator=(const BrokerFilePermission&) = default;
+
+  static BrokerFilePermission ReadOnly(const std::string& path) {
+    return BrokerFilePermission(path, false, false, true, false, false);
+  }
+
+  static BrokerFilePermission ReadOnlyRecursive(const std::string& path) {
+    return BrokerFilePermission(path, true, false, true, false, false);
+  }
+
+  static BrokerFilePermission WriteOnly(const std::string& path) {
+    return BrokerFilePermission(path, false, false, false, true, false);
+  }
+
+  static BrokerFilePermission ReadWrite(const std::string& path) {
+    return BrokerFilePermission(path, false, false, true, true, false);
+  }
+
+  static BrokerFilePermission ReadWriteCreate(const std::string& path) {
+    return BrokerFilePermission(path, false, false, true, true, true);
+  }
+
+  static BrokerFilePermission ReadWriteCreateUnlink(const std::string& path) {
+    return BrokerFilePermission(path, false, true, true, true, true);
+  }
+
+  static BrokerFilePermission ReadWriteCreateUnlinkRecursive(
+      const std::string& path) {
+    return BrokerFilePermission(path, true, true, true, true, true);
+  }
+
+  // Returns true if |requested_filename| is allowed to be opened
+  // by this permission.
+  // If |file_to_open| is not NULL it is set to point to either
+  // the |requested_filename| in the case of a recursive match,
+  // or a pointer the matched path in the whitelist if an absolute
+  // match.
+  // If not NULL |unlink_after_open| is set to point to true if the
+  // caller should unlink the path after openning.
+  // Async signal safe if |file_to_open| is NULL.
+  bool CheckOpen(const char* requested_filename,
+                 int flags,
+                 const char** file_to_open,
+                 bool* unlink_after_open) const;
+  // Returns true if |requested_filename| is allowed to be accessed
+  // by this permission as per access(2).
+  // If |file_to_open| is not NULL it is set to point to either
+  // the |requested_filename| in the case of a recursive match,
+  // or a pointer to the matched path in the whitelist if an absolute
+  // match.
+  // |mode| is per mode argument of access(2).
+  // Async signal safe if |file_to_access| is NULL
+  bool CheckAccess(const char* requested_filename,
+                   int mode,
+                   const char** file_to_access) const;
+
+ private:
+  friend class BrokerFilePermissionTester;
+  BrokerFilePermission(const std::string& path,
+                       bool recursive,
+                       bool unlink,
+                       bool allow_read,
+                       bool allow_write,
+                       bool allow_create);
+
+  // ValidatePath checks |path| and returns true if these conditions are met
+  // * Greater than 0 length
+  // * Is an absolute path
+  // * No trailing slash
+  // * No /../ path traversal
+  static bool ValidatePath(const char* path);
+
+  // MatchPath returns true if |requested_filename| is covered by this instance
+  bool MatchPath(const char* requested_filename) const;
+
+  // Used in by BrokerFilePermissionTester for tests.
+  static const char* GetErrorMessageForTests();
+
+  // These are not const as std::vector requires copy-assignment and this class
+  // is stored in vectors. All methods are marked const so
+  // the compiler will still enforce no changes outside of the constructor.
+  std::string path_;
+  bool recursive_;  // Allow everything under this path. |path| must be a dir.
+  bool unlink_;     // unlink after opening.
+  bool allow_read_;
+  bool allow_write_;
+  bool allow_create_;
+};
+
+}  // namespace syscall_broker
+
+}  // namespace sandbox
+
+#endif  //  SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
\ No newline at end of file
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerFilePermissionTester {
+ public:
+  static bool ValidatePath(const char* path) {
+    return BrokerFilePermission::ValidatePath(path);
+  }
+  static const char* GetErrorMessage() {
+    return BrokerFilePermission::GetErrorMessageForTests();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrokerFilePermissionTester);
+};
+
+namespace {
+
+// Creation tests are DEATH tests as a bad permission causes termination.
+SANDBOX_TEST(BrokerFilePermission, CreateGood) {
+  const char kPath[] = "/tmp/good";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_TEST(BrokerFilePermission, CreateGoodRecursive) {
+  const char kPath[] = "/tmp/good/";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+    BrokerFilePermission,
+    CreateBad,
+    DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+  const char kPath[] = "/tmp/bad/";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+    BrokerFilePermission,
+    CreateBadRecursive,
+    DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+  const char kPath[] = "/tmp/bad";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+    BrokerFilePermission,
+    CreateBadNotAbs,
+    DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+  const char kPath[] = "tmp/bad";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+    BrokerFilePermission,
+    CreateBadEmpty,
+    DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+  const char kPath[] = "";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+// CheckPerm tests |path| against |perm| given |access_flags|.
+// If |create| is true then file creation is tested for success.
+void CheckPerm(const BrokerFilePermission& perm,
+               const char* path,
+               int access_flags,
+               bool create) {
+  const char* file_to_open = NULL;
+
+  ASSERT_FALSE(perm.CheckAccess(path, X_OK, NULL));
+  ASSERT_TRUE(perm.CheckAccess(path, F_OK, NULL));
+  // check bad perms
+  switch (access_flags) {
+    case O_RDONLY:
+      ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+      ASSERT_FALSE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+      ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+      ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
+      ASSERT_FALSE(perm.CheckAccess(path, W_OK, NULL));
+      break;
+    case O_WRONLY:
+      ASSERT_FALSE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+      ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+      ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+      ASSERT_FALSE(perm.CheckAccess(path, R_OK, NULL));
+      ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
+      break;
+    case O_RDWR:
+      ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+      ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+      ASSERT_TRUE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+      ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
+      ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
+      break;
+    default:
+      // Bad test case
+      NOTREACHED();
+  }
+
+// O_SYNC can be defined as (__O_SYNC|O_DSYNC)
+#ifdef O_DSYNC
+  const int kSyncFlag = O_SYNC & ~O_DSYNC;
+#else
+  const int kSyncFlag = O_SYNC;
+#endif
+
+  const int kNumberOfBitsInOAccMode = 2;
+  static_assert(O_ACCMODE == ((1 << kNumberOfBitsInOAccMode) - 1),
+                "incorrect number of bits");
+  // check every possible flag and act accordingly.
+  // Skipping AccMode bits as they are present in every case.
+  for (int i = kNumberOfBitsInOAccMode; i < 32; i++) {
+    int flag = 1 << i;
+    switch (flag) {
+      case O_APPEND:
+      case O_ASYNC:
+      case O_DIRECT:
+      case O_DIRECTORY:
+#ifdef O_DSYNC
+      case O_DSYNC:
+#endif
+      case O_EXCL:
+      case O_LARGEFILE:
+      case O_NOATIME:
+      case O_NOCTTY:
+      case O_NOFOLLOW:
+      case O_NONBLOCK:
+#if (O_NONBLOCK != O_NDELAY)
+      case O_NDELAY:
+#endif
+      case kSyncFlag:
+      case O_TRUNC:
+        ASSERT_TRUE(
+            perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
+        break;
+      case O_CLOEXEC:
+      case O_CREAT:
+      default:
+        ASSERT_FALSE(
+            perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
+    }
+  }
+  if (create) {
+    bool unlink;
+    ASSERT_TRUE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
+                               &file_to_open, &unlink));
+    ASSERT_FALSE(unlink);
+  } else {
+    ASSERT_FALSE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
+                                &file_to_open, NULL));
+  }
+}
+
+TEST(BrokerFilePermission, ReadOnly) {
+  const char kPath[] = "/tmp/good";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+  CheckPerm(perm, kPath, O_RDONLY, false);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerFilePermission, ReadOnlyRecursive) {
+  const char kPath[] = "/tmp/good/";
+  const char kPathFile[] = "/tmp/good/file";
+  BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+  CheckPerm(perm, kPathFile, O_RDONLY, false);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerFilePermission, WriteOnly) {
+  const char kPath[] = "/tmp/good";
+  BrokerFilePermission perm = BrokerFilePermission::WriteOnly(kPath);
+  CheckPerm(perm, kPath, O_WRONLY, false);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerFilePermission, ReadWrite) {
+  const char kPath[] = "/tmp/good";
+  BrokerFilePermission perm = BrokerFilePermission::ReadWrite(kPath);
+  CheckPerm(perm, kPath, O_RDWR, false);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerFilePermission, ReadWriteCreate) {
+  const char kPath[] = "/tmp/good";
+  BrokerFilePermission perm = BrokerFilePermission::ReadWriteCreate(kPath);
+  CheckPerm(perm, kPath, O_RDWR, true);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+void CheckUnlink(BrokerFilePermission& perm,
+                 const char* path,
+                 int access_flags) {
+  bool unlink;
+  ASSERT_FALSE(perm.CheckOpen(path, access_flags, NULL, &unlink));
+  ASSERT_FALSE(perm.CheckOpen(path, access_flags | O_CREAT, NULL, &unlink));
+  ASSERT_TRUE(
+      perm.CheckOpen(path, access_flags | O_CREAT | O_EXCL, NULL, &unlink));
+  ASSERT_TRUE(unlink);
+}
+
+TEST(BrokerFilePermission, ReadWriteCreateUnlink) {
+  const char kPath[] = "/tmp/good";
+  BrokerFilePermission perm =
+      BrokerFilePermission::ReadWriteCreateUnlink(kPath);
+  CheckUnlink(perm, kPath, O_RDWR);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerFilePermission, ReadWriteCreateUnlinkRecursive) {
+  const char kPath[] = "/tmp/good/";
+  const char kPathFile[] = "/tmp/good/file";
+  BrokerFilePermission perm =
+      BrokerFilePermission::ReadWriteCreateUnlinkRecursive(kPath);
+  CheckUnlink(perm, kPathFile, O_RDWR);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerFilePermission, ValidatePath) {
+  EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/path"));
+  EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/"));
+  EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/..path"));
+
+  EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath(""));
+  EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad"));
+  EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/"));
+  EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad/"));
+  EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/.."));
+  EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/../bad"));
+  EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/../bad"));
+}
+
+}  // namespace
+
+}  // namespace syscall_broker
+
+}  // namespace sandbox
--- a/sandbox/linux/syscall_broker/broker_host.cc
+++ b/sandbox/linux/syscall_broker/broker_host.cc
@@ -38,8 +38,13 @@ bool IsRunningOnValgrind() {
 // make a direct system call since we want to keep in control of the broker
 // process' system calls profile to be able to loosely sandbox it.
 int sys_open(const char* pathname, int flags) {
-  // Always pass a defined |mode| in case flags mistakenly contains O_CREAT.
-  const int mode = 0;
+  // Hardcode mode to rw------- when creating files.
+  int mode;
+  if (flags & O_CREAT) {
+    mode = 0600;
+  } else {
+    mode = 0;
+  }
   if (IsRunningOnValgrind()) {
     // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
     return open(pathname, flags, mode);
@@ -59,8 +64,9 @@ void OpenFileForIPC(const BrokerPolicy&
   DCHECK(write_pickle);
   DCHECK(opened_files);
   const char* file_to_open = NULL;
+  bool unlink_after_open = false;
   const bool safe_to_open_file = policy.GetFileNameIfAllowedToOpen(
-      requested_filename.c_str(), flags, &file_to_open);
+      requested_filename.c_str(), flags, &file_to_open, &unlink_after_open);
 
   if (safe_to_open_file) {
     CHECK(file_to_open);
@@ -69,6 +75,9 @@ void OpenFileForIPC(const BrokerPolicy&
       write_pickle->WriteInt(-errno);
     } else {
       // Success.
+      if (unlink_after_open) {
+        unlink(file_to_open);
+      }
       opened_files->push_back(opened_fd);
       write_pickle->WriteInt(0);
     }
@@ -101,21 +110,18 @@ void AccessFileForIPC(const BrokerPolicy
   }
 }
 
-// Handle a |command_type| request contained in |read_pickle| and send the reply
+// Handle a |command_type| request contained in |iter| and send the reply
 // on |reply_ipc|.
 // Currently COMMAND_OPEN and COMMAND_ACCESS are supported.
 bool HandleRemoteCommand(const BrokerPolicy& policy,
                          IPCCommand command_type,
                          int reply_ipc,
-                         const Pickle& read_pickle,
                          PickleIterator iter) {
   // Currently all commands have two arguments: filename and flags.
   std::string requested_filename;
   int flags = 0;
-  if (!read_pickle.ReadString(&iter, &requested_filename) ||
-      !read_pickle.ReadInt(&iter, &flags)) {
+  if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
     return false;
-  }
 
   Pickle write_pickle;
   std::vector<int> opened_files;
@@ -154,8 +160,9 @@ bool HandleRemoteCommand(const BrokerPol
 
 }  // namespace
 
-BrokerHost::BrokerHost(const BrokerPolicy& broker_policy, int ipc_channel)
-    : broker_policy_(broker_policy), ipc_channel_(ipc_channel) {
+BrokerHost::BrokerHost(const BrokerPolicy& broker_policy,
+                       BrokerChannel::EndPoint ipc_channel)
+    : broker_policy_(broker_policy), ipc_channel_(ipc_channel.Pass()) {
 }
 
 BrokerHost::~BrokerHost() {
@@ -165,17 +172,16 @@ BrokerHost::~BrokerHost() {
 // 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 BrokerHost::HandleRequest() const {
+BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
   ScopedVector<base::ScopedFD> fds;
   char buf[kMaxMessageLength];
   errno = 0;
   const ssize_t msg_len =
-      UnixDomainSocket::RecvMsg(ipc_channel_, buf, sizeof(buf), &fds);
+      UnixDomainSocket::RecvMsg(ipc_channel_.get(), buf, sizeof(buf), &fds);
 
   if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
     // EOF from the client, or the client died, we should die.
-    // TODO(jln): change this.
-    _exit(0);
+    return RequestStatus::LOST_CLIENT;
   }
 
   // The client should send exactly one file descriptor, on which we
@@ -183,7 +189,7 @@ bool BrokerHost::HandleRequest() const {
   // TODO(mdempsky): ScopedVector doesn't have 'at()', only 'operator[]'.
   if (msg_len < 0 || fds.size() != 1 || fds[0]->get() < 0) {
     PLOG(ERROR) << "Error reading message from the client";
-    return false;
+    return RequestStatus::FAILURE;
   }
 
   base::ScopedFD temporary_ipc(fds[0]->Pass());
@@ -191,29 +197,33 @@ bool BrokerHost::HandleRequest() const {
   Pickle pickle(buf, msg_len);
   PickleIterator iter(pickle);
   int command_type;
-  if (pickle.ReadInt(&iter, &command_type)) {
-    bool r = false;
+  if (iter.ReadInt(&command_type)) {
+    bool command_handled = false;
     // Go through all the possible IPC messages.
     switch (command_type) {
       case COMMAND_ACCESS:
       case COMMAND_OPEN:
         // We reply on the file descriptor sent to us via the IPC channel.
-        r = HandleRemoteCommand(broker_policy_,
-                                static_cast<IPCCommand>(command_type),
-                                temporary_ipc.get(),
-                                pickle,
-                                iter);
+        command_handled = HandleRemoteCommand(
+            broker_policy_, static_cast<IPCCommand>(command_type),
+            temporary_ipc.get(), iter);
         break;
       default:
         NOTREACHED();
-        r = false;
         break;
     }
-    return r;
+
+    if (command_handled) {
+      return RequestStatus::SUCCESS;
+    } else {
+      return RequestStatus::FAILURE;
+    }
+
+    NOTREACHED();
   }
 
   LOG(ERROR) << "Error parsing IPC request";
-  return false;
+  return RequestStatus::FAILURE;
 }
 
 }  // namespace syscall_broker
--- a/sandbox/linux/syscall_broker/broker_host.h
+++ b/sandbox/linux/syscall_broker/broker_host.h
@@ -6,6 +6,7 @@
 #define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_
 
 #include "base/macros.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
 
 namespace sandbox {
 
@@ -18,14 +19,18 @@ class BrokerPolicy;
 // |ipc_channel| according to |broker_policy|.
 class BrokerHost {
  public:
-  BrokerHost(const BrokerPolicy& broker_policy, int ipc_channel);
+  enum class RequestStatus { LOST_CLIENT = 0, SUCCESS, FAILURE };
+
+  BrokerHost(const BrokerPolicy& broker_policy,
+             BrokerChannel::EndPoint ipc_channel);
   ~BrokerHost();
 
-  bool HandleRequest() const;
+  RequestStatus HandleRequest() const;
 
  private:
   const BrokerPolicy& broker_policy_;
-  const int ipc_channel_;
+  const BrokerChannel::EndPoint ipc_channel_;
+
   DISALLOW_COPY_AND_ASSIGN(BrokerHost);
 };
 
--- a/sandbox/linux/syscall_broker/broker_policy.cc
+++ b/sandbox/linux/syscall_broker/broker_policy.cc
@@ -17,76 +17,19 @@
 namespace sandbox {
 namespace syscall_broker {
 
-namespace {
-
-// 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 & kCurrentProcessOpenFlagsMask)
-    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;
-}
-
-// Needs to be 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 char* 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;
-  }
-
-  // Look for |requested_filename| in |allowed_file_names|.
-  // We don't use ::find() because it takes a std::string and
-  // the conversion allocates memory.
-  for (const auto& allowed_file_name : allowed_file_names) {
-    if (strcmp(requested_filename, allowed_file_name.c_str()) == 0) {
-      if (file_to_open)
-        *file_to_open = allowed_file_name.c_str();
-      return true;
-    }
-  }
-  return false;
-}
-
-}  // namespace
-
 BrokerPolicy::BrokerPolicy(int denied_errno,
-                           const std::vector<std::string>& allowed_r_files,
-                           const std::vector<std::string>& allowed_w_files)
+                           const std::vector<BrokerFilePermission>& permissions)
     : denied_errno_(denied_errno),
-      allowed_r_files_(allowed_r_files),
-      allowed_w_files_(allowed_w_files) {
+      permissions_(permissions),
+      num_of_permissions_(permissions.size()) {
+  // The spec guarantees vectors store their elements contiguously
+  // so set up a pointer to array of element so it can be used
+  // in async signal safe code instead of vector operations.
+  if (num_of_permissions_ > 0) {
+    permissions_array_ = &permissions_[0];
+  } else {
+    permissions_array_ = NULL;
+  }
 }
 
 BrokerPolicy::~BrokerPolicy() {
@@ -107,34 +50,20 @@ bool BrokerPolicy::GetFileNameIfAllowedT
     const char* requested_filename,
     int requested_mode,
     const char** file_to_access) const {
-  // First, check if |requested_mode| is existence, ability to read or ability
-  // to write. We do not support X_OK.
-  if (requested_mode != F_OK && requested_mode & ~(R_OK | W_OK)) {
+  if (file_to_access && *file_to_access) {
+    // 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_access should be NULL");
     return false;
   }
-  switch (requested_mode) {
-    case F_OK:
-      // We allow to check for file existence if we can either read or write.
-      return GetFileNameInWhitelist(
-                 allowed_r_files_, requested_filename, file_to_access) ||
-             GetFileNameInWhitelist(
-                 allowed_w_files_, requested_filename, file_to_access);
-    case R_OK:
-      return GetFileNameInWhitelist(
-          allowed_r_files_, requested_filename, file_to_access);
-    case W_OK:
-      return GetFileNameInWhitelist(
-          allowed_w_files_, requested_filename, file_to_access);
-    case R_OK | W_OK: {
-      bool allowed_for_read_and_write =
-          GetFileNameInWhitelist(allowed_r_files_, requested_filename, NULL) &&
-          GetFileNameInWhitelist(
-              allowed_w_files_, requested_filename, file_to_access);
-      return allowed_for_read_and_write;
+  for (size_t i = 0; i < num_of_permissions_; i++) {
+    if (permissions_array_[i].CheckAccess(requested_filename, requested_mode,
+                                          file_to_access)) {
+      return true;
     }
-    default:
-      return false;
   }
+  return false;
 }
 
 // Check if |requested_filename| can be opened with flags |requested_flags|.
@@ -147,27 +76,22 @@ bool BrokerPolicy::GetFileNameIfAllowedT
 // Async signal safe if and only if |file_to_open| is NULL.
 bool BrokerPolicy::GetFileNameIfAllowedToOpen(const char* requested_filename,
                                               int requested_flags,
-                                              const char** file_to_open) const {
-  if (!IsAllowedOpenFlags(requested_flags)) {
+                                              const char** file_to_open,
+                                              bool* unlink_after_open) const {
+  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;
   }
-  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;
+  for (size_t i = 0; i < num_of_permissions_; i++) {
+    if (permissions_array_[i].CheckOpen(requested_filename, requested_flags,
+                                        file_to_open, unlink_after_open)) {
+      return true;
     }
-    default:
-      return false;
   }
+  return false;
 }
 
 }  // namespace syscall_broker
--- a/sandbox/linux/syscall_broker/broker_policy.h
+++ b/sandbox/linux/syscall_broker/broker_policy.h
@@ -10,6 +10,8 @@
 
 #include "base/macros.h"
 
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
 namespace sandbox {
 namespace syscall_broker {
 
@@ -23,21 +25,21 @@ class BrokerPolicy {
   // |denied_errno| is the error code returned when IPC requests for system
   // calls such as open() or access() are denied because a file is not in the
   // whitelist. EACCESS would be a typical value.
-  // |allowed_r_files| and |allowed_w_files| are white lists of files that
-  // should be allowed for opening, respectively for reading and writing.
-  // A file available read-write should be listed in both.
+  // |permissions| is a list of BrokerPermission objects that define
+  // what the broker will allow.
   BrokerPolicy(int denied_errno,
-               const std::vector<std::string>& allowed_r_files,
-               const std::vector<std::string>& allowed_w_files_);
+               const std::vector<BrokerFilePermission>& permissions);
+
   ~BrokerPolicy();
 
   // Check if calling access() should be allowed on |requested_filename| with
   // mode |requested_mode|.
   // Note: access() being a system call to check permissions, this can get a bit
   // confusing. We're checking if calling access() should even be allowed with
-  // the same policy we would use for open().
-  // If |file_to_access| is not NULL, we will return the matching pointer from
-  // the whitelist. For paranoia a caller should then use |file_to_access|. See
+  // If |file_to_open| is not NULL, a pointer to the path will be returned.
+  // In the case of a recursive match, this will be the requested_filename,
+  // otherwise it will return the matching pointer from the
+  // whitelist. For paranoia a caller should then use |file_to_access|. See
   // GetFileNameIfAllowedToOpen() for more explanation.
   // return true if calling access() on this file should be allowed, false
   // otherwise.
@@ -47,22 +49,34 @@ class BrokerPolicy {
                                     const char** file_to_access) const;
 
   // Check if |requested_filename| can be opened with flags |requested_flags|.
-  // If |file_to_open| is not NULL, we will return the matching pointer from the
+  // If |file_to_open| is not NULL, a pointer to the path will be returned.
+  // In the case of a recursive match, this will be the requested_filename,
+  // otherwise it will return the matching pointer from the
   // whitelist. For paranoia, a caller should then use |file_to_open| rather
   // than |requested_filename|, so that it never attempts to open an
   // attacker-controlled file name, even if an attacker managed to fool the
   // string comparison mechanism.
+  // |unlink_after_open| if not NULL will be set to point to true if the
+  // policy requests the caller unlink the path after opening.
   // Return true if opening should be allowed, false otherwise.
   // Async signal safe if and only if |file_to_open| is NULL.
   bool GetFileNameIfAllowedToOpen(const char* requested_filename,
                                   int requested_flags,
-                                  const char** file_to_open) const;
+                                  const char** file_to_open,
+                                  bool* unlink_after_open) const;
   int denied_errno() const { return denied_errno_; }
 
  private:
   const int denied_errno_;
-  const std::vector<std::string> allowed_r_files_;
-  const std::vector<std::string> allowed_w_files_;
+  // The permissions_ vector is used as storage for the BrokerFilePermission
+  // objects but is not referenced outside of the constructor as
+  // vectors are unfriendly in async signal safe code.
+  const std::vector<BrokerFilePermission> permissions_;
+  // permissions_array_ is set up to point to the backing store of
+  // permissions_ and is used in async signal safe methods.
+  const BrokerFilePermission* permissions_array_;
+  const size_t num_of_permissions_;
+
   DISALLOW_COPY_AND_ASSIGN(BrokerPolicy);
 };
 
--- a/sandbox/linux/syscall_broker/broker_process.cc
+++ b/sandbox/linux/syscall_broker/broker_process.cc
@@ -6,7 +6,6 @@
 
 #include <fcntl.h>
 #include <signal.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -23,30 +22,33 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/process/process_metrics.h"
 #include "build/build_config.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
 #include "sandbox/linux/syscall_broker/broker_client.h"
 #include "sandbox/linux/syscall_broker/broker_host.h"
 
 namespace sandbox {
 
-BrokerProcess::BrokerProcess(int denied_errno,
-                             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)
+namespace syscall_broker {
+
+BrokerProcess::BrokerProcess(
+    int denied_errno,
+    const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+    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),
-      policy_(denied_errno, allowed_r_files, allowed_w_files),
-      ipc_socketpair_(-1) {
+      policy_(denied_errno, permissions) {
 }
 
 BrokerProcess::~BrokerProcess() {
-  if (initialized_ && ipc_socketpair_ != -1) {
-    // Closing the socket should be enough to notify the child to die,
-    // unless it has been duplicated.
-    PCHECK(0 == IGNORE_EINTR(close(ipc_socketpair_)));
+  if (initialized_) {
+    if (broker_client_.get()) {
+      // Closing the socket should be enough to notify the child to die,
+      // unless it has been duplicated.
+      CloseChannel();
+    }
     PCHECK(0 == kill(broker_pid_, SIGKILL));
     siginfo_t process_info;
     // Reap the child.
@@ -58,57 +60,49 @@ BrokerProcess::~BrokerProcess() {
 bool BrokerProcess::Init(
     const base::Callback<bool(void)>& broker_process_init_callback) {
   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;
-  }
+  BrokerChannel::EndPoint ipc_reader;
+  BrokerChannel::EndPoint ipc_writer;
+  BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
 
 #if !defined(THREAD_SANITIZER)
   DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
 #endif
   int child_pid = fork();
   if (child_pid == -1) {
-    close(socket_pair[0]);
-    close(socket_pair[1]);
     return false;
   }
   if (child_pid) {
     // We are the parent and we have just forked our broker process.
-    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;
+    ipc_reader.reset();
     broker_pid_ = child_pid;
-    broker_client_.reset(
-        new syscall_broker::BrokerClient(policy_,
-                                         ipc_socketpair_,
-                                         fast_check_in_client_,
-                                         quiet_failures_for_tests_));
+    broker_client_.reset(new BrokerClient(policy_, ipc_writer.Pass(),
+                                          fast_check_in_client_,
+                                          quiet_failures_for_tests_));
     initialized_ = true;
     return true;
   } else {
-    // We are the broker.
-    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;
+    // We are the broker process. Make sure to close the writer's end so that
+    // we get notified if the client disappears.
+    ipc_writer.reset();
     CHECK(broker_process_init_callback.Run());
-    syscall_broker::BrokerHost broker_host(policy_, ipc_socketpair_);
-    initialized_ = true;
+    BrokerHost broker_host(policy_, ipc_reader.Pass());
     for (;;) {
-      broker_host.HandleRequest();
+      switch (broker_host.HandleRequest()) {
+        case BrokerHost::RequestStatus::LOST_CLIENT:
+          _exit(1);
+        case BrokerHost::RequestStatus::SUCCESS:
+        case BrokerHost::RequestStatus::FAILURE:
+          continue;
+      }
     }
     _exit(1);
   }
   NOTREACHED();
+  return false;
+}
+
+void BrokerProcess::CloseChannel() {
+  broker_client_.reset();
 }
 
 int BrokerProcess::Access(const char* pathname, int mode) const {
@@ -121,4 +115,6 @@ int BrokerProcess::Open(const char* path
   return broker_client_->Open(pathname, flags);
 }
 
+}  // namespace syscall_broker
+
 }  // namespace sandbox.
--- a/sandbox/linux/syscall_broker/broker_process.h
+++ b/sandbox/linux/syscall_broker/broker_process.h
@@ -19,8 +19,9 @@
 namespace sandbox {
 
 namespace syscall_broker {
+
 class BrokerClient;
-}
+class BrokerFilePermission;
 
 // Create a new "broker" process to which we can send requests via an IPC
 // channel by forking the current process.
@@ -42,11 +43,13 @@ class SANDBOX_EXPORT BrokerProcess {
   // A file available read-write should be listed in both.
   // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for
   // unit tests, don't use it.
-  BrokerProcess(int denied_errno,
-                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(
+      int denied_errno,
+      const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+      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().
@@ -68,21 +71,24 @@ class SANDBOX_EXPORT BrokerProcess {
   int broker_pid() const { return broker_pid_; }
 
  private:
+  friend class BrokerProcessTestHelper;
+
+  // Close the IPC channel with the other party. This should only be used
+  // by tests an none of the class methods should be used afterwards.
+  void CloseChannel();
+
   bool initialized_;  // Whether we've been through Init() yet.
-  bool is_child_;     // Whether we're the child (broker process).
-  bool fast_check_in_client_;
-  bool quiet_failures_for_tests_;
+  const bool fast_check_in_client_;
+  const bool quiet_failures_for_tests_;
   pid_t broker_pid_;                     // The PID of the broker (child).
   syscall_broker::BrokerPolicy policy_;  // The sandboxing policy.
-  scoped_ptr<syscall_broker::BrokerClient>
-      broker_client_;  // Can only exist if is_child_ is true.
+  scoped_ptr<syscall_broker::BrokerClient> broker_client_;
 
-  int ipc_socketpair_;  // Our communication channel to parent or child.
   DISALLOW_COPY_AND_ASSIGN(BrokerProcess);
-
-  friend class BrokerProcessTestHelper;
 };
 
+}  // namespace syscall_broker
+
 }  // namespace sandbox
 
 #endif  // SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
--- a/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <poll.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -24,6 +25,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/syscall_broker/broker_client.h"
 #include "sandbox/linux/tests/scoped_temporary_file.h"
 #include "sandbox/linux/tests/test_utils.h"
 #include "sandbox/linux/tests/unit_tests.h"
@@ -31,10 +33,15 @@
 
 namespace sandbox {
 
+namespace syscall_broker {
+
 class BrokerProcessTestHelper {
  public:
-  static int get_ipc_socketpair(const BrokerProcess* broker) {
-    return broker->ipc_socketpair_;
+  static void CloseChannel(BrokerProcess* broker) { broker->CloseChannel(); }
+  // Get the client's IPC descriptor to send IPC requests directly.
+  // TODO(jln): refator tests to get rid of this.
+  static int GetIPCDescriptor(const BrokerProcess* broker) {
+    return broker->broker_client_->GetIPCDescriptor();
   }
 };
 
@@ -47,11 +54,10 @@ bool NoOpCallback() {
 }  // namespace
 
 TEST(BrokerProcess, CreateAndDestroy) {
-  std::vector<std::string> read_whitelist;
-  read_whitelist.push_back("/proc/cpuinfo");
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
 
-  scoped_ptr<BrokerProcess> open_broker(
-      new BrokerProcess(EPERM, read_whitelist, std::vector<std::string>()));
+  scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(EPERM, permissions));
   ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
 
   ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
@@ -61,8 +67,8 @@ TEST(BrokerProcess, CreateAndDestroy) {
 }
 
 TEST(BrokerProcess, TestOpenAccessNull) {
-  const std::vector<std::string> empty;
-  BrokerProcess open_broker(EPERM, empty, empty);
+  std::vector<BrokerFilePermission> empty;
+  BrokerProcess open_broker(EPERM, empty);
   ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
 
   int fd = open_broker.Open(NULL, O_RDONLY);
@@ -81,17 +87,14 @@ void TestOpenFilePerms(bool fast_check_i
   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(kR_WhiteListedButDenied);
-  read_whitelist.push_back(kRW_WhiteListed);
-
-  std::vector<std::string> write_whitelist;
-  write_whitelist.push_back(kW_WhiteListed);
-  write_whitelist.push_back(kRW_WhiteListed);
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadOnly(kR_WhiteListed));
+  permissions.push_back(
+      BrokerFilePermission::ReadOnly(kR_WhiteListedButDenied));
+  permissions.push_back(BrokerFilePermission::WriteOnly(kW_WhiteListed));
+  permissions.push_back(BrokerFilePermission::ReadWrite(kRW_WhiteListed));
 
-  BrokerProcess open_broker(
-      denied_errno, read_whitelist, write_whitelist, fast_check_in_client);
+  BrokerProcess open_broker(denied_errno, permissions, fast_check_in_client);
   ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
 
   int fd = -1;
@@ -236,13 +239,78 @@ TEST(BrokerProcess, OpenOpenFilePermsNoC
   // expected.
 }
 
-void TestOpenCpuinfo(bool fast_check_in_client) {
+void TestBadPaths(bool fast_check_in_client) {
   const char kFileCpuInfo[] = "/proc/cpuinfo";
-  std::vector<std::string> read_whitelist;
-  read_whitelist.push_back(kFileCpuInfo);
+  const char kNotAbsPath[] = "proc/cpuinfo";
+  const char kDotDotStart[] = "/../proc/cpuinfo";
+  const char kDotDotMiddle[] = "/proc/self/../cpuinfo";
+  const char kDotDotEnd[] = "/proc/..";
+  const char kTrailingSlash[] = "/proc/";
+
+  std::vector<BrokerFilePermission> permissions;
 
-  scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(
-      EPERM, read_whitelist, std::vector<std::string>(), fast_check_in_client));
+  permissions.push_back(BrokerFilePermission::ReadOnlyRecursive("/proc/"));
+  scoped_ptr<BrokerProcess> open_broker(
+      new BrokerProcess(EPERM, permissions, fast_check_in_client));
+  ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+  // Open cpuinfo via the broker.
+  int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+  base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+  ASSERT_GE(cpuinfo_fd, 0);
+
+  int fd = -1;
+  int can_access;
+
+  can_access = open_broker->Access(kNotAbsPath, R_OK);
+  ASSERT_EQ(can_access, -EPERM);
+  fd = open_broker->Open(kNotAbsPath, O_RDONLY);
+  ASSERT_EQ(fd, -EPERM);
+
+  can_access = open_broker->Access(kDotDotStart, R_OK);
+  ASSERT_EQ(can_access, -EPERM);
+  fd = open_broker->Open(kDotDotStart, O_RDONLY);
+  ASSERT_EQ(fd, -EPERM);
+
+  can_access = open_broker->Access(kDotDotMiddle, R_OK);
+  ASSERT_EQ(can_access, -EPERM);
+  fd = open_broker->Open(kDotDotMiddle, O_RDONLY);
+  ASSERT_EQ(fd, -EPERM);
+
+  can_access = open_broker->Access(kDotDotEnd, R_OK);
+  ASSERT_EQ(can_access, -EPERM);
+  fd = open_broker->Open(kDotDotEnd, O_RDONLY);
+  ASSERT_EQ(fd, -EPERM);
+
+  can_access = open_broker->Access(kTrailingSlash, R_OK);
+  ASSERT_EQ(can_access, -EPERM);
+  fd = open_broker->Open(kTrailingSlash, O_RDONLY);
+  ASSERT_EQ(fd, -EPERM);
+}
+
+TEST(BrokerProcess, BadPathsClientCheck) {
+  TestBadPaths(true /* fast_check_in_client */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerProcess, BadPathsNoClientCheck) {
+  TestBadPaths(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, bool recursive) {
+  const char kFileCpuInfo[] = "/proc/cpuinfo";
+  const char kDirProc[] = "/proc/";
+
+  std::vector<BrokerFilePermission> permissions;
+  if (recursive)
+    permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(kDirProc));
+  else
+    permissions.push_back(BrokerFilePermission::ReadOnly(kFileCpuInfo));
+
+  scoped_ptr<BrokerProcess> open_broker(
+      new BrokerProcess(EPERM, permissions, fast_check_in_client));
   ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
 
   int fd = -1;
@@ -286,16 +354,28 @@ void TestOpenCpuinfo(bool fast_check_in_
   ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
 }
 
-// Run the same thing twice. The second time, we make sure that no security
-// check is performed on the client.
+// Run this test 4 times. With and without the check in client
+// and using a recursive path.
 TEST(BrokerProcess, OpenCpuinfoWithClientCheck) {
-  TestOpenCpuinfo(true /* fast_check_in_client */);
+  TestOpenCpuinfo(true /* fast_check_in_client */, false /* not recursive */);
   // Don't do anything here, so that ASSERT works in the subfunction as
   // expected.
 }
 
 TEST(BrokerProcess, OpenCpuinfoNoClientCheck) {
-  TestOpenCpuinfo(false /* fast_check_in_client */);
+  TestOpenCpuinfo(false /* fast_check_in_client */, false /* not recursive */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoWithClientCheckRecursive) {
+  TestOpenCpuinfo(true /* fast_check_in_client */, true /* recursive */);
+  // Don't do anything here, so that ASSERT works in the subfunction as
+  // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheckRecursive) {
+  TestOpenCpuinfo(false /* fast_check_in_client */, true /* recursive */);
   // Don't do anything here, so that ASSERT works in the subfunction as
   // expected.
 }
@@ -304,10 +384,10 @@ TEST(BrokerProcess, OpenFileRW) {
   ScopedTemporaryFile tempfile;
   const char* tempfile_name = tempfile.full_file_name();
 
-  std::vector<std::string> whitelist;
-  whitelist.push_back(tempfile_name);
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadWrite(tempfile_name));
 
-  BrokerProcess open_broker(EPERM, whitelist, whitelist);
+  BrokerProcess open_broker(EPERM, permissions);
   ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
 
   // Check we can access that file with read or write.
@@ -337,13 +417,11 @@ TEST(BrokerProcess, OpenFileRW) {
 // SANDBOX_TEST because the process could die with a SIGPIPE
 // and we want this to happen in a subprocess.
 SANDBOX_TEST(BrokerProcess, BrokerDied) {
-  std::vector<std::string> read_whitelist;
-  read_whitelist.push_back("/proc/cpuinfo");
+  const char kCpuInfo[] = "/proc/cpuinfo";
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
 
-  BrokerProcess open_broker(EPERM,
-                            read_whitelist,
-                            std::vector<std::string>(),
-                            true /* fast_check_in_client */,
+  BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
                             true /* quiet_failures_for_tests */);
   SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
   const pid_t broker_pid = open_broker.broker_pid();
@@ -359,16 +437,16 @@ SANDBOX_TEST(BrokerProcess, BrokerDied)
   SANDBOX_ASSERT(SIGKILL == process_info.si_status);
 
   // Check that doing Open with a dead broker won't SIGPIPE us.
-  SANDBOX_ASSERT(open_broker.Open("/proc/cpuinfo", O_RDONLY) == -ENOMEM);
-  SANDBOX_ASSERT(open_broker.Access("/proc/cpuinfo", O_RDONLY) == -ENOMEM);
+  SANDBOX_ASSERT(open_broker.Open(kCpuInfo, O_RDONLY) == -ENOMEM);
+  SANDBOX_ASSERT(open_broker.Access(kCpuInfo, O_RDONLY) == -ENOMEM);
 }
 
 void TestOpenComplexFlags(bool fast_check_in_client) {
   const char kCpuInfo[] = "/proc/cpuinfo";
-  std::vector<std::string> whitelist;
-  whitelist.push_back(kCpuInfo);
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
 
-  BrokerProcess open_broker(EPERM, whitelist, whitelist, fast_check_in_client);
+  BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
   ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
   // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
   int fd = -1;
@@ -447,13 +525,13 @@ SANDBOX_TEST_ALLOW_NOISE(BrokerProcess,
   SANDBOX_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
 
   static const char kCpuInfo[] = "/proc/cpuinfo";
-  std::vector<std::string> read_whitelist;
-  read_whitelist.push_back(kCpuInfo);
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
 
-  BrokerProcess open_broker(EPERM, read_whitelist, std::vector<std::string>());
+  BrokerProcess open_broker(EPERM, permissions);
   SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
 
-  const int ipc_fd = BrokerProcessTestHelper::get_ipc_socketpair(&open_broker);
+  const int ipc_fd = BrokerProcessTestHelper::GetIPCDescriptor(&open_broker);
   SANDBOX_ASSERT(ipc_fd >= 0);
 
   static const char kBogus[] = "not a pickle";
@@ -472,4 +550,108 @@ SANDBOX_TEST_ALLOW_NOISE(BrokerProcess,
   SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd)));
 }
 
+bool CloseFD(int fd) {
+  PCHECK(0 == IGNORE_EINTR(close(fd)));
+  return true;
+}
+
+// Return true if the other end of the |reader| pipe was closed,
+// false if |timeout_in_seconds| was reached or another event
+// or error occured.
+bool WaitForClosedPipeWriter(int reader, int timeout_in_ms) {
+  struct pollfd poll_fd = {reader, POLLIN | POLLRDHUP, 0};
+  const int num_events = HANDLE_EINTR(poll(&poll_fd, 1, timeout_in_ms));
+  if (1 == num_events && poll_fd.revents | POLLHUP)
+    return true;
+  return false;
+}
+
+// Closing the broker client's IPC channel should terminate the broker
+// process.
+TEST(BrokerProcess, BrokerDiesOnClosedChannel) {
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
+
+  // Get the writing end of a pipe into the broker (child) process so
+  // that we can reliably detect when it dies.
+  int lifeline_fds[2];
+  PCHECK(0 == pipe(lifeline_fds));
+
+  BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+                            false /* quiet_failures_for_tests */);
+  ASSERT_TRUE(open_broker.Init(base::Bind(&CloseFD, lifeline_fds[0])));
+  // Make sure the writing end only exists in the broker process.
+  CloseFD(lifeline_fds[1]);
+  base::ScopedFD reader(lifeline_fds[0]);
+
+  const pid_t broker_pid = open_broker.broker_pid();
+
+  // This should cause the broker process to exit.
+  BrokerProcessTestHelper::CloseChannel(&open_broker);
+
+  const int kTimeoutInMilliseconds = 5000;
+  const bool broker_lifeline_closed =
+      WaitForClosedPipeWriter(reader.get(), kTimeoutInMilliseconds);
+  // If the broker exited, its lifeline fd should be closed.
+  ASSERT_TRUE(broker_lifeline_closed);
+  // Now check that the broker has exited, but do not reap it.
+  siginfo_t process_info;
+  ASSERT_EQ(0, HANDLE_EINTR(waitid(P_PID, broker_pid, &process_info,
+                                   WEXITED | WNOWAIT)));
+  EXPECT_EQ(broker_pid, process_info.si_pid);
+  EXPECT_EQ(CLD_EXITED, process_info.si_code);
+  EXPECT_EQ(1, process_info.si_status);
+}
+
+TEST(BrokerProcess, CreateFile) {
+  std::string temp_str;
+  {
+    ScopedTemporaryFile tmp_file;
+    temp_str = tmp_file.full_file_name();
+  }
+  const char* tempfile_name = temp_str.c_str();
+
+  std::vector<BrokerFilePermission> permissions;
+  permissions.push_back(BrokerFilePermission::ReadWriteCreate(tempfile_name));
+
+  BrokerProcess open_broker(EPERM, permissions);
+  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+  int fd = -1;
+
+  // Try without O_EXCL
+  fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT);
+  ASSERT_EQ(fd, -EPERM);
+
+  const char kTestText[] = "TESTTESTTEST";
+  // Create a file
+  fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+  ASSERT_GE(fd, 0);
+  {
+    base::ScopedFD scoped_fd(fd);
+
+    // Confirm fail if file exists
+    int bad_fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+    ASSERT_EQ(bad_fd, -EEXIST);
+
+    // Write to the descriptor opened by the broker.
+
+    ssize_t len = HANDLE_EINTR(write(fd, kTestText, sizeof(kTestText)));
+    ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+  }
+
+  int fd_check = open(tempfile_name, O_RDONLY);
+  ASSERT_GE(fd_check, 0);
+  {
+    base::ScopedFD scoped_fd(fd_check);
+    char buf[1024];
+    ssize_t len = HANDLE_EINTR(read(fd_check, buf, sizeof(buf)));
+
+    ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+    ASSERT_EQ(memcmp(kTestText, buf, sizeof(kTestText)), 0);
+  }
+}
+
+}  // namespace syscall_broker
+
 }  // namespace sandbox
--- a/sandbox/linux/tests/scoped_temporary_file.cc
+++ b/sandbox/linux/tests/scoped_temporary_file.cc
@@ -20,8 +20,8 @@ ScopedTemporaryFile::ScopedTemporaryFile
 #else
   static const char file_template[] = "/tmp/ScopedTempFileXXXXXX";
 #endif  // defined(OS_ANDROID)
-  COMPILE_ASSERT(sizeof(full_file_name_) >= sizeof(file_template),
-                 full_file_name_is_large_enough);
+  static_assert(sizeof(full_file_name_) >= sizeof(file_template),
+                "full_file_name is not large enough");
   memcpy(full_file_name_, file_template, sizeof(file_template));
   fd_ = mkstemp(full_file_name_);
   CHECK_LE(0, fd_);
--- a/sandbox/linux/tests/test_utils.cc
+++ b/sandbox/linux/tests/test_utils.cc
@@ -28,4 +28,16 @@ bool TestUtils::CurrentProcessHasChildre
   }
 }
 
+void TestUtils::HandlePostForkReturn(pid_t pid) {
+  const int kChildExitCode = 1;
+  if (pid > 0) {
+    int status = 0;
+    PCHECK(pid == HANDLE_EINTR(waitpid(pid, &status, 0)));
+    CHECK(WIFEXITED(status));
+    CHECK_EQ(kChildExitCode, WEXITSTATUS(status));
+  } else if (pid == 0) {
+    _exit(kChildExitCode);
+  }
+}
+
 }  // namespace sandbox
--- a/sandbox/linux/tests/test_utils.h
+++ b/sandbox/linux/tests/test_utils.h
@@ -5,6 +5,8 @@
 #ifndef SANDBOX_LINUX_TESTS_TEST_UTILS_H_
 #define SANDBOX_LINUX_TESTS_TEST_UTILS_H_
 
+#include <sys/types.h>
+
 #include "base/basictypes.h"
 
 namespace sandbox {
@@ -13,6 +15,10 @@ namespace sandbox {
 class TestUtils {
  public:
   static bool CurrentProcessHasChildren();
+  // |pid| is the return value of a fork()-like call. This
+  // makes sure that if fork() succeeded the child exits
+  // and the parent waits for it.
+  static void HandlePostForkReturn(pid_t pid);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(TestUtils);
--- /dev/null
+++ b/sandbox/linux/tests/test_utils_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/test_utils.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+// Check that HandlePostForkReturn works.
+TEST(TestUtils, HandlePostForkReturn) {
+  pid_t pid = fork();
+  TestUtils::HandlePostForkReturn(pid);
+}
+
+}  // namespace
+
+}  // namespace sandbox
--- a/sandbox/linux/tests/unit_tests.cc
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -263,6 +263,16 @@ void UnitTests::DeathMessage(int status,
 
   bool subprocess_exited_without_matching_message =
       msg.find(expected_msg) == std::string::npos;
+
+// In official builds CHECK messages are dropped, so look for SIGABRT.
+// See https://code.google.com/p/chromium/issues/detail?id=437312
+#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID)
+  if (subprocess_exited_without_matching_message) {
+    static const char kSigAbortMessage[] = "Received signal 6";
+    subprocess_exited_without_matching_message =
+        msg.find(kSigAbortMessage) == std::string::npos;
+  }
+#endif
   EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
 }
 
--- a/sandbox/linux/tests/unit_tests.h
+++ b/sandbox/linux/tests/unit_tests.h
@@ -99,6 +99,13 @@ bool IsRunningOnValgrind();
   ((expr) ? static_cast<void>(0) : sandbox::UnitTests::AssertionFailure( \
                                        SANDBOX_STR(expr), __FILE__, __LINE__))
 
+#define SANDBOX_ASSERT_EQ(x, y) SANDBOX_ASSERT((x) == (y))
+#define SANDBOX_ASSERT_NE(x, y) SANDBOX_ASSERT((x) != (y))
+#define SANDBOX_ASSERT_LT(x, y) SANDBOX_ASSERT((x) < (y))
+#define SANDBOX_ASSERT_GT(x, y) SANDBOX_ASSERT((x) > (y))
+#define SANDBOX_ASSERT_LE(x, y) SANDBOX_ASSERT((x) <= (y))
+#define SANDBOX_ASSERT_GE(x, y) SANDBOX_ASSERT((x) >= (y))
+
 // This class allows to run unittests in their own process. The main method is
 // RunTestInProcess().
 class UnitTests {
--- a/third_party/webrtc/common_audio/resampler/sinc_resampler.cc
+++ b/third_party/webrtc/common_audio/resampler/sinc_resampler.cc
@@ -139,8 +139,7 @@ void SincResampler::InitializeCPUSpecifi
 #define CONVOLVE_FUNC convolve_proc_
 
 void SincResampler::InitializeCPUSpecificFeatures() {
-  convolve_proc_ = WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON ?
-      Convolve_NEON : Convolve_C;
+  convolve_proc_ = Convolve_C;
 }
 #endif
 #else
