Merge from Chromium at DEPS revision 257591

This commit was generated by merge_to_master.py.

Change-Id: I0010df2ec3fbb5d4947cd026de2feb150ce7a6b5
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
index 21c0401..59c61ff 100644
--- a/sandbox/linux/sandbox_linux.gypi
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -181,8 +181,12 @@
         'services/broker_process.h',
         'services/init_process_reaper.cc',
         'services/init_process_reaper.h',
+        'services/scoped_process.cc',
+        'services/scoped_process.h',
         'services/thread_helpers.cc',
         'services/thread_helpers.h',
+        'services/yama.h',
+        'services/yama.cc',
       ],
       'dependencies': [
         '../base/base.gyp:base',
diff --git a/sandbox/linux/sandbox_linux_test_sources.gypi b/sandbox/linux/sandbox_linux_test_sources.gypi
index 37e48f8..a48017b 100644
--- a/sandbox/linux/sandbox_linux_test_sources.gypi
+++ b/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -15,10 +15,14 @@
   ],
   'sources': [
     'tests/main.cc',
+    'tests/test_utils.cc',
+    'tests/test_utils.h',
     'tests/unit_tests.cc',
     'tests/unit_tests.h',
     'services/broker_process_unittest.cc',
+    'services/scoped_process_unittest.cc',
     'services/thread_helpers_unittests.cc',
+    'services/yama_unittests.cc',
   ],
   'conditions': [
     [ 'compile_suid_client==1', {
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
index 988e295..d05878c 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -20,6 +20,7 @@
 
 #include <ostream>
 
+#include "base/bind.h"
 #include "base/memory/scoped_ptr.h"
 #include "build/build_config.h"
 #include "sandbox/linux/seccomp-bpf/bpf_tests.h"
@@ -670,6 +671,8 @@
   BPF_ASSERT(errno == 0);
 }
 
+bool NoOpCallback() { return true; }
+
 // Test a trap handler that makes use of a broker process to open().
 
 class InitializedOpenBroker {
@@ -682,7 +685,7 @@
     broker_process_.reset(
         new BrokerProcess(EPERM, allowed_files, std::vector<std::string>()));
     BPF_ASSERT(broker_process() != NULL);
-    BPF_ASSERT(broker_process_->Init(NULL));
+    BPF_ASSERT(broker_process_->Init(base::Bind(&NoOpCallback)));
 
     initialized_ = true;
   }
diff --git a/sandbox/linux/services/broker_process.cc b/sandbox/linux/services/broker_process.cc
index 438e972..2956cf9 100644
--- a/sandbox/linux/services/broker_process.cc
+++ b/sandbox/linux/services/broker_process.cc
@@ -5,10 +5,12 @@
 #include "sandbox/linux/services/broker_process.h"
 
 #include <fcntl.h>
+#include <signal.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include <algorithm>
@@ -16,6 +18,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/pickle.h"
@@ -132,11 +135,19 @@
 
 BrokerProcess::~BrokerProcess() {
   if (initialized_ && ipc_socketpair_ != -1) {
-    close(ipc_socketpair_);
+    // Closing the socket should be enough to notify the child to die,
+    // unless it has been duplicated.
+    PCHECK(0 == IGNORE_EINTR(close(ipc_socketpair_)));
+    PCHECK(0 == kill(broker_pid_, SIGKILL));
+    siginfo_t process_info;
+    // Reap the child.
+    int ret = HANDLE_EINTR(waitid(P_PID, broker_pid_, &process_info, WEXITED));
+    PCHECK(0 == ret);
   }
 }
 
-bool BrokerProcess::Init(bool (*sandbox_callback)(void)) {
+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
@@ -147,7 +158,9 @@
     return false;
   }
 
+#if !defined(THREAD_SANITIZER)
   DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+#endif
   int child_pid = fork();
   if (child_pid == -1) {
     close(socket_pair[0]);
@@ -173,10 +186,7 @@
     shutdown(socket_pair[0], SHUT_WR);
     ipc_socketpair_ = socket_pair[0];
     is_child_ = true;
-    // Enable the sandbox if provided.
-    if (sandbox_callback) {
-      CHECK(sandbox_callback());
-    }
+    CHECK(broker_process_init_callback.Run());
     initialized_ = true;
     for (;;) {
       HandleRequest();
diff --git a/sandbox/linux/services/broker_process.h b/sandbox/linux/services/broker_process.h
index 6b13b33..84de396 100644
--- a/sandbox/linux/services/broker_process.h
+++ b/sandbox/linux/services/broker_process.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/callback_forward.h"
 #include "base/pickle.h"
 #include "base/process/process.h"
 
@@ -42,9 +43,9 @@
   ~BrokerProcess();
   // Will initialize the broker process. There should be no threads at this
   // point, since we need to fork().
-  // sandbox_callback is a function that should be called to enable the
-  // sandbox in the broker.
-  bool Init(bool (*sandbox_callback)(void));
+  // broker_process_init_callback will be called in the new broker process,
+  // after fork() returns.
+  bool Init(const base::Callback<bool(void)>& broker_process_init_callback);
 
   // Can be used in place of access(). Will be async signal safe.
   // X_OK will always return an error in practice since the broker process
diff --git a/sandbox/linux/services/broker_process_unittest.cc b/sandbox/linux/services/broker_process_unittest.cc
index f163ef9..7f1a685 100644
--- a/sandbox/linux/services/broker_process_unittest.cc
+++ b/sandbox/linux/services/broker_process_unittest.cc
@@ -15,15 +15,16 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/bind.h"
 #include "base/file_util.h"
+#include "base/files/scoped_file.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/tests/test_utils.h"
 #include "sandbox/linux/tests/unit_tests.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using file_util::ScopedFD;
-
 namespace sandbox {
 
 namespace {
@@ -60,13 +61,9 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
 };
 
-}  // namespace
+bool NoOpCallback() { return true; }
 
-#if defined(OS_ANDROID)
-  #define DISABLE_ON_ANDROID(function) DISABLED_##function
-#else
-  #define DISABLE_ON_ANDROID(function) function
-#endif
+}  // namespace
 
 TEST(BrokerProcess, CreateAndDestroy) {
   std::vector<std::string> read_whitelist;
@@ -74,21 +71,18 @@
 
   scoped_ptr<BrokerProcess> open_broker(
       new BrokerProcess(EPERM, read_whitelist, std::vector<std::string>()));
-  ASSERT_TRUE(open_broker->Init(NULL));
-  pid_t broker_pid = open_broker->broker_pid();
+  ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
 
+  ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
   // Destroy the broker and check it has exited properly.
   open_broker.reset();
-  int status = 0;
-  ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid);
-  ASSERT_TRUE(WIFEXITED(status));
-  ASSERT_EQ(WEXITSTATUS(status), 0);
+  ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
 }
 
 TEST(BrokerProcess, TestOpenAccessNull) {
   const std::vector<std::string> empty;
   BrokerProcess open_broker(EPERM, empty, empty);
-  ASSERT_TRUE(open_broker.Init(NULL));
+  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
 
   int fd = open_broker.Open(NULL, O_RDONLY);
   ASSERT_EQ(fd, -EFAULT);
@@ -119,7 +113,7 @@
                             read_whitelist,
                             write_whitelist,
                             fast_check_in_client);
-  ASSERT_TRUE(open_broker.Init(NULL));
+  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
 
   int fd = -1;
   fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
@@ -270,12 +264,11 @@
 
   scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(
       EPERM, read_whitelist, std::vector<std::string>(), fast_check_in_client));
-  ASSERT_TRUE(open_broker->Init(NULL));
-  pid_t broker_pid = open_broker->broker_pid();
+  ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
 
   int fd = -1;
   fd = open_broker->Open(kFileCpuInfo, O_RDWR);
-  ScopedFD fd_closer(&fd);
+  base::ScopedFD fd_closer(fd);
   ASSERT_EQ(fd, -EPERM);
 
   // Check we can read /proc/cpuinfo.
@@ -287,7 +280,7 @@
 
   // Open cpuinfo via the broker.
   int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
-  ScopedFD cpuinfo_fd_closer(&cpuinfo_fd);
+  base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
   ASSERT_GE(cpuinfo_fd, 0);
   char buf[3];
   memset(buf, 0, sizeof(buf));
@@ -296,7 +289,7 @@
 
   // Open cpuinfo directly.
   int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
-  ScopedFD cpuinfo_fd2_closer(&cpuinfo_fd2);
+  base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
   ASSERT_GE(cpuinfo_fd2, 0);
   char buf2[3];
   memset(buf2, 1, sizeof(buf2));
@@ -309,13 +302,9 @@
   // ourselves.
   ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
 
+  ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
   open_broker.reset();
-
-  // Now we check that the broker has exited properly.
-  int status = 0;
-  ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid);
-  ASSERT_TRUE(WIFEXITED(status));
-  ASSERT_EQ(WEXITSTATUS(status), 0);
+  ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
 }
 
 // Run the same thing twice. The second time, we make sure that no security
@@ -340,7 +329,7 @@
   whitelist.push_back(tempfile_name);
 
   BrokerProcess open_broker(EPERM, whitelist, whitelist);
-  ASSERT_TRUE(open_broker.Init(NULL));
+  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
 
   // Check we can access that file with read or write.
   int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
@@ -377,15 +366,19 @@
                             std::vector<std::string>(),
                             true /* fast_check_in_client */,
                             true /* quiet_failures_for_tests */);
-  SANDBOX_ASSERT(open_broker.Init(NULL));
-  pid_t broker_pid = open_broker.broker_pid();
+  SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+  const pid_t broker_pid = open_broker.broker_pid();
   SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
 
-  // Now we check that the broker has exited properly.
-  int status = 0;
-  SANDBOX_ASSERT(waitpid(broker_pid, &status, 0) == broker_pid);
-  SANDBOX_ASSERT(WIFSIGNALED(status));
-  SANDBOX_ASSERT(WTERMSIG(status) == SIGKILL);
+  // Now we check that the broker has been signaled, but do not reap it.
+  siginfo_t process_info;
+  SANDBOX_ASSERT(HANDLE_EINTR(waitid(
+                     P_PID, broker_pid, &process_info, WEXITED | WNOWAIT)) ==
+                 0);
+  SANDBOX_ASSERT(broker_pid == process_info.si_pid);
+  SANDBOX_ASSERT(CLD_KILLED == process_info.si_code);
+  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);
@@ -400,7 +393,7 @@
                             whitelist,
                             whitelist,
                             fast_check_in_client);
-  ASSERT_TRUE(open_broker.Init(NULL));
+  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
   // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
   int fd = -1;
   int ret = 0;
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index 4f041dc..8077c68 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -7,21 +7,28 @@
 #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>
 #include <unistd.h>
 
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/template_util.h"
+#include "base/third_party/valgrind/valgrind.h"
 #include "base/threading/thread.h"
 
 namespace {
 
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
 struct CapFreeDeleter {
   inline void operator()(cap_t cap) const {
     int ret = cap_free(cap);
@@ -49,7 +56,7 @@
   }
 };
 
-// Don't use ScopedFILE in base/file_util.h since it doesn't check fclose().
+// Don't use ScopedFILE in base since it doesn't check fclose().
 // TODO(jln): fix base/.
 typedef scoped_ptr<FILE, FILECloser> ScopedFILE;
 
@@ -146,6 +153,16 @@
   return is_chrooted;
 }
 
+// CHECK() that an attempt to move to a new user namespace raised an expected
+// errno.
+void CheckCloneNewUserErrno(int error) {
+  // EPERM can happen if already in a chroot. EUSERS if too many nested
+  // namespaces are used. EINVAL for kernels that don't support the feature.
+  // Valgrind will ENOSYS unshare().
+  PCHECK(error == EPERM || error == EUSERS || error == EINVAL ||
+         error == ENOSYS);
+}
+
 }  // namespace.
 
 namespace sandbox {
@@ -231,6 +248,37 @@
   return scoped_ptr<std::string> (new std::string(cap_text.get()));
 }
 
+// static
+bool Credentials::SupportsNewUserNS() {
+  // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
+  // so always consider UserNS unsupported there.
+  if (IsRunningOnValgrind()) {
+    return false;
+  }
+
+  // This is roughly a fork().
+  const pid_t pid = syscall(__NR_clone, CLONE_NEWUSER | SIGCHLD, 0, 0, 0);
+
+  if (pid == -1) {
+    CheckCloneNewUserErrno(errno);
+    return false;
+  }
+
+  // The parent process could have had threads. In the child, these threads
+  // have disappeared. Make sure to not do anything in the child, as this is a
+  // fragile execution environment.
+  if (pid == 0) {
+    _exit(0);
+  }
+
+  // Always reap the child.
+  siginfo_t infop;
+  PCHECK(0 == HANDLE_EINTR(waitid(P_PID, pid, &infop, WEXITED)));
+
+  // clone(2) succeeded, we can use CLONE_NEWUSER.
+  return true;
+}
+
 bool Credentials::MoveToNewUserNS() {
   uid_t uid;
   gid_t gid;
@@ -241,16 +289,14 @@
     return false;
   }
   int ret = unshare(CLONE_NEWUSER);
-  // EPERM can happen if already in a chroot. EUSERS if too many nested
-  // namespaces are used. EINVAL for kernels that don't support the feature.
-  // Valgrind will ENOSYS unshare().
-  PCHECK(!ret || errno == EPERM || errno == EUSERS || errno == EINVAL ||
-         errno == ENOSYS);
   if (ret) {
+    const int unshare_errno = errno;
     VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
             << "on this kernel.";
+    CheckCloneNewUserErrno(unshare_errno);
     return false;
   }
+
   // The current {r,e,s}{u,g}id is now an overflow id (c.f.
   // /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
   DCHECK(GetRESIds(NULL, NULL));
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
index c23db93..48cf259 100644
--- a/sandbox/linux/services/credentials.h
+++ b/sandbox/linux/services/credentials.h
@@ -51,6 +51,13 @@
   // debugging and tests.
   scoped_ptr<std::string> GetCurrentCapString() const;
 
+  // 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
+  // in using this method right before calling MoveToNewUserNS(), simply call
+  // MoveToNewUserNS() immediately. This method is only useful to test kernel
+  // support ahead of time.
+  static bool SupportsNewUserNS();
+
   // Move the current process to a new "user namespace" as supported by Linux
   // 3.8+ (CLONE_NEWUSER).
   // The uid map will be set-up so that the perceived uid and gid will not
diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc
index 9160bf7..a54ed04 100644
--- a/sandbox/linux/services/credentials_unittest.cc
+++ b/sandbox/linux/services/credentials_unittest.cc
@@ -12,13 +12,12 @@
 #include <unistd.h>
 
 #include "base/file_util.h"
+#include "base/files/scoped_file.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "sandbox/linux/tests/unit_tests.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using file_util::ScopedFD;
-
 namespace sandbox {
 
 namespace {
@@ -65,7 +64,7 @@
   {
     // Have a "/dev" file descriptor around.
     int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    ScopedFD dev_fd_closer(&dev_fd);
+    base::ScopedFD dev_fd_closer(dev_fd);
     EXPECT_TRUE(creds.HasOpenDirectory(-1));
   }
   EXPECT_FALSE(creds.HasOpenDirectory(-1));
@@ -75,7 +74,7 @@
   Credentials creds;
 
   int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
-  ScopedFD proc_fd_closer(&proc_fd);
+  base::ScopedFD proc_fd_closer(proc_fd);
   ASSERT_LE(0, proc_fd);
 
   // Don't pass |proc_fd|, an open directory (proc_fd) should
@@ -87,7 +86,7 @@
   {
     // Have a "/dev" file descriptor around.
     int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    ScopedFD dev_fd_closer(&dev_fd);
+    base::ScopedFD dev_fd_closer(dev_fd);
     EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
   }
 
@@ -112,11 +111,12 @@
 SANDBOX_TEST(Credentials, MoveToNewUserNS) {
   Credentials creds;
   creds.DropAllCapabilities();
-  bool userns_supported = creds.MoveToNewUserNS();
-  fprintf(stdout, "Unprivileged CLONE_NEWUSER supported: %s\n",
-          userns_supported ? "true." : "false.");
+  bool moved_to_new_ns = creds.MoveToNewUserNS();
+  fprintf(stdout,
+          "Unprivileged CLONE_NEWUSER supported: %s\n",
+          moved_to_new_ns ? "true." : "false.");
   fflush(stdout);
-  if (!userns_supported) {
+  if (!moved_to_new_ns) {
     fprintf(stdout, "This kernel does not support unprivileged namespaces. "
             "USERNS tests will succeed without running.\n");
     fflush(stdout);
@@ -127,6 +127,14 @@
   CHECK(!creds.HasAnyCapability());
 }
 
+SANDBOX_TEST(Credentials, SupportsUserNS) {
+  Credentials creds;
+  creds.DropAllCapabilities();
+  bool user_ns_supported = Credentials::SupportsNewUserNS();
+  bool moved_to_new_ns = creds.MoveToNewUserNS();
+  CHECK_EQ(user_ns_supported, moved_to_new_ns);
+}
+
 SANDBOX_TEST(Credentials, UidIsPreserved) {
   Credentials creds;
   creds.DropAllCapabilities();
@@ -207,6 +215,7 @@
 
   // The kernel should now prevent us from regaining capabilities because we
   // are in a chroot.
+  CHECK(!Credentials::SupportsNewUserNS());
   CHECK(!creds.MoveToNewUserNS());
 }
 
diff --git a/sandbox/linux/services/scoped_process.cc b/sandbox/linux/services/scoped_process.cc
new file mode 100644
index 0000000..6a03a69
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.cc
@@ -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.
+
+#include "sandbox/linux/services/scoped_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/thread_helpers.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kSynchronisationChar[] = "D";
+
+void WaitForever() {
+  while(true) {
+    pause();
+  }
+}
+
+}  // namespace
+
+ScopedProcess::ScopedProcess(const base::Closure& child_callback)
+    : child_process_id_(-1), process_id_(getpid()) {
+  PCHECK(0 == pipe(pipe_fds_));
+#if !defined(THREAD_SANITIZER)
+  // Make sure that we can safely fork().
+  CHECK(ThreadHelpers::IsSingleThreaded(-1));
+#endif
+  child_process_id_ = fork();
+  PCHECK(0 <= child_process_id_);
+
+  if (0 == child_process_id_) {
+    PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+    pipe_fds_[0] = -1;
+    child_callback.Run();
+    // Notify the parent that the closure has run.
+    CHECK_EQ(1, write(pipe_fds_[1], kSynchronisationChar, 1));
+    WaitForever();
+    NOTREACHED();
+    _exit(1);
+  }
+
+  PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+  pipe_fds_[1] = -1;
+}
+
+ScopedProcess::~ScopedProcess() {
+  CHECK(IsOriginalProcess());
+  if (child_process_id_ >= 0) {
+    PCHECK(0 == kill(child_process_id_, SIGKILL));
+    siginfo_t process_info;
+
+    PCHECK(0 == HANDLE_EINTR(
+                    waitid(P_PID, child_process_id_, &process_info, WEXITED)));
+  }
+  if (pipe_fds_[0] >= 0) {
+    PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+  }
+  if (pipe_fds_[1] >= 0) {
+    PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+  }
+}
+
+int ScopedProcess::WaitForExit(bool* got_signaled) {
+  DCHECK(got_signaled);
+  CHECK(IsOriginalProcess());
+  siginfo_t process_info;
+  // WNOWAIT to make sure that the destructor can wait on the child.
+  int ret = HANDLE_EINTR(
+      waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT));
+  PCHECK(0 == ret) << "Did something else wait on the child?";
+
+  if (process_info.si_code == CLD_EXITED) {
+    *got_signaled = false;
+  } else if (process_info.si_code == CLD_KILLED ||
+             process_info.si_code == CLD_DUMPED) {
+    *got_signaled = true;
+  } else {
+    CHECK(false) << "ScopedProcess needs to be extended for si_code "
+                 << process_info.si_code;
+  }
+  return process_info.si_status;
+}
+
+bool ScopedProcess::WaitForClosureToRun() {
+  char c = 0;
+  int ret = read(pipe_fds_[0], &c, 1);
+  PCHECK(ret >= 0);
+  if (0 == ret)
+    return false;
+
+  CHECK_EQ(c, kSynchronisationChar[0]);
+  return true;
+}
+
+// It would be problematic if after a fork(), another process would start using
+// this object.
+// 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);
+  return pid == process_id_;
+}
+
+}  // namespace sandbox
diff --git a/sandbox/linux/services/scoped_process.h b/sandbox/linux/services/scoped_process.h
new file mode 100644
index 0000000..d9f8b25
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.h
@@ -0,0 +1,54 @@
+// 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_SCOPED_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/process/process_handle.h"
+
+namespace sandbox {
+
+// fork() a child process that will run a Closure.
+// After the Closure has run, the child will pause forever. If this object
+// is detroyed, the child will be destroyed, even if the closure did not
+// finish running. It's ok to signal the child from outside of this class to
+// destroy it.
+// This class cannot be instanciated from a multi-threaded process, as it needs
+// to fork().
+class ScopedProcess {
+ public:
+  // A new process will be created and |child_callback| will run in the child
+  // process. This callback is allowed to terminate the process or to simply
+  // return. If the callback returns, the process will wait forever.
+  explicit ScopedProcess(const base::Closure& child_callback);
+  ~ScopedProcess();
+
+  // Wait for the process to exit.
+  // |got_signaled| tells how to interpret the return value: either as an exit
+  // code, or as a signal number.
+  // When this returns, the process will still not have been reaped and will
+  // survive as a zombie for the lifetime of this object. This method can be
+  // called multiple times.
+  int WaitForExit(bool* got_signaled);
+
+  // Wait for the |child_callback| passed at construction to run. Return false
+  // if |child_callback| did not finish running and we know it never will (for
+  // instance the child crashed or used _exit()).
+  bool WaitForClosureToRun();
+  base::ProcessId GetPid() { return child_process_id_; }
+
+ private:
+  bool IsOriginalProcess();
+
+  base::ProcessId child_process_id_;
+  base::ProcessId process_id_;
+  int pipe_fds_[2];
+  DISALLOW_COPY_AND_ASSIGN(ScopedProcess);
+};
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
diff --git a/sandbox/linux/services/scoped_process_unittest.cc b/sandbox/linux/services/scoped_process_unittest.cc
new file mode 100644
index 0000000..2800bd7
--- /dev/null
+++ b/sandbox/linux/services/scoped_process_unittest.cc
@@ -0,0 +1,129 @@
+// 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 <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoExit() { _exit(0); }
+
+void ExitWithCode(int exit_code) { _exit(exit_code); }
+
+void RaiseAndExit(int signal) {
+  PCHECK(0 == raise(signal));
+  _exit(0);
+}
+
+void DoNothing() {}
+
+TEST(ScopedProcess, ScopedProcessNormalExit) {
+  const int kCustomExitCode = 12;
+  ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode));
+  bool got_signaled = true;
+  int exit_code = process.WaitForExit(&got_signaled);
+  EXPECT_FALSE(got_signaled);
+  EXPECT_EQ(kCustomExitCode, exit_code);
+
+  // Verify that WaitForExit() can be called multiple times on the same
+  // process.
+  bool got_signaled2 = true;
+  int exit_code2 = process.WaitForExit(&got_signaled2);
+  EXPECT_FALSE(got_signaled2);
+  EXPECT_EQ(kCustomExitCode, exit_code2);
+}
+
+// Disable this test on Android, SIGABRT is funky there.
+TEST(ScopedProcess, DISABLE_ON_ANDROID(ScopedProcessAbort)) {
+  ScopedProcess process(base::Bind(&RaiseAndExit, SIGABRT));
+  bool got_signaled = false;
+  int exit_code = process.WaitForExit(&got_signaled);
+  EXPECT_TRUE(got_signaled);
+  EXPECT_EQ(SIGABRT, exit_code);
+}
+
+TEST(ScopedProcess, ScopedProcessSignaled) {
+  ScopedProcess process(base::Bind(&DoNothing));
+  bool got_signaled = false;
+  ASSERT_EQ(0, kill(process.GetPid(), SIGKILL));
+  int exit_code = process.WaitForExit(&got_signaled);
+  EXPECT_TRUE(got_signaled);
+  EXPECT_EQ(SIGKILL, exit_code);
+}
+
+TEST(ScopedProcess, DiesForReal) {
+  int pipe_fds[2];
+  ASSERT_EQ(0, pipe(pipe_fds));
+  base::ScopedFD read_end_closer(pipe_fds[0]);
+  base::ScopedFD write_end_closer(pipe_fds[1]);
+
+  { ScopedProcess process(base::Bind(&DoExit)); }
+
+  // Close writing end of the pipe.
+  write_end_closer.reset();
+  pipe_fds[1] = -1;
+
+  ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+  char c;
+  // If the child process is dead for real, there will be no writing end
+  // for this pipe left and read will EOF instead of returning EWOULDBLOCK.
+  ASSERT_EQ(0, read(pipe_fds[0], &c, 1));
+}
+
+TEST(ScopedProcess, SynchronizationBasic) {
+  ScopedProcess process1(base::Bind(&DoNothing));
+  EXPECT_TRUE(process1.WaitForClosureToRun());
+
+  ScopedProcess process2(base::Bind(&DoExit));
+  // The closure didn't finish running normally. This case is simple enough
+  // that process.WaitForClosureToRun() should return false, even though the
+  // API does not guarantees that it will return at all.
+  EXPECT_FALSE(process2.WaitForClosureToRun());
+}
+
+void SleepInMsAndWriteOneByte(int time_to_sleep, int fd) {
+  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(time_to_sleep));
+  CHECK(1 == write(fd, "1", 1));
+}
+
+TEST(ScopedProcess, SynchronizationWorks) {
+  int pipe_fds[2];
+  ASSERT_EQ(0, pipe(pipe_fds));
+  base::ScopedFD read_end_closer(pipe_fds[0]);
+  base::ScopedFD write_end_closer(pipe_fds[1]);
+
+  // Start a process with a closure that takes a little bit to run.
+  ScopedProcess process(
+      base::Bind(&SleepInMsAndWriteOneByte, 100, pipe_fds[1]));
+  EXPECT_TRUE(process.WaitForClosureToRun());
+
+  // Verify that the closure did, indeed, run.
+  ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+  char c = 0;
+  EXPECT_EQ(1, read(pipe_fds[0], &c, 1));
+  EXPECT_EQ('1', c);
+}
+
+}  // namespace
+
+}  // namespace sandbox
diff --git a/sandbox/linux/services/thread_helpers.cc b/sandbox/linux/services/thread_helpers.cc
index e0794f8..e820449 100644
--- a/sandbox/linux/services/thread_helpers.cc
+++ b/sandbox/linux/services/thread_helpers.cc
@@ -5,6 +5,7 @@
 #include "sandbox/linux/services/thread_helpers.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -21,7 +22,9 @@
 
 namespace sandbox {
 
-bool ThreadHelpers::IsSingleThreaded(int proc_self_task) {
+namespace {
+
+bool IsSingleThreadedImpl(int proc_self_task) {
   CHECK_LE(0, proc_self_task);
   struct stat task_stat;
   int fstat_ret = fstat(proc_self_task, &task_stat);
@@ -35,6 +38,21 @@
   return task_stat.st_nlink == 3;
 }
 
+}  // namespace
+
+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);
+    PCHECK(0 <= task_fd);
+    const bool result = IsSingleThreadedImpl(task_fd);
+    PCHECK(0 == IGNORE_EINTR(close(task_fd)));
+    return result;
+  } else {
+    return IsSingleThreadedImpl(proc_self_task);
+  }
+}
+
 bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_self_task,
                                              base::Thread* thread) {
   DCHECK_LE(0, proc_self_task);
diff --git a/sandbox/linux/services/thread_helpers.h b/sandbox/linux/services/thread_helpers.h
index 651e5d9..f1b9327 100644
--- a/sandbox/linux/services/thread_helpers.h
+++ b/sandbox/linux/services/thread_helpers.h
@@ -14,8 +14,10 @@
 class ThreadHelpers {
  public:
   // Check whether the current process is single threaded. |proc_self_tasks|
-  // should be a file descriptor to /proc/self/task/ and remains owned by the
-  // caller.
+  // can be a file descriptor to /proc/self/task/ and remains owned by the
+  // caller or -1.
+  // If |proc_self_tasks| is -1, this method will open /proc/self/task/ and
+  // crash if it cannot.
   static bool IsSingleThreaded(int proc_self_task);
 
   // Stop |thread| and ensure that it does not have an entry in
@@ -28,6 +30,6 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadHelpers);
 };
 
-}  // namespace content
+}  // namespace sandbox
 
 #endif  // SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
diff --git a/sandbox/linux/services/thread_helpers_unittests.cc b/sandbox/linux/services/thread_helpers_unittests.cc
index 991e60e..a36fd29 100644
--- a/sandbox/linux/services/thread_helpers_unittests.cc
+++ b/sandbox/linux/services/thread_helpers_unittests.cc
@@ -17,6 +17,7 @@
 #include "base/process/process_metrics.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread.h"
+#include "build/build_config.h"
 #include "sandbox/linux/tests/unit_tests.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -64,10 +65,12 @@
 TEST(ThreadHelpers, MAYBE_IsSingleThreadedBasic) {
   ScopedProcSelfTask task;
   ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(task.fd()));
+  ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(-1));
 
   base::Thread thread("sandbox_tests");
   ASSERT_TRUE(thread.Start());
   ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(task.fd()));
+  ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(-1));
   // Explicitly stop the thread here to not pollute the next test.
   ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(task.fd(), &thread));
 }
diff --git a/sandbox/linux/services/yama.cc b/sandbox/linux/services/yama.cc
new file mode 100644
index 0000000..39ac079
--- /dev/null
+++ b/sandbox/linux/services/yama.cc
@@ -0,0 +1,116 @@
+// 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/yama.h"
+
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+#if !defined(PR_SET_PTRACER_ANY)
+#define PR_SET_PTRACER_ANY ((unsigned long)-1)
+#endif
+
+#if !defined(PR_SET_PTRACER)
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// Enable or disable the Yama ptracers restrictions.
+// Return false if Yama is not present on this kernel.
+bool SetYamaPtracersRestriction(bool enable_restrictions) {
+  unsigned long set_ptracer_arg;
+  if (enable_restrictions) {
+    set_ptracer_arg = 0;
+  } else {
+    set_ptracer_arg = PR_SET_PTRACER_ANY;
+  }
+
+  const int ret = prctl(PR_SET_PTRACER, set_ptracer_arg);
+  const int prctl_errno = errno;
+
+  if (0 == ret) {
+    return true;
+  } else {
+    // ENOSYS or EINVAL means Yama is not in the current kernel.
+    CHECK(ENOSYS == prctl_errno || EINVAL == prctl_errno);
+    return false;
+  }
+}
+
+bool CanAccessProcFS() {
+  static const char kProcfsKernelSysPath[] = "/proc/sys/kernel/";
+  int ret = access(kProcfsKernelSysPath, F_OK);
+  if (ret) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+// static
+bool Yama::RestrictPtracersToAncestors() {
+  return SetYamaPtracersRestriction(true /* enable_restrictions */);
+}
+
+// static
+bool Yama::DisableYamaRestrictions() {
+  return SetYamaPtracersRestriction(false /* enable_restrictions */);
+}
+
+// static
+int Yama::GetStatus() {
+  if (!CanAccessProcFS()) {
+    return 0;
+  }
+
+  static const char kPtraceScopePath[] = "/proc/sys/kernel/yama/ptrace_scope";
+
+  base::ScopedFD yama_scope(open(kPtraceScopePath, O_RDONLY));
+
+  if (!yama_scope.is_valid()) {
+    const int open_errno = errno;
+    DCHECK(ENOENT == open_errno);
+    // The status is known, yama is not present.
+    return STATUS_KNOWN;
+  }
+
+  char yama_scope_value = 0;
+  ssize_t num_read = read(yama_scope.get(), &yama_scope_value, 1);
+  PCHECK(1 == num_read);
+
+  switch (yama_scope_value) {
+    case '0':
+      return STATUS_KNOWN | STATUS_PRESENT;
+    case '1':
+      return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING;
+    case '2':
+    case '3':
+      return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING |
+             STATUS_STRICT_ENFORCING;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+// static
+bool Yama::IsPresent() { return GetStatus() & STATUS_PRESENT; }
+
+// static
+bool Yama::IsEnforcing() { return GetStatus() & STATUS_ENFORCING; }
+
+}  // namespace sandbox
diff --git a/sandbox/linux/services/yama.h b/sandbox/linux/services/yama.h
new file mode 100644
index 0000000..236b74c
--- /dev/null
+++ b/sandbox/linux/services/yama.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_YAMA_H_
+#define SANDBOX_LINUX_SERVICES_YAMA_H_
+
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+
+namespace sandbox {
+
+// Yama is a LSM kernel module which can restrict ptrace().
+// This class provides ways to detect if Yama is present and enabled
+// and to restrict which processes can ptrace the current process.
+class Yama {
+ public:
+  // This enum should be used to set or check a bitmask.
+  // A value of 0 would indicate that the status is not known.
+  enum GlobalStatus {
+    STATUS_KNOWN = 1 << 0,
+    STATUS_PRESENT = 1 << 1,
+    STATUS_ENFORCING = 1 << 2,
+    // STATUS_STRICT_ENFORCING corresponds to either mode 2 or mode 3 of Yama.
+    // Ptrace could be entirely denied, or restricted to CAP_SYS_PTRACE
+    // and PTRACE_TRACEME.
+    STATUS_STRICT_ENFORCING = 1 << 3
+  };
+
+  // Restrict who can ptrace() the current process to its ancestors.
+  // If this succeeds, then Yama is available on this kernel.
+  // However, Yama may not be enforcing at this time.
+  static bool RestrictPtracersToAncestors();
+
+  // Disable Yama restrictions for the current process.
+  // This will fail if Yama is not available on this kernel.
+  // This is meant for testing only. If you need this, implement
+  // a per-pid authorization instead.
+  static bool DisableYamaRestrictions();
+
+  // Checks if Yama is currently in enforcing mode for the machine (not the
+  // current process). This requires access to the filesystem and will use
+  // /proc/sys/kernel/yama/ptrace_scope.
+  static int GetStatus();
+
+  // Helper for checking for STATUS_PRESENT in GetStatus().
+  static bool IsPresent();
+  // Helper for checkking for STATUS_ENFORCING in GetStatus().
+  static bool IsEnforcing();
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Yama);
+};
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SERVICES_YAMA_H_
diff --git a/sandbox/linux/services/yama_unittests.cc b/sandbox/linux/services/yama_unittests.cc
new file mode 100644
index 0000000..17ef4b4
--- /dev/null
+++ b/sandbox/linux/services/yama_unittests.cc
@@ -0,0 +1,152 @@
+// 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 <errno.h>
+#include <fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/services/yama.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool CanPtrace(pid_t pid) {
+  int ret;
+  ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
+  if (ret == -1) {
+    CHECK_EQ(EPERM, errno);
+    return false;
+  }
+  // Wait for the process to be stopped so that it can be detached.
+  siginfo_t process_info;
+  int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
+  PCHECK(0 == wait_ret);
+  PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
+  return true;
+}
+
+// _exit(0) if pid can be ptraced by the current process.
+// _exit(1) otherwise.
+void ExitZeroIfCanPtrace(pid_t pid) {
+  if (CanPtrace(pid)) {
+    _exit(0);
+  } else {
+    _exit(1);
+  }
+}
+
+bool CanSubProcessPtrace(pid_t pid) {
+  ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
+  bool signaled;
+  int exit_code = process.WaitForExit(&signaled);
+  CHECK(!signaled);
+  return 0 == exit_code;
+}
+
+// The tests below assume that the system-level configuration will not change
+// while they run.
+
+TEST(Yama, GetStatus) {
+  int status1 = Yama::GetStatus();
+
+  // Check that the value is a possible bitmask.
+  ASSERT_LE(0, status1);
+  ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
+                Yama::STATUS_STRICT_ENFORCING,
+            status1);
+
+  // The status should not just be a random value.
+  int status2 = Yama::GetStatus();
+  EXPECT_EQ(status1, status2);
+
+  // This test is not running sandboxed, there is no reason to not know the
+  // status.
+  EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
+
+  if (status1 & Yama::STATUS_STRICT_ENFORCING) {
+    // If Yama is strictly enforcing, it is also enforcing.
+    EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
+  }
+
+  if (status1 & Yama::STATUS_ENFORCING) {
+    // If Yama is enforcing, Yama is present.
+    EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
+  }
+
+  // Verify that the helper functions work as intended.
+  EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
+            Yama::IsEnforcing());
+  EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
+            Yama::IsPresent());
+
+  fprintf(stdout,
+          "Yama present: %s - enforcing: %s\n",
+          Yama::IsPresent() ? "Y" : "N",
+          Yama::IsEnforcing() ? "Y" : "N");
+}
+
+SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
+  // This call will succeed iff Yama is present.
+  bool restricted = Yama::RestrictPtracersToAncestors();
+  CHECK_EQ(restricted, Yama::IsPresent());
+}
+
+// Attempts to enable or disable Yama restrictions.
+void SetYamaRestrictions(bool enable_restriction) {
+  if (enable_restriction) {
+    Yama::RestrictPtracersToAncestors();
+  } else {
+    Yama::DisableYamaRestrictions();
+  }
+}
+
+TEST(Yama, RestrictPtraceWorks) {
+  ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
+  ASSERT_TRUE(process1.WaitForClosureToRun());
+
+  if (Yama::IsEnforcing()) {
+    // A sibling process cannot ptrace process1.
+    ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
+  }
+
+  if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
+    // However, parent can ptrace process1.
+    ASSERT_TRUE(CanPtrace(process1.GetPid()));
+
+    // A sibling can ptrace process2 which disables any Yama protection.
+    ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
+    ASSERT_TRUE(process2.WaitForClosureToRun());
+    ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
+  }
+}
+
+void DoNothing() {}
+
+SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
+  if (!Yama::IsPresent())
+    return;
+
+  CHECK(Yama::DisableYamaRestrictions());
+  ScopedProcess process1(base::Bind(&DoNothing));
+
+  if (Yama::IsEnforcing()) {
+    // Check that process1 is protected by Yama, even though it has
+    // been created from a process that disabled Yama.
+    CHECK(!CanSubProcessPtrace(process1.GetPid()));
+  }
+}
+
+}  // namespace
+
+}  // namespace sandbox
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc
index 740823a..224f754 100644
--- a/sandbox/linux/suid/client/setuid_sandbox_client.cc
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -129,7 +129,7 @@
   // We need to reap the chroot helper process in any event.
   pid_t helper_pid = GetHelperPID(env_);
   // If helper_pid is -1 we wait for any child.
-  if (waitpid(helper_pid, NULL, 0) < 0) {
+  if (HANDLE_EINTR(waitpid(helper_pid, NULL, 0)) < 0) {
     PLOG(ERROR) << "Failed to wait for setuid helper to die";
     return false;
   }
@@ -182,4 +182,3 @@
 }
 
 }  // namespace sandbox
-
diff --git a/sandbox/linux/tests/main.cc b/sandbox/linux/tests/main.cc
index 8fd85d9..b07718e 100644
--- a/sandbox/linux/tests/main.cc
+++ b/sandbox/linux/tests/main.cc
@@ -3,8 +3,25 @@
 // found in the LICENSE file.
 
 #include "base/at_exit.h"
+#include "base/logging.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 {
+
+// Check for leaks in our tests.
+void RunPostTestsChecks() {
+  if (TestUtils::CurrentProcessHasChildren()) {
+    LOG(ERROR) << "One of the tests created a child that was not waited for. "
+               << "Please, clean-up after your tests!";
+  }
+}
+
+}  // namespace
+}  // namespace sandbox
+
 int main(int argc, char* argv[]) {
   // The use of Callbacks requires an AtExitManager.
   base::AtExitManager exit_manager;
@@ -14,5 +31,8 @@
   // additional side effect of getting rid of gtest warnings about fork()
   // safety.
   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
-  return RUN_ALL_TESTS();
+  int tests_result = RUN_ALL_TESTS();
+
+  sandbox::RunPostTestsChecks();
+  return tests_result;
 }
diff --git a/sandbox/linux/tests/test_utils.cc b/sandbox/linux/tests/test_utils.cc
new file mode 100644
index 0000000..398654b
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.cc
@@ -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.
+
+#include "sandbox/linux/tests/test_utils.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+bool TestUtils::CurrentProcessHasChildren() {
+  siginfo_t process_info;
+  int ret = HANDLE_EINTR(
+      waitid(P_ALL, 0, &process_info, WEXITED | WNOHANG | WNOWAIT));
+  if (-1 == ret) {
+    PCHECK(ECHILD == errno);
+    return false;
+  } else {
+    return true;
+  }
+}
+
+}  // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.h b/sandbox/linux/tests/test_utils.h
new file mode 100644
index 0000000..3269847
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.h
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_TEST_UTILS_H_
+#define SANDBOX_LINUX_TESTS_TEST_UTILS_H_
+
+#include "base/basictypes.h"
+
+namespace sandbox {
+
+// This class provide small helpers to help writing tests.
+class TestUtils {
+ public:
+  static bool CurrentProcessHasChildren();
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(TestUtils);
+};
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_TESTS_TEST_UTILS_H_
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
index ad30d84..2e714aa 100644
--- a/sandbox/linux/tests/unit_tests.cc
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -157,7 +157,7 @@
   // Make sure read() will never block as we'll use poll() to
   // block with a timeout instead.
   const int fcntl_ret = fcntl(fds[0], F_SETFL, O_NONBLOCK);
-  ASSERT_EQ(fcntl_ret, 0);
+  ASSERT_EQ(0, fcntl_ret);
   struct pollfd poll_fd = {fds[0], POLLIN | POLLRDHUP, 0};
 
   int poll_ret;
@@ -232,7 +232,7 @@
   bool subprocess_terminated_normally = WIFEXITED(status);
   ASSERT_TRUE(subprocess_terminated_normally) << details;
   int subprocess_exit_status = WEXITSTATUS(status);
-  ASSERT_EQ(subprocess_exit_status, expected_exit_code) << details;
+  ASSERT_EQ(expected_exit_code, subprocess_exit_status) << details;
 }
 
 void UnitTests::DeathBySignal(int status,
@@ -244,7 +244,7 @@
   bool subprocess_terminated_by_signal = WIFSIGNALED(status);
   ASSERT_TRUE(subprocess_terminated_by_signal) << details;
   int subprocess_signal_number = WTERMSIG(status);
-  ASSERT_EQ(subprocess_signal_number, expected_signo) << details;
+  ASSERT_EQ(expected_signo, subprocess_signal_number) << details;
 }
 
 void UnitTests::AssertionFailure(const char* expr, const char* file, int line) {
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h
index 5480b56..74aabe4 100644
--- a/sandbox/linux/tests/unit_tests.h
+++ b/sandbox/linux/tests/unit_tests.h
@@ -25,6 +25,12 @@
 #define DISABLE_ON_TSAN(test_name) test_name
 #endif  // defined(THREAD_SANITIZER)
 
+#if defined(OS_ANDROID)
+#define DISABLE_ON_ANDROID(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_ANDROID(test_name) test_name
+#endif
+
 // While it is perfectly OK for a complex test to provide its own DeathCheck
 // function. Most death tests have very simple requirements. These tests should
 // use one of the predefined DEATH_XXX macros as an argument to