Merge from Chromium at DEPS revision r167172

This commit was generated by merge_to_master.py.

Change-Id: Ib8d56fd5ae39a2d7e8c91dcd76cc6d13f25f2aab
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc
new file mode 100644
index 0000000..45d700b
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
+#include "setuid_sandbox_client.h"
+
+namespace {
+
+// Set an environment variable that reflects the API version we expect from the
+// setuid sandbox. Old versions of the sandbox will ignore this.
+void SetSandboxAPIEnvironmentVariable(base::Environment* env) {
+  env->SetVar(sandbox::kSandboxEnvironmentApiRequest,
+              base::IntToString(sandbox::kSUIDSandboxApiNumber));
+}
+
+// Wrapper around a shared C function.
+// Returns the "saved" environment variable name corresponding to |envvar|
+// in a new string or NULL.
+std::string* CreateSavedVariableName(const char* env_var) {
+  char* const saved_env_var = SandboxSavedEnvironmentVariable(env_var);
+  if (!saved_env_var)
+    return NULL;
+  std::string* saved_env_var_copy = new std::string(saved_env_var);
+  // SandboxSavedEnvironmentVariable is the C function that we wrap and uses
+  // malloc() to allocate memory.
+  free(saved_env_var);
+  return saved_env_var_copy;
+}
+
+// The ELF loader will clear many environment variables so we save them to
+// different names here so that the SUID sandbox can resolve them for the
+// renderer.
+void SaveSUIDUnsafeEnvironmentVariables(base::Environment* env) {
+  for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
+    const char* env_var = kSUIDUnsafeEnvironmentVariables[i];
+    // Get the saved environment variable corresponding to envvar.
+    scoped_ptr<std::string> saved_env_var(CreateSavedVariableName(env_var));
+    if (saved_env_var == NULL)
+      continue;
+
+    std::string value;
+    if (env->GetVar(env_var, &value))
+      env->SetVar(saved_env_var->c_str(), value);
+    else
+      env->UnSetVar(saved_env_var->c_str());
+  }
+}
+
+int GetHelperApi(base::Environment* env) {
+  std::string api_string;
+  int api_number = 0;  // Assume API version 0 if no environment was found.
+  if (env->GetVar(sandbox::kSandboxEnvironmentApiProvides, &api_string) &&
+      !base::StringToInt(api_string, &api_number)) {
+    // It's an error if we could not convert the API number.
+    api_number = -1;
+  }
+  return api_number;
+}
+
+// Convert |var_name| from the environment |env| to an int.
+// Return -1 if the variable does not exist or the value cannot be converted.
+int EnvToInt(base::Environment* env, const char* var_name) {
+  std::string var_string;
+  int var_value = -1;
+  if (env->GetVar(var_name, &var_string) &&
+      !base::StringToInt(var_string, &var_value)) {
+    var_value = -1;
+  }
+  return var_value;
+}
+
+pid_t GetHelperPID(base::Environment* env) {
+  return EnvToInt(env, sandbox::kSandboxHelperPidEnvironmentVarName);
+}
+
+// Get the IPC file descriptor used to communicate with the setuid helper.
+int GetIPCDescriptor(base::Environment* env) {
+  return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName);
+}
+
+}  // namespace
+
+namespace sandbox {
+
+SetuidSandboxClient* SetuidSandboxClient::Create() {
+  base::Environment* environment(base::Environment::Create());
+  SetuidSandboxClient* sandbox_client(new(SetuidSandboxClient));
+
+  CHECK(environment);
+  sandbox_client->env_ = environment;
+  return sandbox_client;
+}
+
+SetuidSandboxClient::SetuidSandboxClient()
+    : env_(NULL),
+      sandboxed_(false) {
+}
+
+SetuidSandboxClient::~SetuidSandboxClient() {
+  delete env_;
+}
+
+bool SetuidSandboxClient::ChrootMe() {
+  int ipc_fd = GetIPCDescriptor(env_);
+
+  if (ipc_fd < 0) {
+    LOG(ERROR) << "Failed to obtain the sandbox IPC descriptor";
+    return false;
+  }
+
+  if (HANDLE_EINTR(write(ipc_fd, &kMsgChrootMe, 1)) != 1) {
+    PLOG(ERROR) << "Failed to write to chroot pipe";
+    return false;
+  }
+
+  // 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) {
+    PLOG(ERROR) << "Failed to wait for setuid helper to die";
+    return false;
+  }
+
+  char reply;
+  if (HANDLE_EINTR(read(ipc_fd, &reply, 1)) != 1) {
+    PLOG(ERROR) << "Failed to read from chroot pipe";
+    return false;
+  }
+
+  if (reply != kMsgChrootSuccessful) {
+    LOG(ERROR) << "Error code reply from chroot helper";
+    return false;
+  }
+
+  // We now consider ourselves "fully sandboxed" as far as the
+  // setuid sandbox is concerned.
+  sandboxed_ = true;
+  return true;
+}
+
+bool SetuidSandboxClient::IsSuidSandboxUpToDate() const {
+  return GetHelperApi(env_) == kSUIDSandboxApiNumber;
+}
+
+bool SetuidSandboxClient::IsSuidSandboxChild() const {
+  return GetIPCDescriptor(env_) >= 0;
+}
+
+bool SetuidSandboxClient::IsInNewPIDNamespace() const {
+  return env_->HasVar(kSandboxPIDNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsInNewNETNamespace() const {
+  return env_->HasVar(kSandboxNETNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsSandboxed() const {
+  return sandboxed_;
+}
+
+void SetuidSandboxClient::SetupLaunchEnvironment() {
+  SaveSUIDUnsafeEnvironmentVariables(env_);
+  SetSandboxAPIEnvironmentVariable(env_);
+}
+
+}  // namespace sandbox
+
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.h b/sandbox/linux/suid/client/setuid_sandbox_client.h
new file mode 100644
index 0000000..a9f6536
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+
+#include "base/basictypes.h"
+
+namespace base { class Environment; }
+
+namespace sandbox {
+
+// Helper class to use the setuid sandbox. This class is to be used both
+// before launching the setuid helper and after being executed through the
+// setuid helper.
+//
+// A typical use would be:
+// 1. The browser calls SetupLaunchEnvironment()
+// 2. The browser launches a renderer through the setuid sandbox.
+// 3. The renderer requests being chroot-ed through ChrootMe() and
+//    requests other sandboxing status via the status functions.
+class SetuidSandboxClient {
+ public:
+  // All instantation should go through this factory method.
+  static class SetuidSandboxClient* Create();
+  ~SetuidSandboxClient();
+
+  // Ask the setuid helper over the setuid sandbox IPC channel to chroot() us
+  // to an empty directory.
+  // Will only work if we have been launched through the setuid helper.
+  bool ChrootMe();
+
+  // Did we get launched through an up to date setuid binary ?
+  bool IsSuidSandboxUpToDate() const;
+  // Did we get launched through the setuid helper ?
+  bool IsSuidSandboxChild() const;
+  // Did the setuid helper create a new PID namespace ?
+  bool IsInNewPIDNamespace() const;
+  // Did the setuid helper create a new network namespace ?
+  bool IsInNewNETNamespace() const;
+  // Are we done and fully sandboxed ?
+  bool IsSandboxed() const;
+
+  // Set-up the environment. This should be done prior to launching the setuid
+  // helper.
+  void SetupLaunchEnvironment();
+
+ private:
+  // Holds the environment. Will never be NULL.
+  base::Environment* env_;
+  bool sandboxed_;
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SetuidSandboxClient);
+};
+
+}  // namespace sandbox
+
+#endif  // SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
new file mode 100644
index 0000000..293f423
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "setuid_sandbox_client.h"
+
+namespace sandbox {
+
+TEST(SetuidSandboxClient, SetupLaunchEnvironment) {
+  const char kTestValue[] = "This is a test";
+  scoped_ptr<base::Environment> env(base::Environment::Create());
+  EXPECT_TRUE(env != NULL);
+
+  std::string saved_ld_preload;
+  bool environment_had_ld_preload;
+  // First, back-up the real LD_PRELOAD if any.
+  environment_had_ld_preload = env->GetVar("LD_PRELOAD", &saved_ld_preload);
+  // Setup environment variables to save or not save.
+  EXPECT_TRUE(env->SetVar("LD_PRELOAD", kTestValue));
+  EXPECT_TRUE(env->UnSetVar("LD_ORIGIN_PATH"));
+
+  scoped_ptr<SetuidSandboxClient>
+      sandbox_client(SetuidSandboxClient::Create());
+  EXPECT_TRUE(sandbox_client != NULL);
+
+  // Make sure the environment is clean.
+  EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiRequest));
+  EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiProvides));
+
+  sandbox_client->SetupLaunchEnvironment();
+
+  // Check if the requested API environment was set.
+  std::string api_request;
+  EXPECT_TRUE(env->GetVar(kSandboxEnvironmentApiRequest, &api_request));
+  int api_request_num;
+  EXPECT_TRUE(base::StringToInt(api_request, &api_request_num));
+  EXPECT_EQ(api_request_num, kSUIDSandboxApiNumber);
+
+  // Now check if LD_PRELOAD was saved to SANDBOX_LD_PRELOAD.
+  std::string sandbox_ld_preload;
+  EXPECT_TRUE(env->GetVar("SANDBOX_LD_PRELOAD", &sandbox_ld_preload));
+  EXPECT_EQ(sandbox_ld_preload, kTestValue);
+
+  // Check that LD_ORIGIN_PATH was not saved.
+  EXPECT_FALSE(env->HasVar("SANDBOX_LD_ORIGIN_PATH"));
+
+  // We should not forget to restore LD_PRELOAD at the end, or this environment
+  // variable will affect the next running tests!
+  if (environment_had_ld_preload) {
+    EXPECT_TRUE(env->SetVar("LD_PRELOAD", saved_ld_preload));
+  } else {
+    EXPECT_TRUE(env->UnSetVar("LD_PRELOAD"));
+  }
+}
+
+TEST(SetuidSandboxClient, SandboxedClientAPI) {
+  scoped_ptr<base::Environment> env(base::Environment::Create());
+  EXPECT_TRUE(env != NULL);
+
+  scoped_ptr<SetuidSandboxClient>
+      sandbox_client(SetuidSandboxClient::Create());
+  EXPECT_TRUE(sandbox_client != NULL);
+
+  // Set-up a fake environment as if we went through the setuid sandbox.
+  EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
+              base::IntToString(kSUIDSandboxApiNumber)));
+  EXPECT_TRUE(env->SetVar(kSandboxDescriptorEnvironmentVarName, "1"));
+  EXPECT_TRUE(env->SetVar(kSandboxPIDNSEnvironmentVarName, "1"));
+  EXPECT_TRUE(env->UnSetVar(kSandboxNETNSEnvironmentVarName));
+
+  // Check the API.
+  EXPECT_TRUE(sandbox_client->IsSuidSandboxUpToDate());
+  EXPECT_TRUE(sandbox_client->IsSuidSandboxChild());
+  EXPECT_TRUE(sandbox_client->IsInNewPIDNamespace());
+  EXPECT_FALSE(sandbox_client->IsInNewNETNamespace());
+
+  // Forge an incorrect API version and check.
+  EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
+              base::IntToString(kSUIDSandboxApiNumber + 1)));
+  EXPECT_FALSE(sandbox_client->IsSuidSandboxUpToDate());
+  // We didn't go through the actual sandboxing mechanism as it is
+  // very hard in a unit test.
+  EXPECT_FALSE(sandbox_client->IsSandboxed());
+}
+
+}  // namespace sandbox
+
diff --git a/sandbox/linux/suid/common/sandbox.h b/sandbox/linux/suid/common/sandbox.h
new file mode 100644
index 0000000..aad4ff8
--- /dev/null
+++ b/sandbox/linux/suid/common/sandbox.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SUID_SANDBOX_H_
+#define SANDBOX_LINUX_SUID_SANDBOX_H_
+
+#if defined(__cplusplus)
+namespace sandbox {
+#endif
+
+// These are command line switches that may be used by other programs
+// (e.g. Chrome) to construct a command line for the sandbox.
+static const char kAdjustOOMScoreSwitch[] = "--adjust-oom-score";
+#if defined(OS_CHROMEOS)
+static const char kAdjustLowMemMarginSwitch[] = "--adjust-low-mem";
+#endif
+
+static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D";
+static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID";
+
+static const long kSUIDSandboxApiNumber = 1;
+static const char kSandboxEnvironmentApiRequest[] = "SBX_CHROME_API_RQ";
+static const char kSandboxEnvironmentApiProvides[] = "SBX_CHROME_API_PRV";
+
+// This number must be kept in sync with common/zygote_commands_linux.h
+static const int kZygoteIdFd = 7;
+
+// These are the magic byte values which the sandboxed process uses to request
+// that it be chrooted.
+static const char kMsgChrootMe = 'C';
+static const char kMsgChrootSuccessful = 'O';
+
+// These are set if we have respectively switched to a new PID or NET namespace
+// by going through the setuid binary helper.
+static const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS";
+static const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS";
+
+#if defined(__cplusplus)
+}  // namespace sandbox
+#endif
+
+#endif  // SANDBOX_LINUX_SUID_SANDBOX_H_
diff --git a/sandbox/linux/suid/common/suid_unsafe_environment_variables.h b/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
new file mode 100644
index 0000000..ee4db76
--- /dev/null
+++ b/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a list of environment variables which the ELF loader unsets when
+// loading a SUID binary. Because they are unset rather than just ignored, they
+// aren't passed to child processes of SUID processes either.
+//
+// We need to save these environment variables before running a SUID sandbox
+// and restore them before running child processes (but after dropping root).
+//
+// List gathered from glibc sources (00ebd7ed58df389a78e41dece058048725cb585e):
+//   sysdeps/unix/sysv/linux/i386/dl-librecon.h
+//   sysdeps/generic/unsecvars.h
+
+#ifndef SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
+#define SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
+
+#if defined(__cplusplus)
+#include <limits>
+#define SIZE_MAX std::numeric_limits<size_t>::max()
+#endif
+
+#include <stdlib.h>  // malloc
+#include <string.h>  // memcpy
+
+static const char* kSUIDUnsafeEnvironmentVariables[] = {
+  "LD_AOUT_LIBRARY_PATH",
+  "LD_AOUT_PRELOAD",
+  "GCONV_PATH",
+  "GETCONF_DIR",
+  "HOSTALIASES",
+  "LD_AUDIT",
+  "LD_DEBUG",
+  "LD_DEBUG_OUTPUT",
+  "LD_DYNAMIC_WEAK",
+  "LD_LIBRARY_PATH",
+  "LD_ORIGIN_PATH",
+  "LD_PRELOAD",
+  "LD_PROFILE",
+  "LD_SHOW_AUXV",
+  "LD_USE_LOAD_BIAS",
+  "LOCALDOMAIN",
+  "LOCPATH",
+  "MALLOC_TRACE",
+  "NIS_PATH",
+  "NLSPATH",
+  "RESOLV_HOST_CONF",
+  "RES_OPTIONS",
+  "TMPDIR",
+  "TZDIR",
+  NULL,
+};
+
+// Return a malloc allocated string containing the 'saved' environment variable
+// name for a given environment variable.
+static inline char* SandboxSavedEnvironmentVariable(const char* envvar) {
+  const size_t envvar_len = strlen(envvar);
+
+  if (envvar_len > SIZE_MAX - 1 -8)
+    return NULL;
+
+  const size_t saved_envvarlen = envvar_len + 1 /* NUL terminator */ +
+                                              8 /* strlen("SANDBOX_") */;
+  char* const saved_envvar = (char*) malloc(saved_envvarlen);
+  if (!saved_envvar)
+    return NULL;
+
+  memcpy(saved_envvar, "SANDBOX_", 8);
+  memcpy(saved_envvar + 8, envvar, envvar_len);
+  saved_envvar[8 + envvar_len] = 0;
+
+  return saved_envvar;
+}
+
+#if defined(__cplusplus)
+#undef SIZE_MAX
+#endif
+
+#endif  // SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
diff --git a/sandbox/linux/suid/linux_util.c b/sandbox/linux/suid/linux_util.c
new file mode 100644
index 0000000..c5af0d0
--- /dev/null
+++ b/sandbox/linux/suid/linux_util.c
@@ -0,0 +1,112 @@
+// Copyright (c) 2009 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.
+
+// The following is duplicated from base/linux_utils.cc.
+// We shouldn't link against C++ code in a setuid binary.
+
+#include "linux_util.h"
+
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// expected prefix of the target of the /proc/self/fd/%d link for a socket
+static const char kSocketLinkPrefix[] = "socket:[";
+
+// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
+// socket.
+//   inode_out: (output) set to the inode number on success
+//   path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
+static bool ProcPathGetInode(ino_t* inode_out, const char* path) {
+  char buf[256];
+  const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
+  if (n == -1)
+    return false;
+  buf[n] = 0;
+
+  if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1))
+    return false;
+
+  char *endptr;
+  const unsigned long long int inode_ul =
+      strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
+  if (*endptr != ']')
+    return false;
+
+  if (inode_ul == ULLONG_MAX)
+    return false;
+
+  *inode_out = inode_ul;
+  return true;
+}
+
+bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
+  bool already_found = false;
+
+  DIR* proc = opendir("/proc");
+  if (!proc)
+    return false;
+
+  const uid_t uid = getuid();
+  struct dirent* dent;
+  while ((dent = readdir(proc))) {
+    char *endptr;
+    const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
+    if (pid_ul == ULONG_MAX || *endptr)
+      continue;
+
+    // We have this setuid code here because the zygote and its children have
+    // /proc/$pid/fd owned by root. While scanning through /proc, we add this
+    // extra check so users cannot accidentally gain information about other
+    // users' processes. To determine process ownership, we use the property
+    // that if user foo owns process N, then /proc/N is owned by foo.
+    {
+      char buf[256];
+      struct stat statbuf;
+      snprintf(buf, sizeof(buf), "/proc/%lu", pid_ul);
+      if (stat(buf, &statbuf) < 0)
+        continue;
+      if (uid != statbuf.st_uid)
+        continue;
+    }
+
+    char buf[256];
+    snprintf(buf, sizeof(buf), "/proc/%lu/fd", pid_ul);
+    DIR* fd = opendir(buf);
+    if (!fd)
+      continue;
+
+    while ((dent = readdir(fd))) {
+      int printed = snprintf(buf, sizeof(buf), "/proc/%lu/fd/%s", pid_ul,
+                             dent->d_name);
+      if (printed < 0 || printed >= (int)(sizeof(buf) - 1)) {
+        continue;
+      }
+
+      ino_t fd_inode;
+      if (ProcPathGetInode(&fd_inode, buf)) {
+        if (fd_inode == socket_inode) {
+          if (already_found) {
+            closedir(fd);
+            closedir(proc);
+            return false;
+          }
+
+          already_found = true;
+          *pid_out = pid_ul;
+          break;
+        }
+      }
+    }
+    closedir(fd);
+  }
+  closedir(proc);
+
+  return already_found;
+}
diff --git a/sandbox/linux/suid/linux_util.h b/sandbox/linux/suid/linux_util.h
new file mode 100644
index 0000000..d064252
--- /dev/null
+++ b/sandbox/linux/suid/linux_util.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following is duplicated from base/linux_utils.h.
+// We shouldn't link against C++ code in a setuid binary.
+
+#ifndef SANDBOX_LINUX_SUID_LINUX_UTIL_H_
+#define SANDBOX_LINUX_SUID_LINUX_UTIL_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+static const char kFindInodeSwitch[] = "--find-inode";
+static const char kSuidSandboxGetApiSwitch[] = "--get-api";
+
+// Find the process which holds the given socket, named by inode number. If
+// multiple processes hold the socket, this function returns false.
+bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode);
+
+#endif  // SANDBOX_LINUX_SUID_LINUX_UTIL_H_
diff --git a/sandbox/linux/suid/process_util.h b/sandbox/linux/suid/process_util.h
new file mode 100644
index 0000000..f6b4c31
--- /dev/null
+++ b/sandbox/linux/suid/process_util.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following is duplicated from base/process_utils.h.
+// We shouldn't link against C++ code in a setuid binary.
+
+#ifndef SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
+#define SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "base/base_export.h"
+
+// This adjusts /proc/process/oom_score_adj so the Linux OOM killer
+// will prefer certain process types over others. The range for the
+// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
+//
+// If the Linux system isn't new enough to use oom_score_adj, then we
+// try to set the older oom_adj value instead, scaling the score to
+// the required range of [0, 15]. This may result in some aliasing of
+// values, of course.
+BASE_EXPORT bool AdjustOOMScore(pid_t process, int score);
+
+// This adjusts /sys/kernel/mm/chromeos-low_mem/margin so that
+// the kernel notifies us that we are low on memory when less than
+// |margin_mb| megabytes are available.  Setting |margin_mb| to -1
+// turns off low memory notification.
+BASE_EXPORT bool AdjustLowMemoryMargin(int64_t margin_mb);
+
+#endif  // SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
diff --git a/sandbox/linux/suid/process_util_linux.c b/sandbox/linux/suid/process_util_linux.c
new file mode 100644
index 0000000..5e6b33b
--- /dev/null
+++ b/sandbox/linux/suid/process_util_linux.c
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following is the C version of code from base/process_utils_linux.cc.
+// We shouldn't link against C++ code in a setuid binary.
+
+#define _GNU_SOURCE  // needed for O_DIRECTORY
+
+#include "process_util.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Ranges for the current (oom_score_adj) and previous (oom_adj)
+// flavors of OOM score.
+static const int kMaxOomScore = 1000;
+static const int kMaxOldOomScore = 15;
+
+// Kernel pseudo-file that allows setting of the low memory margin.
+static const char kLowMemMarginFile[] =
+    "/sys/kernel/mm/chromeos-low_mem/margin";
+
+// NOTE: This is not the only version of this function in the source:
+// the base library (in process_util_linux.cc) also has its own C++ version.
+bool AdjustOOMScore(pid_t process, int score) {
+  if (score < 0 || score > kMaxOomScore)
+    return false;
+
+  char oom_adj[27];  // "/proc/" + log_10(2**64) + "\0"
+                     //    6     +       20     +     1         = 27
+  snprintf(oom_adj, sizeof(oom_adj), "/proc/%" PRIdMAX, (intmax_t)process);
+
+  const int dirfd = open(oom_adj, O_RDONLY | O_DIRECTORY);
+  if (dirfd < 0)
+    return false;
+
+  struct stat statbuf;
+  if (fstat(dirfd, &statbuf) < 0) {
+    close(dirfd);
+    return false;
+  }
+  if (getuid() != statbuf.st_uid) {
+    close(dirfd);
+    return false;
+  }
+
+  int fd = openat(dirfd, "oom_score_adj", O_WRONLY);
+  if (fd < 0) {
+    // We failed to open oom_score_adj, so let's try for the older
+    // oom_adj file instead.
+    fd = openat(dirfd, "oom_adj", O_WRONLY);
+    if (fd < 0) {
+      // Nope, that doesn't work either.
+      return false;
+    } else {
+      // If we're using the old oom_adj file, the allowed range is now
+      // [0, kMaxOldOomScore], so we scale the score.  This may result in some
+      // aliasing of values, of course.
+      score = score * kMaxOldOomScore / kMaxOomScore;
+    }
+  }
+  close(dirfd);
+
+  char buf[11];  // 0 <= |score| <= kMaxOomScore; using log_10(2**32) + 1 size
+  snprintf(buf, sizeof(buf), "%d", score);
+  size_t len = strlen(buf);
+
+  ssize_t bytes_written = write(fd, buf, len);
+  close(fd);
+  return (bytes_written == len);
+}
+
+bool AdjustLowMemoryMargin(int64_t margin_mb) {
+  int file_descriptor = open(kLowMemMarginFile, O_WRONLY);
+  if (file_descriptor < 0)
+    return false;
+
+  // Only allow those values which are reasonable, to prevent mischief.
+  char value[21];
+  switch (margin_mb) {
+    case -1L:
+      snprintf(value, sizeof(value), "off");
+      break;
+    case 0L:
+    case 25L:
+    case 50L:
+    case 100L:
+    case 200L:
+      snprintf(value, sizeof(value), "%lld", (long long int)margin_mb);
+      break;
+    default:
+      return false;
+  }
+
+  bool success = (write(file_descriptor, value, strlen(value)) >= 0);
+  close(file_descriptor);
+  return success;
+}
diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c
new file mode 100644
index 0000000..32435a7
--- /dev/null
+++ b/sandbox/linux/suid/sandbox.c
@@ -0,0 +1,482 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
+
+#include "common/sandbox.h"
+
+#define _GNU_SOURCE
+#include <asm/unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "linux_util.h"
+#include "process_util.h"
+#include "common/suid_unsafe_environment_variables.h"
+
+#if !defined(CLONE_NEWPID)
+#define CLONE_NEWPID 0x20000000
+#endif
+#if !defined(CLONE_NEWNET)
+#define CLONE_NEWNET 0x40000000
+#endif
+
+static bool DropRoot();
+
+#define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x)
+
+static void FatalError(const char *msg, ...)
+    __attribute__((noreturn, format(printf, 1, 2)));
+
+static void FatalError(const char *msg, ...) {
+  va_list ap;
+  va_start(ap, msg);
+
+  vfprintf(stderr, msg, ap);
+  fprintf(stderr, ": %s\n", strerror(errno));
+  fflush(stderr);
+  va_end(ap);
+  _exit(1);
+}
+
+// We will chroot() to the helper's /proc/self directory. Anything there will
+// not exist anymore if we make sure to wait() for the helper.
+//
+// /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty
+// even if the helper survives as a zombie.
+//
+// There is very little reason to use fdinfo/ instead of fd/ but we are
+// paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/
+#define SAFE_DIR "/proc/self/fdinfo"
+#define SAFE_DIR2 "/proc/self/fd"
+
+static bool SpawnChrootHelper() {
+  int sv[2];
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+    perror("socketpair");
+    return false;
+  }
+
+  char *safedir = NULL;
+  struct stat sdir_stat;
+  if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
+    safedir = SAFE_DIR;
+  else
+    if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode))
+      safedir = SAFE_DIR2;
+    else {
+      fprintf(stderr, "Could not find %s\n", SAFE_DIR2);
+      return false;
+    }
+
+  const pid_t pid = syscall(
+      __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
+
+  if (pid == -1) {
+    perror("clone");
+    close(sv[0]);
+    close(sv[1]);
+    return false;
+  }
+
+  if (pid == 0) {
+    // We share our files structure with an untrusted process. As a security in
+    // depth measure, we make sure that we can't open anything by mistake.
+    // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT
+
+    const struct rlimit nofile = {0, 0};
+    if (setrlimit(RLIMIT_NOFILE, &nofile))
+      FatalError("Setting RLIMIT_NOFILE");
+
+    if (close(sv[1]))
+      FatalError("close");
+
+    // wait for message
+    char msg;
+    ssize_t bytes;
+    do {
+      bytes = read(sv[0], &msg, 1);
+    } while (bytes == -1 && errno == EINTR);
+
+    if (bytes == 0)
+      _exit(0);
+    if (bytes != 1)
+      FatalError("read");
+
+    // do chrooting
+    if (msg != kMsgChrootMe)
+      FatalError("Unknown message from sandboxed process");
+
+    // sanity check
+    if (chdir(safedir))
+      FatalError("Cannot chdir into /proc/ directory");
+
+    if (chroot(safedir))
+      FatalError("Cannot chroot into /proc/ directory");
+
+    if (chdir("/"))
+      FatalError("Cannot chdir to / after chroot");
+
+    const char reply = kMsgChrootSuccessful;
+    do {
+      bytes = write(sv[0], &reply, 1);
+    } while (bytes == -1 && errno == EINTR);
+
+    if (bytes != 1)
+      FatalError("Writing reply");
+
+    _exit(0);
+    // We now become a zombie. /proc/self/fd(info) is now an empty dir and we
+    // are chrooted there.
+    // Our (unprivileged) parent should not even be able to open "." or "/"
+    // since they would need to pass the ptrace() check. If our parent wait()
+    // for us, our root directory will completely disappear.
+  }
+
+  if (close(sv[0])) {
+    close(sv[1]);
+    perror("close");
+    return false;
+  }
+
+  // In the parent process, we install an environment variable containing the
+  // number of the file descriptor.
+  char desc_str[64];
+  int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]);
+  if (printed < 0 || printed >= (int)sizeof(desc_str)) {
+    fprintf(stderr, "Failed to snprintf\n");
+    return false;
+  }
+
+  if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
+    perror("setenv");
+    close(sv[1]);
+    return false;
+  }
+
+  // We also install an environment variable containing the pid of the child
+  char helper_pid_str[64];
+  printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid);
+  if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) {
+    fprintf(stderr, "Failed to snprintf\n");
+    return false;
+  }
+
+  if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) {
+    perror("setenv");
+    close(sv[1]);
+    return false;
+  }
+
+  return true;
+}
+
+// Block until child_pid exits, then exit. Try to preserve the exit code.
+static void WaitForChildAndExit(pid_t child_pid) {
+  int exit_code = -1;
+  siginfo_t reaped_child_info;
+
+  int wait_ret =
+    HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED));
+
+  if (!wait_ret && reaped_child_info.si_pid == child_pid) {
+    if (reaped_child_info.si_code == CLD_EXITED) {
+      exit_code = reaped_child_info.si_status;
+    } else {
+      // Exit with code 0 if the child got signaled.
+      exit_code = 0;
+    }
+  }
+  _exit(exit_code);
+}
+
+static bool MoveToNewNamespaces() {
+  // These are the sets of flags which we'll try, in order.
+  const int kCloneExtraFlags[] = {
+    CLONE_NEWPID | CLONE_NEWNET,
+    CLONE_NEWPID,
+  };
+
+  // We need to close kZygoteIdFd before the child can continue. We use this
+  // socketpair to tell the child when to continue;
+  int sync_fds[2];
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
+    FatalError("Failed to create a socketpair");
+  }
+
+  for (size_t i = 0;
+       i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
+       i++) {
+    pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
+
+    if (pid > 0) {
+      if (!DropRoot()) {
+        FatalError("Could not drop privileges");
+      } else {
+        if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD))
+          FatalError("Could not close socketpair");
+        // The kZygoteIdFd needs to be closed in the parent before
+        // Zygote gets started.
+        if (close(kZygoteIdFd))
+          FatalError("close");
+        // Tell our child to continue
+        if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1)
+          FatalError("send");
+        if (close(sync_fds[1]))
+          FatalError("close");
+        // We want to keep a full process tree and we don't want our childs to
+        // be reparented to (the outer PID namespace) init. So we wait for it.
+        WaitForChildAndExit(pid);
+      }
+      // NOTREACHED
+      FatalError("Not reached");
+    }
+
+    if (pid == 0) {
+      if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR))
+        FatalError("Could not close socketpair");
+
+      // Wait for the parent to confirm it closed kZygoteIdFd before we
+      // continue
+      char should_continue;
+      if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1)
+        FatalError("Read on socketpair");
+      if (close(sync_fds[0]))
+        FatalError("close");
+
+      if (kCloneExtraFlags[i] & CLONE_NEWPID) {
+        setenv(kSandboxPIDNSEnvironmentVarName, "", 1 /* overwrite */);
+      } else {
+        unsetenv(kSandboxPIDNSEnvironmentVarName);
+      }
+
+      if (kCloneExtraFlags[i] & CLONE_NEWNET) {
+        setenv(kSandboxNETNSEnvironmentVarName, "", 1 /* overwrite */);
+      } else {
+        unsetenv(kSandboxNETNSEnvironmentVarName);
+      }
+
+      break;
+    }
+
+    if (errno != EINVAL) {
+      perror("Failed to move to new PID namespace");
+      return false;
+    }
+  }
+
+  // If the system doesn't support NEWPID then we carry on anyway.
+  return true;
+}
+
+static bool DropRoot() {
+  if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
+    perror("prctl(PR_SET_DUMPABLE)");
+    return false;
+  }
+
+  if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+    perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
+    return false;
+  }
+
+  gid_t rgid, egid, sgid;
+  if (getresgid(&rgid, &egid, &sgid)) {
+    perror("getresgid");
+    return false;
+  }
+
+  if (setresgid(rgid, rgid, rgid)) {
+    perror("setresgid");
+    return false;
+  }
+
+  uid_t ruid, euid, suid;
+  if (getresuid(&ruid, &euid, &suid)) {
+    perror("getresuid");
+    return false;
+  }
+
+  if (setresuid(ruid, ruid, ruid)) {
+    perror("setresuid");
+    return false;
+  }
+
+  return true;
+}
+
+static bool SetupChildEnvironment() {
+  unsigned i;
+
+  // ld.so may have cleared several environment variables because we are SUID.
+  // However, the child process might need them so zygote_host_linux.cc saves a
+  // copy in SANDBOX_$x. This is safe because we have dropped root by this
+  // point, so we can only exec a binary with the permissions of the user who
+  // ran us in the first place.
+
+  for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
+    const char* const envvar = kSUIDUnsafeEnvironmentVariables[i];
+    char* const saved_envvar = SandboxSavedEnvironmentVariable(envvar);
+    if (!saved_envvar)
+      return false;
+
+    const char* const value = getenv(saved_envvar);
+    if (value) {
+      setenv(envvar, value, 1 /* overwrite */);
+      unsetenv(saved_envvar);
+    }
+
+    free(saved_envvar);
+  }
+
+  return true;
+}
+
+bool CheckAndExportApiVersion() {
+  // Check the environment to see if a specific API version was requested.
+  // assume version 0 if none.
+  long api_number = -1;
+  char *api_string = getenv(kSandboxEnvironmentApiRequest);
+  if (!api_string) {
+    api_number = 0;
+  } else {
+    errno = 0;
+    char* endptr = NULL;
+    api_number = strtol(api_string, &endptr, 10);
+    if (!endptr || *endptr || errno != 0)
+      return false;
+  }
+
+  // Warn only for now.
+  if (api_number != kSUIDSandboxApiNumber) {
+    fprintf(stderr, "The setuid sandbox provides API version %ld, "
+      "but you need %ld\n"
+      "Please read "
+      "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment."
+      "\n\n",
+      kSUIDSandboxApiNumber,
+      api_number);
+  }
+
+  // Export our version so that the sandboxed process can verify it did not
+  // use an old sandbox.
+  char version_string[64];
+  snprintf(version_string, sizeof(version_string), "%ld",
+           kSUIDSandboxApiNumber);
+  if (setenv(kSandboxEnvironmentApiProvides, version_string, 1)) {
+    perror("setenv");
+    return false;
+  }
+
+  return true;
+}
+
+int main(int argc, char **argv) {
+  if (argc <= 1) {
+    if (argc <= 0) {
+      return 1;
+    }
+
+    fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
+    return 1;
+  }
+
+  // Allow someone to query our API version
+  if (argc == 2 && 0 == strcmp(argv[1], kSuidSandboxGetApiSwitch)) {
+    printf("%ld\n", kSUIDSandboxApiNumber);
+    return 0;
+  }
+
+  // In the SUID sandbox, if we succeed in calling MoveToNewNamespaces()
+  // below, then the zygote and all the renderers are in an alternate PID
+  // namespace and do not know their real PIDs. As such, they report the wrong
+  // PIDs to the task manager.
+  //
+  // To fix this, when the zygote spawns a new renderer, it gives the renderer
+  // a dummy socket, which has a unique inode number. Then it asks the sandbox
+  // host to find the PID of the process holding that fd by searching /proc.
+  //
+  // Since the zygote and renderers are all spawned by this setuid executable,
+  // their entries in /proc are owned by root and only readable by root. In
+  // order to search /proc for the fd we want, this setuid executable has to
+  // double as a helper and perform the search. The code block below does this
+  // when you call it with --find-inode INODE_NUMBER.
+  if (argc == 3 && (0 == strcmp(argv[1], kFindInodeSwitch))) {
+    pid_t pid;
+    char* endptr = NULL;
+    errno = 0;
+    ino_t inode = strtoull(argv[2], &endptr, 10);
+    if (inode == ULLONG_MAX || !endptr || *endptr || errno != 0)
+      return 1;
+    if (!FindProcessHoldingSocket(&pid, inode))
+      return 1;
+    printf("%d\n", pid);
+    return 0;
+  }
+  // Likewise, we cannot adjust /proc/pid/oom_adj for sandboxed renderers
+  // because those files are owned by root. So we need another helper here.
+  if (argc == 4 && (0 == strcmp(argv[1], kAdjustOOMScoreSwitch))) {
+    char* endptr = NULL;
+    long score;
+    errno = 0;
+    unsigned long pid_ul = strtoul(argv[2], &endptr, 10);
+    if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
+      return 1;
+    pid_t pid = pid_ul;
+    endptr = NULL;
+    errno = 0;
+    score = strtol(argv[3], &endptr, 10);
+    if (score == LONG_MAX || score == LONG_MIN ||
+        !endptr || *endptr || errno != 0)
+      return 1;
+    return AdjustOOMScore(pid, score);
+  }
+#if defined(OS_CHROMEOS)
+  if (argc == 3 && (0 == strcmp(argv[1], kAdjustLowMemMarginSwitch))) {
+    char* endptr = NULL;
+    errno = 0;
+    unsigned long margin_mb = strtoul(argv[2], &endptr, 10);
+    if (!endptr || *endptr || errno != 0)
+      return 1;
+    return AdjustLowMemoryMargin(margin_mb);
+  }
+#endif
+
+  // Protect the core setuid sandbox functionality with an API version
+  if (!CheckAndExportApiVersion()) {
+    return 1;
+  }
+
+  if (!MoveToNewNamespaces())
+    return 1;
+  if (!SpawnChrootHelper())
+    return 1;
+  if (!DropRoot())
+    return 1;
+  if (!SetupChildEnvironment())
+    return 1;
+
+  execv(argv[1], &argv[1]);
+  FatalError("execv failed");
+
+  return 1;
+}