summaryrefslogtreecommitdiff
path: root/chromium/content/zygote
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/content/zygote
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-c30a6232df03e1efbd9f3b226777b07e087a1122.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/content/zygote')
-rw-r--r--chromium/content/zygote/BUILD.gn33
-rw-r--r--chromium/content/zygote/DEPS5
-rw-r--r--chromium/content/zygote/OWNERS5
-rw-r--r--chromium/content/zygote/zygote_browsertest.cc107
-rw-r--r--chromium/content/zygote/zygote_linux.cc660
-rw-r--r--chromium/content/zygote/zygote_linux.h152
-rw-r--r--chromium/content/zygote/zygote_main.h24
-rw-r--r--chromium/content/zygote/zygote_main_linux.cc247
8 files changed, 1122 insertions, 111 deletions
diff --git a/chromium/content/zygote/BUILD.gn b/chromium/content/zygote/BUILD.gn
new file mode 100644
index 00000000000..1673fc1378e
--- /dev/null
+++ b/chromium/content/zygote/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2018 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.
+
+import("//build/config/nacl/config.gni")
+import("//content/public/common/zygote/features.gni")
+
+if (is_linux) {
+ source_set("zygote") {
+ sources = [
+ "zygote_linux.cc",
+ "zygote_linux.h",
+ "zygote_main.h",
+ "zygote_main_linux.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//base:i18n",
+ "//content/public/common:common_sources",
+ "//ipc",
+ "//sandbox",
+ "//services/service_manager/embedder",
+ "//services/service_manager/sandbox",
+ "//third_party/icu",
+ ]
+
+ configs += [ "//content:content_implementation" ]
+ }
+} else {
+ group("zygote") {
+ }
+}
diff --git a/chromium/content/zygote/DEPS b/chromium/content/zygote/DEPS
index 9af3bd99586..cbd32378dba 100644
--- a/chromium/content/zygote/DEPS
+++ b/chromium/content/zygote/DEPS
@@ -1,3 +1,4 @@
include_rules = [
- "+services/service_manager/sandbox",
-] \ No newline at end of file
+ "+content/public/common/zygote",
+ "+services/service_manager/sandbox",
+]
diff --git a/chromium/content/zygote/OWNERS b/chromium/content/zygote/OWNERS
index 696f688510e..aad33ebfd00 100644
--- a/chromium/content/zygote/OWNERS
+++ b/chromium/content/zygote/OWNERS
@@ -1,3 +1,4 @@
-file://services/service_manager/zygote/OWNERS
-# COMPONENT: Internals>Sandbox
+file://sandbox/linux/OWNERS
+
# TEAM: security-dev@chromium.org
+# COMPONENT: Internals>Sandbox
diff --git a/chromium/content/zygote/zygote_browsertest.cc b/chromium/content/zygote/zygote_browsertest.cc
deleted file mode 100644
index 4f0de6ce3a4..00000000000
--- a/chromium/content/zygote/zygote_browsertest.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2016 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 <string>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/strings/string_split.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "content/shell/browser/shell.h"
-#include "services/service_manager/embedder/switches.h"
-#include "services/service_manager/sandbox/linux/sandbox_linux.h"
-#include "services/service_manager/sandbox/switches.h"
-#include "services/service_manager/zygote/common/zygote_buildflags.h"
-#if BUILDFLAG(USE_ZYGOTE_HANDLE)
-#include "services/service_manager/zygote/common/zygote_handle.h"
-#include "services/service_manager/zygote/host/zygote_communication_linux.h"
-#include "services/service_manager/zygote/host/zygote_host_impl_linux.h"
-#endif
-
-namespace content {
-
-class LinuxZygoteBrowserTest : public ContentBrowserTest {
- public:
- LinuxZygoteBrowserTest() = default;
- ~LinuxZygoteBrowserTest() override = default;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LinuxZygoteBrowserTest);
-};
-
-// https://crbug.com/638303
-IN_PROC_BROWSER_TEST_F(LinuxZygoteBrowserTest, GetLocalTimeHasTimeZone) {
- const char kTestCommand[] =
- "window.domAutomationController.send(new Date().toString());";
-
- EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,start page")));
- std::string result;
- ASSERT_TRUE(ExecuteScriptAndExtractString(shell(), kTestCommand, &result));
- std::vector<std::string> parts = base::SplitString(
- result, "()", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
- ASSERT_EQ(3U, parts.size());
- EXPECT_FALSE(parts[0].empty());
- EXPECT_FALSE(parts[1].empty());
- EXPECT_TRUE(parts[2].empty());
-}
-
-#if BUILDFLAG(USE_ZYGOTE_HANDLE)
-IN_PROC_BROWSER_TEST_F(LinuxZygoteBrowserTest, ZygoteSandboxes) {
- // We need zygotes and the standard sandbox config to run this test.
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote) ||
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- service_manager::switches::kNoSandbox)) {
- return;
- }
-
- // Sanity check the sandbox flags we expect to be everywhere.
- const int flags = service_manager::GetGenericZygote()->GetSandboxStatus();
- constexpr int kExpectedFlags = service_manager::SandboxLinux::kPIDNS |
- service_manager::SandboxLinux::kNetNS |
- service_manager::SandboxLinux::kUserNS;
- EXPECT_EQ(kExpectedFlags, flags & kExpectedFlags);
-
- EXPECT_EQ(service_manager::GetUnsandboxedZygote()->GetSandboxStatus(), 0);
-}
-#endif
-
-class LinuxZygoteDisabledBrowserTest : public ContentBrowserTest {
- public:
- LinuxZygoteDisabledBrowserTest() = default;
- ~LinuxZygoteDisabledBrowserTest() override = default;
-
- protected:
- void SetUpCommandLine(base::CommandLine* command_line) override {
- ContentBrowserTest::SetUpCommandLine(command_line);
- command_line->AppendSwitch(switches::kNoZygote);
- command_line->AppendSwitch(service_manager::switches::kNoSandbox);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LinuxZygoteDisabledBrowserTest);
-};
-
-// https://crbug.com/712779
-#if !defined(THREAD_SANITIZER)
-// Test that the renderer doesn't crash during launch if zygote is disabled.
-IN_PROC_BROWSER_TEST_F(LinuxZygoteDisabledBrowserTest,
- NoCrashWhenZygoteDisabled) {
- EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,start page")));
-}
-#endif
-
-#if BUILDFLAG(USE_ZYGOTE_HANDLE)
-IN_PROC_BROWSER_TEST_F(LinuxZygoteDisabledBrowserTest,
- NoZygoteWhenZygoteDisabled) {
- EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,start page")));
-
- EXPECT_FALSE(service_manager::ZygoteHostImpl::GetInstance()->HasZygote());
-}
-#endif
-
-} // namespace content
diff --git a/chromium/content/zygote/zygote_linux.cc b/chromium/content/zygote/zygote_linux.cc
new file mode 100644
index 00000000000..48ab8fce3e7
--- /dev/null
+++ b/chromium/content/zygote/zygote_linux.cc
@@ -0,0 +1,660 @@
+// 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 "content/zygote/zygote_linux.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/linux_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/global_descriptors.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "content/common/zygote/zygote_commands_linux.h"
+#include "content/public/common/zygote/send_zygote_child_ping_linux.h"
+#include "content/public/common/zygote/zygote_fork_delegate_linux.h"
+#include "ipc/ipc_channel.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_sandbox.h"
+#include "services/service_manager/embedder/descriptors.h"
+#include "services/service_manager/embedder/result_codes.h"
+#include "services/service_manager/embedder/set_process_title.h"
+#include "services/service_manager/embedder/switches.h"
+#include "services/service_manager/sandbox/linux/sandbox_linux.h"
+#include "services/service_manager/sandbox/sandbox.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+
+// See
+// https://chromium.googlesource.com/chromium/src/+/master/docs/linux/zygote.md
+
+namespace content {
+
+namespace {
+
+// NOP function. See below where this handler is installed.
+void SIGCHLDHandler(int signal) {}
+
+int LookUpFd(const base::GlobalDescriptors::Mapping& fd_mapping, uint32_t key) {
+ for (size_t index = 0; index < fd_mapping.size(); ++index) {
+ if (fd_mapping[index].key == key)
+ return fd_mapping[index].fd;
+ }
+ return -1;
+}
+
+void KillAndReap(pid_t pid, ZygoteForkDelegate* helper) {
+ if (helper) {
+ // Helper children may be forked in another PID namespace, so |pid| might
+ // be meaningless to us; or we just might not be able to directly send it
+ // signals. So we can't kill it.
+ // Additionally, we're not its parent, so we can't reap it anyway.
+ // TODO(mdempsky): Extend the ZygoteForkDelegate API to handle this.
+ LOG(WARNING) << "Unable to kill or reap helper children";
+ return;
+ }
+
+ // Kill the child process in case it's not already dead, so we can safely
+ // perform a blocking wait.
+ PCHECK(0 == kill(pid, SIGKILL));
+ PCHECK(pid == HANDLE_EINTR(waitpid(pid, nullptr, 0)));
+}
+
+} // namespace
+
+Zygote::Zygote(int sandbox_flags,
+ std::vector<std::unique_ptr<ZygoteForkDelegate>> helpers,
+ const base::GlobalDescriptors::Descriptor& ipc_backchannel)
+ : sandbox_flags_(sandbox_flags),
+ helpers_(std::move(helpers)),
+ initial_uma_index_(0),
+ to_reap_(),
+ ipc_backchannel_(ipc_backchannel) {}
+
+Zygote::~Zygote() {}
+
+bool Zygote::ProcessRequests() {
+ // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
+ // browser on it.
+ // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel.
+ // See
+ // https://chromium.googlesource.com/chromium/src/+/master/docs/linux/sandbox_ipc.md
+
+ // We need to accept SIGCHLD, even though our handler is a no-op because
+ // otherwise we cannot wait on children. (According to POSIX 2001.)
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = &SIGCHLDHandler;
+ PCHECK(sigaction(SIGCHLD, &action, nullptr) == 0);
+
+ // Block SIGCHLD until a child might be ready to reap.
+ sigset_t sigset;
+ sigset_t orig_sigmask;
+ PCHECK(sigemptyset(&sigset) == 0);
+ PCHECK(sigaddset(&sigset, SIGCHLD) == 0);
+ PCHECK(sigprocmask(SIG_BLOCK, &sigset, &orig_sigmask) == 0);
+
+ if (UsingSUIDSandbox() || UsingNSSandbox()) {
+ // Let the ZygoteHost know we are ready to go.
+ // The receiving code is in
+ // content/browser/zygote_host/zygote_host_impl_linux.cc.
+ bool r = base::UnixDomainSocket::SendMsg(
+ kZygoteSocketPairFd, kZygoteHelloMessage, sizeof(kZygoteHelloMessage),
+ std::vector<int>());
+#if defined(OS_CHROMEOS)
+ LOG_IF(WARNING, !r) << "Sending zygote magic failed";
+ // Exit normally on chromeos because session manager may send SIGTERM
+ // right after the process starts and it may fail to send zygote magic
+ // number to browser process.
+ if (!r)
+ _exit(service_manager::RESULT_CODE_NORMAL_EXIT);
+#else
+ CHECK(r) << "Sending zygote magic failed";
+#endif
+ }
+
+ sigset_t ppoll_sigmask = orig_sigmask;
+ PCHECK(sigdelset(&ppoll_sigmask, SIGCHLD) == 0);
+ struct pollfd pfd;
+ pfd.fd = kZygoteSocketPairFd;
+ pfd.events = POLLIN;
+
+ struct timespec timeout;
+ timeout.tv_sec = 2;
+ timeout.tv_nsec = 0;
+
+ for (;;) {
+ struct timespec* timeout_ptr = nullptr;
+ if (!to_reap_.empty())
+ timeout_ptr = &timeout;
+ int rc = ppoll(&pfd, 1, timeout_ptr, &ppoll_sigmask);
+ PCHECK(rc >= 0 || errno == EINTR);
+ ReapChildren();
+
+ if (pfd.revents & POLLIN) {
+ // This function call can return multiple times, once per fork().
+ if (HandleRequestFromBrowser(kZygoteSocketPairFd)) {
+ PCHECK(sigprocmask(SIG_SETMASK, &orig_sigmask, nullptr) == 0);
+ return true;
+ }
+ }
+ }
+ // The loop should not be exited unless a request was successfully processed.
+ NOTREACHED();
+ return false;
+}
+
+bool Zygote::ReapChild(const base::TimeTicks& now, ZygoteProcessInfo* child) {
+ pid_t pid = child->internal_pid;
+ pid_t r = HANDLE_EINTR(waitpid(pid, nullptr, WNOHANG));
+ if (r > 0) {
+ if (r != pid) {
+ DLOG(ERROR) << "While waiting for " << pid
+ << " to terminate, "
+ "waitpid returned "
+ << r;
+ }
+ return r == pid;
+ }
+ if ((now - child->time_of_reap_request).InSeconds() < 2) {
+ return false;
+ }
+ // If the process has been requested reaped >= 2 seconds ago, kill it.
+ if (!child->sent_sigkill) {
+ if (kill(pid, SIGKILL) != 0)
+ DPLOG(ERROR) << "Sending SIGKILL to process " << pid << " failed";
+
+ child->sent_sigkill = true;
+ }
+ return false;
+}
+
+void Zygote::ReapChildren() {
+ base::TimeTicks now = base::TimeTicks::Now();
+ std::vector<ZygoteProcessInfo>::iterator it = to_reap_.begin();
+ while (it != to_reap_.end()) {
+ if (ReapChild(now, &(*it))) {
+ it = to_reap_.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+bool Zygote::GetProcessInfo(base::ProcessHandle pid,
+ ZygoteProcessInfo* process_info) {
+ DCHECK(process_info);
+ const ZygoteProcessMap::const_iterator it = process_info_map_.find(pid);
+ if (it == process_info_map_.end()) {
+ return false;
+ }
+ *process_info = it->second;
+ return true;
+}
+
+bool Zygote::UsingSUIDSandbox() const {
+ return sandbox_flags_ & service_manager::SandboxLinux::kSUID;
+}
+
+bool Zygote::UsingNSSandbox() const {
+ return sandbox_flags_ & service_manager::SandboxLinux::kUserNS;
+}
+
+bool Zygote::HandleRequestFromBrowser(int fd) {
+ std::vector<base::ScopedFD> fds;
+ char buf[kZygoteMaxMessageLength];
+ const ssize_t len =
+ base::UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
+
+ if (len == 0 || (len == -1 && errno == ECONNRESET)) {
+ // EOF from the browser. We should die.
+ // TODO(eugenis): call __sanititizer_cov_dump() here to obtain code
+ // coverage for the Zygote. Currently it's not possible because of
+ // confusion over who is responsible for closing the file descriptor.
+ _exit(0);
+ return false;
+ }
+
+ if (len == -1) {
+ PLOG(ERROR) << "Error reading message from browser";
+ return false;
+ }
+
+ base::Pickle pickle(buf, len);
+ base::PickleIterator iter(pickle);
+
+ int kind;
+ if (iter.ReadInt(&kind)) {
+ switch (kind) {
+ case kZygoteCommandFork:
+ // This function call can return multiple times, once per fork().
+ return HandleForkRequest(fd, iter, std::move(fds));
+
+ case kZygoteCommandReap:
+ if (!fds.empty())
+ break;
+ HandleReapRequest(fd, iter);
+ return false;
+ case kZygoteCommandGetTerminationStatus:
+ if (!fds.empty())
+ break;
+ HandleGetTerminationStatus(fd, iter);
+ return false;
+ case kZygoteCommandGetSandboxStatus:
+ HandleGetSandboxStatus(fd, iter);
+ return false;
+ case kZygoteCommandForkRealPID:
+ // This shouldn't happen in practice, but some failure paths in
+ // HandleForkRequest (e.g., if ReadArgsAndFork fails during depickling)
+ // could leave this command pending on the socket.
+ LOG(ERROR) << "Unexpected real PID message from browser";
+ NOTREACHED();
+ return false;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ LOG(WARNING) << "Error parsing message from browser";
+ return false;
+}
+
+void Zygote::HandleReapRequest(int fd, base::PickleIterator iter) {
+ base::ProcessId child;
+
+ if (!iter.ReadInt(&child)) {
+ LOG(WARNING) << "Error parsing reap request from browser";
+ return;
+ }
+
+ ZygoteProcessInfo child_info;
+ if (!GetProcessInfo(child, &child_info)) {
+ LOG(ERROR) << "Child not found!";
+ NOTREACHED();
+ return;
+ }
+ child_info.time_of_reap_request = base::TimeTicks::Now();
+
+ if (!child_info.started_from_helper) {
+ to_reap_.push_back(child_info);
+ } else {
+ // For processes from the helper, send a GetTerminationStatus request
+ // with known_dead set to true.
+ // This is not perfect, as the process may be killed instantly, but is
+ // better than ignoring the request.
+ base::TerminationStatus status;
+ int exit_code;
+ bool got_termination_status =
+ GetTerminationStatus(child, true /* known_dead */, &status, &exit_code);
+ DCHECK(got_termination_status);
+ }
+ process_info_map_.erase(child);
+}
+
+bool Zygote::GetTerminationStatus(base::ProcessHandle real_pid,
+ bool known_dead,
+ base::TerminationStatus* status,
+ int* exit_code) {
+ ZygoteProcessInfo child_info;
+ if (!GetProcessInfo(real_pid, &child_info)) {
+ LOG(ERROR) << "Zygote::GetTerminationStatus for unknown PID " << real_pid;
+ NOTREACHED();
+ return false;
+ }
+ // We know about |real_pid|.
+ const base::ProcessHandle child = child_info.internal_pid;
+ if (child_info.started_from_helper) {
+ if (!child_info.started_from_helper->GetTerminationStatus(
+ child, known_dead, status, exit_code)) {
+ return false;
+ }
+ } else {
+ // Handle the request directly.
+ if (known_dead) {
+ *status = base::GetKnownDeadTerminationStatus(child, exit_code);
+ } else {
+ // We don't know if the process is dying, so get its status but don't
+ // wait.
+ *status = base::GetTerminationStatus(child, exit_code);
+ }
+ }
+ // Successfully got a status for |real_pid|.
+ if (*status != base::TERMINATION_STATUS_STILL_RUNNING) {
+ // Time to forget about this process.
+ process_info_map_.erase(real_pid);
+ }
+
+ if (WIFEXITED(*exit_code)) {
+ const int exit_status = WEXITSTATUS(*exit_code);
+ if (exit_status == sandbox::NamespaceSandbox::SignalExitCode(SIGINT) ||
+ exit_status == sandbox::NamespaceSandbox::SignalExitCode(SIGTERM)) {
+ *status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ }
+ }
+
+ return true;
+}
+
+void Zygote::HandleGetTerminationStatus(int fd, base::PickleIterator iter) {
+ bool known_dead;
+ base::ProcessHandle child_requested;
+
+ if (!iter.ReadBool(&known_dead) || !iter.ReadInt(&child_requested)) {
+ LOG(WARNING) << "Error parsing GetTerminationStatus request "
+ << "from browser";
+ return;
+ }
+
+ base::TerminationStatus status;
+ int exit_code;
+
+ bool got_termination_status =
+ GetTerminationStatus(child_requested, known_dead, &status, &exit_code);
+ if (!got_termination_status) {
+ // Assume that if we can't find the child in the sandbox, then
+ // it terminated normally.
+ NOTREACHED();
+ status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
+ exit_code = service_manager::RESULT_CODE_NORMAL_EXIT;
+ }
+
+ base::Pickle write_pickle;
+ write_pickle.WriteInt(static_cast<int>(status));
+ write_pickle.WriteInt(exit_code);
+ ssize_t written =
+ HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size()));
+ if (written != static_cast<ssize_t>(write_pickle.size()))
+ PLOG(ERROR) << "write";
+}
+
+int Zygote::ForkWithRealPid(const std::string& process_type,
+ const base::GlobalDescriptors::Mapping& fd_mapping,
+ const std::string& channel_id,
+ base::ScopedFD pid_oracle,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ ZygoteForkDelegate* helper = nullptr;
+ for (auto i = helpers_.begin(); i != helpers_.end(); ++i) {
+ if ((*i)->CanHelp(process_type, uma_name, uma_sample, uma_boundary_value)) {
+ helper = i->get();
+ break;
+ }
+ }
+
+ base::ScopedFD read_pipe, write_pipe;
+ base::ProcessId pid = 0;
+ if (helper) {
+ int mojo_channel_fd =
+ LookUpFd(fd_mapping, service_manager::kMojoIPCChannel);
+ if (mojo_channel_fd < 0) {
+ DLOG(ERROR) << "Failed to find kMojoIPCChannel in FD mapping";
+ return -1;
+ }
+ std::vector<int> fds;
+ fds.push_back(mojo_channel_fd); // kBrowserFDIndex
+ fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex
+ pid = helper->Fork(process_type, fds, channel_id);
+
+ // Helpers should never return in the child process.
+ CHECK_NE(pid, 0);
+ } else {
+ PCHECK(base::CreatePipe(&read_pipe, &write_pipe));
+ if (sandbox_flags_ & service_manager::SandboxLinux::kPIDNS &&
+ sandbox_flags_ & service_manager::SandboxLinux::kUserNS) {
+ pid = sandbox::NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ } else {
+ pid = sandbox::Credentials::ForkAndDropCapabilitiesInChild();
+ }
+ }
+
+ if (pid == 0) {
+ // In the child process.
+
+ // If the process is the init process inside a PID namespace, it must have
+ // explicit signal handlers.
+ if (getpid() == 1) {
+ static const int kTerminationSignals[] = {
+ SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGABRT, SIGPIPE, SIGUSR1, SIGUSR2};
+ for (const int sig : kTerminationSignals) {
+ sandbox::NamespaceSandbox::InstallTerminationSignalHandler(
+ sig, sandbox::NamespaceSandbox::SignalExitCode(sig));
+ }
+ }
+
+ write_pipe.reset();
+
+ // Ping the PID oracle socket so the browser can find our PID.
+ CHECK(SendZygoteChildPing(pid_oracle.get()));
+
+ // Now read back our real PID from the zygote.
+ base::ProcessId real_pid;
+ if (!base::ReadFromFD(read_pipe.get(), reinterpret_cast<char*>(&real_pid),
+ sizeof(real_pid))) {
+ LOG(FATAL) << "Failed to synchronise with parent zygote process";
+ }
+ if (real_pid <= 0) {
+ LOG(FATAL) << "Invalid pid from parent zygote";
+ }
+ // Sandboxed processes need to send the global, non-namespaced PID when
+ // setting up an IPC channel to their parent.
+ IPC::Channel::SetGlobalPid(real_pid);
+ // Force the real PID so chrome event data have a PID that corresponds
+ // to system trace event data.
+ base::trace_event::TraceLog::GetInstance()->SetProcessID(
+ static_cast<int>(real_pid));
+ base::InitUniqueIdForProcessInPidNamespace(real_pid);
+ return 0;
+ }
+
+ // In the parent process.
+ if (pid < 0) {
+ // Fork failed.
+ return -1;
+ }
+
+ read_pipe.reset();
+ pid_oracle.reset();
+
+ // Always receive a real PID from the zygote host, though it might
+ // be invalid (see below).
+ base::ProcessId real_pid = -1;
+ {
+ std::vector<base::ScopedFD> recv_fds;
+ char buf[kZygoteMaxMessageLength];
+ const ssize_t len = base::UnixDomainSocket::RecvMsg(
+ kZygoteSocketPairFd, buf, sizeof(buf), &recv_fds);
+
+ if (len > 0) {
+ CHECK(recv_fds.empty());
+
+ base::Pickle pickle(buf, len);
+ base::PickleIterator iter(pickle);
+
+ int kind;
+ CHECK(iter.ReadInt(&kind));
+ CHECK(kind == kZygoteCommandForkRealPID);
+ CHECK(iter.ReadInt(&real_pid));
+ }
+ }
+
+ // If we successfully forked a child, but it crashed without sending
+ // a message to the browser, the browser won't have found its PID.
+ if (real_pid < 0) {
+ KillAndReap(pid, helper);
+ return -1;
+ }
+
+ // If we're not using a helper, send the PID back to the child process.
+ if (!helper) {
+ ssize_t written =
+ HANDLE_EINTR(write(write_pipe.get(), &real_pid, sizeof(real_pid)));
+ if (written != sizeof(real_pid)) {
+ KillAndReap(pid, helper);
+ return -1;
+ }
+ }
+
+ // Now set-up this process to be tracked by the Zygote.
+ if (process_info_map_.find(real_pid) != process_info_map_.end()) {
+ LOG(ERROR) << "Already tracking PID " << real_pid;
+ NOTREACHED();
+ }
+ process_info_map_[real_pid].internal_pid = pid;
+ process_info_map_[real_pid].started_from_helper = helper;
+
+ return real_pid;
+}
+
+base::ProcessId Zygote::ReadArgsAndFork(base::PickleIterator iter,
+ std::vector<base::ScopedFD> fds,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ std::vector<std::string> args;
+ int argc = 0;
+ int numfds = 0;
+ base::GlobalDescriptors::Mapping mapping;
+ std::string process_type;
+ std::string channel_id;
+ const std::string channel_id_prefix =
+ std::string("--") +
+ service_manager::switches::kServiceRequestChannelToken + std::string("=");
+
+ if (!iter.ReadString(&process_type))
+ return -1;
+ if (!iter.ReadInt(&argc))
+ return -1;
+
+ for (int i = 0; i < argc; ++i) {
+ std::string arg;
+ if (!iter.ReadString(&arg))
+ return -1;
+ args.push_back(arg);
+ if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0)
+ channel_id = arg.substr(channel_id_prefix.length());
+ }
+
+ // timezone_id is obtained from ICU in zygote host so that it can't be
+ // invalid. For an unknown reason, if an invalid ID is passed down here, the
+ // worst result would be that timezone would be set to Etc/Unknown.
+ base::string16 timezone_id;
+ if (!iter.ReadString16(&timezone_id))
+ return -1;
+ icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
+ icu::UnicodeString(FALSE, timezone_id.data(), timezone_id.length())));
+
+ if (!iter.ReadInt(&numfds))
+ return -1;
+ if (numfds != static_cast<int>(fds.size()))
+ return -1;
+
+ // First FD is the PID oracle socket.
+ if (fds.size() < 1)
+ return -1;
+ base::ScopedFD pid_oracle(std::move(fds[0]));
+
+ // Remaining FDs are for the global descriptor mapping.
+ for (int i = 1; i < numfds; ++i) {
+ base::GlobalDescriptors::Key key;
+ if (!iter.ReadUInt32(&key))
+ return -1;
+ mapping.push_back(base::GlobalDescriptors::Descriptor(key, fds[i].get()));
+ }
+
+ mapping.push_back(ipc_backchannel_);
+
+ // Returns twice, once per process.
+ base::ProcessId child_pid =
+ ForkWithRealPid(process_type, mapping, channel_id, std::move(pid_oracle),
+ uma_name, uma_sample, uma_boundary_value);
+ if (!child_pid) {
+ // This is the child process.
+
+ // Our socket from the browser.
+ PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd)));
+
+ // Pass ownership of file descriptors from fds to GlobalDescriptors.
+ for (base::ScopedFD& fd : fds)
+ ignore_result(fd.release());
+ base::GlobalDescriptors::GetInstance()->Reset(mapping);
+
+ // Reset the process-wide command line to our new command line.
+ base::CommandLine::Reset();
+ base::CommandLine::Init(0, nullptr);
+ base::CommandLine::ForCurrentProcess()->InitFromArgv(args);
+
+ // Update the process title. The argv was already cached by the call to
+ // SetProcessTitleFromCommandLine in ChromeMain, so we can pass NULL here
+ // (we don't have the original argv at this point).
+ service_manager::SetProcessTitleFromCommandLine(nullptr);
+ } else if (child_pid < 0) {
+ LOG(ERROR) << "Zygote could not fork: process_type " << process_type
+ << " numfds " << numfds << " child_pid " << child_pid;
+ }
+ return child_pid;
+}
+
+bool Zygote::HandleForkRequest(int fd,
+ base::PickleIterator iter,
+ std::vector<base::ScopedFD> fds) {
+ std::string uma_name;
+ int uma_sample;
+ int uma_boundary_value;
+ base::ProcessId child_pid = ReadArgsAndFork(iter, std::move(fds), &uma_name,
+ &uma_sample, &uma_boundary_value);
+ if (child_pid == 0)
+ return true;
+ // If there's no UMA report for this particular fork, then check if any
+ // helpers have an initial UMA report for us to send instead.
+ while (uma_name.empty() && initial_uma_index_ < helpers_.size()) {
+ helpers_[initial_uma_index_++]->InitialUMA(&uma_name, &uma_sample,
+ &uma_boundary_value);
+ }
+ // Must always send reply, as ZygoteHost blocks while waiting for it.
+ base::Pickle reply_pickle;
+ reply_pickle.WriteInt(child_pid);
+ reply_pickle.WriteString(uma_name);
+ if (!uma_name.empty()) {
+ reply_pickle.WriteInt(uma_sample);
+ reply_pickle.WriteInt(uma_boundary_value);
+ }
+ if (HANDLE_EINTR(write(fd, reply_pickle.data(), reply_pickle.size())) !=
+ static_cast<ssize_t>(reply_pickle.size()))
+ PLOG(ERROR) << "write";
+ return false;
+}
+
+bool Zygote::HandleGetSandboxStatus(int fd, base::PickleIterator iter) {
+ if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) !=
+ sizeof(sandbox_flags_)) {
+ PLOG(ERROR) << "write";
+ }
+
+ return false;
+}
+
+} // namespace content
diff --git a/chromium/content/zygote/zygote_linux.h b/chromium/content/zygote/zygote_linux.h
new file mode 100644
index 00000000000..8e451fa6afe
--- /dev/null
+++ b/chromium/content/zygote/zygote_linux.h
@@ -0,0 +1,152 @@
+// 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 CONTENT_ZYGOTE_ZYGOTE_LINUX_H_
+#define CONTENT_ZYGOTE_ZYGOTE_LINUX_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/small_map.h"
+#include "base/files/scoped_file.h"
+#include "base/posix/global_descriptors.h"
+#include "base/process/kill.h"
+#include "base/process/process.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
+
+namespace base {
+class PickleIterator;
+}
+
+namespace content {
+
+class ZygoteForkDelegate;
+
+// This is the object which implements the zygote. The ZygoteMain function,
+// which is called from ChromeMain, simply constructs one of these objects and
+// runs it.
+class Zygote {
+ public:
+ Zygote(int sandbox_flags,
+ std::vector<std::unique_ptr<ZygoteForkDelegate>> helpers,
+ const base::GlobalDescriptors::Descriptor& ipc_backchannel);
+ ~Zygote();
+
+ bool ProcessRequests();
+
+ private:
+ struct ZygoteProcessInfo {
+ // Pid from inside the Zygote's PID namespace.
+ base::ProcessHandle internal_pid;
+ // Keeps track of which fork delegate helper the process was started from.
+ ZygoteForkDelegate* started_from_helper;
+ // Records when the browser requested the zygote to reap this process.
+ base::TimeTicks time_of_reap_request;
+ // Notes whether the zygote has sent SIGKILL to this process.
+ bool sent_sigkill;
+ };
+ using ZygoteProcessMap =
+ base::small_map<std::map<base::ProcessHandle, ZygoteProcessInfo>>;
+
+ // Retrieve a ZygoteProcessInfo from the process_info_map_.
+ // Returns true and write to process_info if |pid| can be found, return
+ // false otherwise.
+ bool GetProcessInfo(base::ProcessHandle pid, ZygoteProcessInfo* process_info);
+
+ // Returns true if the SUID sandbox is active.
+ bool UsingSUIDSandbox() const;
+ // Returns true if the NS sandbox is active.
+ bool UsingNSSandbox() const;
+
+ // ---------------------------------------------------------------------------
+ // Requests from the browser...
+
+ // Read and process a request from the browser. Returns true if we are in a
+ // new process and thus need to unwind back into ChromeMain.
+ bool HandleRequestFromBrowser(int fd);
+
+ void HandleReapRequest(int fd, base::PickleIterator iter);
+
+ // Get the termination status of |real_pid|. |real_pid| is the PID as it
+ // appears outside of the sandbox.
+ // Return true if it managed to get the termination status and return the
+ // status in |status| and the exit code in |exit_code|.
+ bool GetTerminationStatus(base::ProcessHandle real_pid,
+ bool known_dead,
+ base::TerminationStatus* status,
+ int* exit_code);
+
+ void HandleGetTerminationStatus(int fd, base::PickleIterator iter);
+
+ // This is equivalent to fork(), except that, when using the SUID sandbox, it
+ // returns the real PID of the child process as it appears outside the
+ // sandbox, rather than returning the PID inside the sandbox. The child's
+ // real PID is determined by having it call
+ // service_manager::SendZygoteChildPing(int) using the |pid_oracle|
+ // descriptor.
+ // Finally, when using a ZygoteForkDelegate helper, |uma_name|, |uma_sample|,
+ // and |uma_boundary_value| may be set if the helper wants to make a UMA
+ // report via UMA_HISTOGRAM_ENUMERATION.
+ int ForkWithRealPid(const std::string& process_type,
+ const base::GlobalDescriptors::Mapping& fd_mapping,
+ const std::string& channel_id,
+ base::ScopedFD pid_oracle,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value);
+
+ // Unpacks process type and arguments from |iter| and forks a new process.
+ // Returns -1 on error, otherwise returns twice, returning 0 to the child
+ // process and the child process ID to the parent process, like fork().
+ base::ProcessId ReadArgsAndFork(base::PickleIterator iter,
+ std::vector<base::ScopedFD> fds,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value);
+
+ // Handle a 'fork' request from the browser: this means that the browser
+ // wishes to start a new renderer. Returns true if we are in a new process,
+ // otherwise writes the child_pid back to the browser via |fd|. Writes a
+ // child_pid of -1 on error.
+ bool HandleForkRequest(int fd,
+ base::PickleIterator iter,
+ std::vector<base::ScopedFD> fds);
+
+ bool HandleGetSandboxStatus(int fd, base::PickleIterator iter);
+
+ // Attempt to reap the child process by calling waitpid, and return
+ // whether successful. If the process has not terminated within
+ // 2 seconds of its reap request, send it SIGKILL.
+ bool ReapChild(const base::TimeTicks& now, ZygoteProcessInfo* child);
+
+ // Attempt to reap all outstanding children in |to_reap_|.
+ void ReapChildren();
+
+ // The Zygote needs to keep some information about each process. Most
+ // notably what the PID of the process is inside the PID namespace of
+ // the Zygote and whether or not a process was started by the
+ // ZygoteForkDelegate helper.
+ ZygoteProcessMap process_info_map_;
+
+ const int sandbox_flags_;
+ std::vector<std::unique_ptr<ZygoteForkDelegate>> helpers_;
+
+ // Count of how many fork delegates for which we've invoked InitialUMA().
+ size_t initial_uma_index_;
+
+ // The vector contains the child processes that need to be reaped.
+ std::vector<ZygoteProcessInfo> to_reap_;
+
+ // Sandbox IPC channel for renderers to invoke services from the browser. See
+ // https://chromium.googlesource.com/chromium/src/+/master/docs/linux/sandbox_ipc.md
+ base::GlobalDescriptors::Descriptor ipc_backchannel_;
+};
+
+} // namespace content
+
+#endif // CONTENT_ZYGOTE_ZYGOTE_LINUX_H_
diff --git a/chromium/content/zygote/zygote_main.h b/chromium/content/zygote/zygote_main.h
new file mode 100644
index 00000000000..1959950a6c0
--- /dev/null
+++ b/chromium/content/zygote/zygote_main.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_ZYGOTE_ZYGOTE_MAIN_H_
+#define CONTENT_ZYGOTE_ZYGOTE_MAIN_H_
+
+#include <memory>
+#include <vector>
+
+#include "build/build_config.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class ZygoteForkDelegate;
+
+// |delegate| must outlive this call.
+CONTENT_EXPORT bool ZygoteMain(
+ std::vector<std::unique_ptr<ZygoteForkDelegate>> fork_delegates);
+
+} // namespace content
+
+#endif // CONTENT_ZYGOTE_ZYGOTE_MAIN_H_
diff --git a/chromium/content/zygote/zygote_main_linux.cc b/chromium/content/zygote/zygote_main_linux.cc
new file mode 100644
index 00000000000..b7dc390c499
--- /dev/null
+++ b/chromium/content/zygote/zygote_main_linux.cc
@@ -0,0 +1,247 @@
+// 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 "content/zygote/zygote_main.h"
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/rand_util.h"
+#include "base/strings/safe_sprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/system/sys_info.h"
+#include "build/build_config.h"
+#include "content/common/zygote/zygote_commands_linux.h"
+#include "content/public/common/zygote/sandbox_support_linux.h"
+#include "content/public/common/zygote/zygote_fork_delegate_linux.h"
+#include "content/zygote/zygote_linux.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/init_process_reaper.h"
+#include "sandbox/linux/services/libc_interceptor.h"
+#include "sandbox/linux/services/namespace_sandbox.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
+#include "services/service_manager/embedder/descriptors.h"
+#include "services/service_manager/embedder/switches.h"
+#include "services/service_manager/sandbox/linux/sandbox_debug_handling_linux.h"
+#include "services/service_manager/sandbox/linux/sandbox_linux.h"
+#include "services/service_manager/sandbox/sandbox.h"
+#include "services/service_manager/sandbox/switches.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+
+namespace content {
+
+namespace {
+
+void CloseFds(const std::vector<int>& fds) {
+ for (const auto& it : fds) {
+ PCHECK(0 == IGNORE_EINTR(close(it)));
+ }
+}
+
+base::OnceClosure ClosureFromTwoClosures(base::OnceClosure one,
+ base::OnceClosure two) {
+ return base::BindOnce(
+ [](base::OnceClosure one, base::OnceClosure two) {
+ if (!one.is_null())
+ std::move(one).Run();
+ if (!two.is_null())
+ std::move(two).Run();
+ },
+ std::move(one), std::move(two));
+}
+
+} // namespace
+
+// This function triggers the static and lazy construction of objects that need
+// to be created before imposing the sandbox.
+static void ZygotePreSandboxInit() {
+ base::RandUint64();
+
+ base::SysInfo::AmountOfPhysicalMemory();
+ base::SysInfo::NumberOfProcessors();
+
+ // ICU DateFormat class (used in base/time_format.cc) needs to get the
+ // Olson timezone ID by accessing the zoneinfo files on disk. After
+ // TimeZone::createDefault is called once here, the timezone ID is
+ // cached and there's no more need to access the file system.
+ std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
+}
+
+static bool CreateInitProcessReaper(
+ base::OnceClosure post_fork_parent_callback) {
+ // The current process becomes init(1), this function returns from a
+ // newly created process.
+ if (!sandbox::CreateInitProcessReaper(std::move(post_fork_parent_callback))) {
+ LOG(ERROR) << "Error creating an init process to reap zombies";
+ return false;
+ }
+ return true;
+}
+
+// Enter the setuid sandbox. This requires the current process to have been
+// created through the setuid sandbox.
+static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox,
+ base::OnceClosure post_fork_parent_callback) {
+ DCHECK(setuid_sandbox);
+ DCHECK(setuid_sandbox->IsSuidSandboxChild());
+
+ // Use the SUID sandbox. This still allows the seccomp sandbox to
+ // be enabled by the process later.
+
+ if (!setuid_sandbox->IsSuidSandboxUpToDate()) {
+ LOG(WARNING) << "You are using a wrong version of the setuid binary!\n"
+ "Please read "
+ "https://chromium.googlesource.com/chromium/src/+/master/"
+ "docs/linux/suid_sandbox_development.md."
+ "\n\n";
+ }
+
+ if (!setuid_sandbox->ChrootMe())
+ return false;
+
+ if (setuid_sandbox->IsInNewPIDNamespace()) {
+ CHECK_EQ(1, getpid())
+ << "The SUID sandbox created a new PID namespace but Zygote "
+ "is not the init process. Please, make sure the SUID "
+ "binary is up to date.";
+ }
+
+ if (getpid() == 1) {
+ // The setuid sandbox has created a new PID namespace and we need
+ // to assume the role of init.
+ CHECK(CreateInitProcessReaper(std::move(post_fork_parent_callback)));
+ }
+
+ CHECK(service_manager::SandboxDebugHandling::SetDumpableStatusAndHandlers());
+ return true;
+}
+
+static void DropAllCapabilities(int proc_fd) {
+ CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd));
+}
+
+static void EnterNamespaceSandbox(service_manager::SandboxLinux* linux_sandbox,
+ base::OnceClosure post_fork_parent_callback) {
+ linux_sandbox->EngageNamespaceSandbox(true /* from_zygote */);
+ if (getpid() == 1) {
+ CHECK(CreateInitProcessReaper(ClosureFromTwoClosures(
+ base::BindOnce(DropAllCapabilities, linux_sandbox->proc_fd()),
+ std::move(post_fork_parent_callback))));
+ }
+}
+
+static void EnterLayerOneSandbox(service_manager::SandboxLinux* linux_sandbox,
+ const bool using_layer1_sandbox,
+ base::OnceClosure post_fork_parent_callback) {
+ DCHECK(linux_sandbox);
+
+ ZygotePreSandboxInit();
+
+// Check that the pre-sandbox initialization didn't spawn threads.
+// It's not just our code which may do so - some system-installed libraries
+// are known to be culprits, e.g. lttng.
+#if !defined(THREAD_SANITIZER)
+ CHECK(sandbox::ThreadHelpers::IsSingleThreaded());
+#endif
+
+ sandbox::SetuidSandboxClient* setuid_sandbox =
+ linux_sandbox->setuid_sandbox_client();
+ if (setuid_sandbox->IsSuidSandboxChild()) {
+ CHECK(
+ EnterSuidSandbox(setuid_sandbox, std::move(post_fork_parent_callback)))
+ << "Failed to enter setuid sandbox";
+ } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) {
+ EnterNamespaceSandbox(linux_sandbox, std::move(post_fork_parent_callback));
+ } else {
+ CHECK(!using_layer1_sandbox);
+ }
+}
+
+bool ZygoteMain(
+ std::vector<std::unique_ptr<ZygoteForkDelegate>> fork_delegates) {
+ sandbox::SetAmZygoteOrRenderer(true, GetSandboxFD());
+
+ auto* linux_sandbox = service_manager::SandboxLinux::GetInstance();
+
+ // Skip pre-initializing sandbox when sandbox is disabled for
+ // https://crbug.com/444900.
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+ service_manager::switches::kNoSandbox) &&
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ service_manager::switches::kNoZygoteSandbox)) {
+ // This will pre-initialize the various sandboxes that need it.
+ linux_sandbox->PreinitializeSandbox();
+ }
+
+ const bool using_setuid_sandbox =
+ linux_sandbox->setuid_sandbox_client()->IsSuidSandboxChild();
+ const bool using_namespace_sandbox =
+ sandbox::NamespaceSandbox::InNewUserNamespace();
+ const bool using_layer1_sandbox =
+ using_setuid_sandbox || using_namespace_sandbox;
+
+ if (using_setuid_sandbox) {
+ linux_sandbox->setuid_sandbox_client()->CloseDummyFile();
+ }
+
+ if (using_layer1_sandbox) {
+ // Let the ZygoteHost know we're booting up.
+ if (!base::UnixDomainSocket::SendMsg(
+ kZygoteSocketPairFd, kZygoteBootMessage, sizeof(kZygoteBootMessage),
+ std::vector<int>())) {
+ // This is not a CHECK failure because the browser process could either
+ // crash or quickly exit while the zygote is starting. In either case a
+ // zygote crash is not useful. https://crbug.com/692227
+ PLOG(ERROR) << "Failed sending zygote boot message";
+ _exit(1);
+ }
+ }
+
+ VLOG(1) << "ZygoteMain: initializing " << fork_delegates.size()
+ << " fork delegates";
+ for (const auto& fork_delegate : fork_delegates) {
+ fork_delegate->Init(GetSandboxFD(), using_layer1_sandbox);
+ }
+
+ // Turn on the first layer of the sandbox if the configuration warrants it.
+ EnterLayerOneSandbox(
+ linux_sandbox, using_layer1_sandbox,
+ base::BindOnce(CloseFds, linux_sandbox->GetFileDescriptorsToClose()));
+
+ const int sandbox_flags = linux_sandbox->GetStatus();
+ const bool setuid_sandbox_engaged =
+ !!(sandbox_flags & service_manager::SandboxLinux::kSUID);
+ CHECK_EQ(using_setuid_sandbox, setuid_sandbox_engaged);
+
+ const bool namespace_sandbox_engaged =
+ !!(sandbox_flags & service_manager::SandboxLinux::kUserNS);
+ CHECK_EQ(using_namespace_sandbox, namespace_sandbox_engaged);
+
+ Zygote zygote(sandbox_flags, std::move(fork_delegates),
+ base::GlobalDescriptors::Descriptor(
+ static_cast<uint32_t>(service_manager::kSandboxIPCChannel),
+ GetSandboxFD()));
+
+ // This function call can return multiple times, once per fork().
+ return zygote.ProcessRequests();
+}
+
+} // namespace content