summaryrefslogtreecommitdiff
path: root/chromium/content/browser/zygote_host
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/zygote_host')
-rw-r--r--chromium/content/browser/zygote_host/OWNERS5
-rw-r--r--chromium/content/browser/zygote_host/zygote_browsertest.cc107
-rw-r--r--chromium/content/browser/zygote_host/zygote_host_impl_linux.cc293
-rw-r--r--chromium/content/browser/zygote_host/zygote_host_impl_linux.h73
4 files changed, 474 insertions, 4 deletions
diff --git a/chromium/content/browser/zygote_host/OWNERS b/chromium/content/browser/zygote_host/OWNERS
index 50e496afb9e..eb4b322bdf5 100644
--- a/chromium/content/browser/zygote_host/OWNERS
+++ b/chromium/content/browser/zygote_host/OWNERS
@@ -1,7 +1,4 @@
-jln@chromium.org
-kerrnel@chromium.org
-rsesek@chromium.org
-tsepez@chromium.org
+file://content/zygote/OWNERS
# TEAM: security-dev@chromium.org
# COMPONENT: Internals>Sandbox
diff --git a/chromium/content/browser/zygote_host/zygote_browsertest.cc b/chromium/content/browser/zygote_host/zygote_browsertest.cc
new file mode 100644
index 00000000000..f7339c77d49
--- /dev/null
+++ b/chromium/content/browser/zygote_host/zygote_browsertest.cc
@@ -0,0 +1,107 @@
+// 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/common/zygote/zygote_buildflags.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"
+#if BUILDFLAG(USE_ZYGOTE_HANDLE)
+#include "content/browser/zygote_host/zygote_host_impl_linux.h"
+#include "content/common/zygote/zygote_communication_linux.h"
+#include "content/common/zygote/zygote_handle_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 = GetGenericZygote()->GetSandboxStatus();
+ constexpr int kExpectedFlags = service_manager::SandboxLinux::kPIDNS |
+ service_manager::SandboxLinux::kNetNS |
+ service_manager::SandboxLinux::kUserNS;
+ EXPECT_EQ(kExpectedFlags, flags & kExpectedFlags);
+
+ EXPECT_EQ(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(ZygoteHostImpl::GetInstance()->HasZygote());
+}
+#endif
+
+} // namespace content
diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc
new file mode 100644
index 00000000000..ced1c83e82b
--- /dev/null
+++ b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc
@@ -0,0 +1,293 @@
+// 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/browser/zygote_host/zygote_host_impl_linux.h"
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "base/allocator/allocator_extension.h"
+#include "base/files/file_enumerator.h"
+#include "base/logging.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/process/kill.h"
+#include "base/process/memory.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "content/common/zygote/zygote_commands_linux.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_sandbox.h"
+#include "sandbox/linux/suid/client/setuid_sandbox_host.h"
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "services/service_manager/sandbox/linux/sandbox_linux.h"
+#include "services/service_manager/sandbox/switches.h"
+
+namespace content {
+
+namespace {
+
+// Receive a fixed message on fd and return the sender's PID.
+// Returns true if the message received matches the expected message.
+bool ReceiveFixedMessage(int fd,
+ const char* expect_msg,
+ size_t expect_len,
+ base::ProcessId* sender_pid) {
+ // Allocate an extra byte of buffer space so we can check that we received
+ // exactly |expect_len| bytes, and the message wasn't just truncated to fit.
+ char buf[expect_len + 1];
+ std::vector<base::ScopedFD> fds_vec;
+
+ const ssize_t len = base::UnixDomainSocket::RecvMsgWithPid(
+ fd, buf, sizeof(buf), &fds_vec, sender_pid);
+ if (static_cast<size_t>(len) != expect_len)
+ return false;
+ if (memcmp(buf, expect_msg, expect_len) != 0)
+ return false;
+ if (!fds_vec.empty())
+ return false;
+ return true;
+}
+
+} // namespace
+
+// static
+ZygoteHost* ZygoteHost::GetInstance() {
+ return ZygoteHostImpl::GetInstance();
+}
+
+ZygoteHostImpl::ZygoteHostImpl()
+ : use_namespace_sandbox_(false),
+ use_suid_sandbox_(false),
+ use_suid_sandbox_for_adj_oom_score_(false),
+ sandbox_binary_(),
+ zygote_pids_lock_(),
+ zygote_pids_() {}
+
+ZygoteHostImpl::~ZygoteHostImpl() {}
+
+// static
+ZygoteHostImpl* ZygoteHostImpl::GetInstance() {
+ return base::Singleton<ZygoteHostImpl>::get();
+}
+
+void ZygoteHostImpl::Init(const base::CommandLine& command_line) {
+ if (command_line.HasSwitch(service_manager::switches::kNoSandbox)) {
+ return;
+ }
+
+ // Exit early if running as root without --no-sandbox. See
+ // https://crbug.com/638180.
+ // When running as root with the sandbox enabled, the browser process
+ // crashes on zygote initialization. Running as root with the sandbox
+ // is not supported, and if Chrome were able to display UI it would be showing
+ // an error message. With the zygote crashing it doesn't even get to that,
+ // so print an error message on the console.
+ uid_t uid = 0;
+ gid_t gid = 0;
+ if (!sandbox::Credentials::GetRESIds(&uid, &gid) || uid == 0) {
+ LOG(ERROR) << "Running as root without --"
+ << service_manager::switches::kNoSandbox
+ << " is not supported. See https://crbug.com/638180.";
+ exit(EXIT_FAILURE);
+ }
+
+ {
+ std::unique_ptr<sandbox::SetuidSandboxHost> setuid_sandbox_host(
+ sandbox::SetuidSandboxHost::Create());
+ sandbox_binary_ = setuid_sandbox_host->GetSandboxBinaryPath().value();
+ }
+
+ if (!command_line.HasSwitch(
+ service_manager::switches::kDisableNamespaceSandbox) &&
+ sandbox::Credentials::CanCreateProcessInNewUserNS()) {
+ use_namespace_sandbox_ = true;
+ } else if (!command_line.HasSwitch(
+ service_manager::switches::kDisableSetuidSandbox) &&
+ !sandbox_binary_.empty()) {
+ use_suid_sandbox_ = true;
+
+ // Use the SUID sandbox for adjusting OOM scores when we are using
+ // the setuid sandbox. This is needed beacuse the processes are
+ // non-dumpable, so /proc/pid/oom_score_adj can only be written by
+ // root.
+ use_suid_sandbox_for_adj_oom_score_ = use_suid_sandbox_;
+ } else {
+ LOG(FATAL)
+ << "No usable sandbox! Update your kernel or see "
+ "https://chromium.googlesource.com/chromium/src/+/master/"
+ "docs/linux/suid_sandbox_development.md for more information on "
+ "developing with the SUID sandbox. "
+ "If you want to live dangerously and need an immediate workaround, "
+ "you can try using --"
+ << service_manager::switches::kNoSandbox << ".";
+ }
+}
+
+void ZygoteHostImpl::AddZygotePid(pid_t pid) {
+ base::AutoLock lock(zygote_pids_lock_);
+ zygote_pids_.insert(pid);
+}
+
+bool ZygoteHostImpl::IsZygotePid(pid_t pid) {
+ base::AutoLock lock(zygote_pids_lock_);
+ return zygote_pids_.find(pid) != zygote_pids_.end();
+}
+
+void ZygoteHostImpl::SetRendererSandboxStatus(int status) {
+ renderer_sandbox_status_ = status;
+}
+
+int ZygoteHostImpl::GetRendererSandboxStatus() {
+ return renderer_sandbox_status_;
+}
+
+pid_t ZygoteHostImpl::LaunchZygote(
+ base::CommandLine* cmd_line,
+ base::ScopedFD* control_fd,
+ base::FileHandleMappingVector additional_remapped_fds) {
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ CHECK(base::UnixDomainSocket::EnableReceiveProcessId(fds[0]));
+
+ base::LaunchOptions options;
+ options.fds_to_remap = std::move(additional_remapped_fds);
+ options.fds_to_remap.emplace_back(fds[1], kZygoteSocketPairFd);
+
+ const bool is_sandboxed_zygote =
+ !cmd_line->HasSwitch(service_manager::switches::kNoZygoteSandbox);
+
+ base::ScopedFD dummy_fd;
+ if (is_sandboxed_zygote && use_suid_sandbox_) {
+ std::unique_ptr<sandbox::SetuidSandboxHost> sandbox_host(
+ sandbox::SetuidSandboxHost::Create());
+ sandbox_host->PrependWrapper(cmd_line);
+ sandbox_host->SetupLaunchOptions(&options, &dummy_fd);
+ sandbox_host->SetupLaunchEnvironment();
+ }
+
+ base::Process process =
+ (is_sandboxed_zygote && use_namespace_sandbox_)
+ ? sandbox::NamespaceSandbox::LaunchProcess(*cmd_line, options)
+ : base::LaunchProcess(*cmd_line, options);
+ CHECK(process.IsValid()) << "Failed to launch zygote process";
+
+ dummy_fd.reset();
+ close(fds[1]);
+ control_fd->reset(fds[0]);
+
+ pid_t pid = process.Pid();
+
+ if (is_sandboxed_zygote && (use_namespace_sandbox_ || use_suid_sandbox_)) {
+ // The namespace and SUID sandbox will execute the zygote in a new
+ // PID namespace, and the main zygote process will then fork from
+ // there. Watch now our elaborate dance to find and validate the
+ // zygote's PID.
+
+ // First we receive a message from the zygote boot process.
+ base::ProcessId boot_pid;
+ CHECK(ReceiveFixedMessage(fds[0], kZygoteBootMessage,
+ sizeof(kZygoteBootMessage), &boot_pid));
+
+ // Within the PID namespace, the zygote boot process thinks it's PID 1,
+ // but its real PID can never be 1. This gives us a reliable test that
+ // the kernel is translating the sender's PID to our namespace.
+ CHECK_GT(boot_pid, 1)
+ << "Received invalid process ID for zygote; kernel might be too old? "
+ "See crbug.com/357670 or try using --"
+ << service_manager::switches::kNoSandbox << " to workaround.";
+
+ // Now receive the message that the zygote's ready to go, along with the
+ // main zygote process's ID.
+ pid_t real_pid;
+ CHECK(ReceiveFixedMessage(fds[0], kZygoteHelloMessage,
+ sizeof(kZygoteHelloMessage), &real_pid));
+ CHECK_GT(real_pid, 1);
+
+ if (real_pid != pid) {
+ // Reap the sandbox.
+ base::EnsureProcessGetsReaped(std::move(process));
+ }
+ pid = real_pid;
+ }
+
+ AddZygotePid(pid);
+ return pid;
+}
+
+#if !defined(OS_OPENBSD)
+void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid,
+ int score) {
+ // 1) You can't change the oom_score_adj of a non-dumpable process
+ // (EPERM) unless you're root. Because of this, we can't set the
+ // oom_adj from the browser process.
+ //
+ // 2) We can't set the oom_score_adj before entering the sandbox
+ // because the zygote is in the sandbox and the zygote is as
+ // critical as the browser process. Its oom_adj value shouldn't
+ // be changed.
+ //
+ // 3) A non-dumpable process can't even change its own oom_score_adj
+ // because it's root owned 0644. The sandboxed processes don't
+ // even have /proc, but one could imagine passing in a descriptor
+ // from outside.
+ //
+ // So, in the normal case, we use the SUID binary to change it for us.
+ // However, Fedora (and other SELinux systems) don't like us touching other
+ // process's oom_score_adj (or oom_adj) values
+ // (https://bugzilla.redhat.com/show_bug.cgi?id=581256).
+ //
+ // The offical way to get the SELinux mode is selinux_getenforcemode, but I
+ // don't want to add another library to the build as it's sure to cause
+ // problems with other, non-SELinux distros.
+ //
+ // So we just check for files in /selinux. This isn't foolproof, but it's not
+ // bad and it's easy.
+
+ static bool selinux;
+ static bool selinux_valid = false;
+
+ if (!selinux_valid) {
+ const base::FilePath kSelinuxPath("/selinux");
+ base::FileEnumerator en(kSelinuxPath, false, base::FileEnumerator::FILES);
+ bool has_selinux_files = !en.Next().empty();
+
+ selinux =
+ has_selinux_files && access(kSelinuxPath.value().c_str(), X_OK) == 0;
+ selinux_valid = true;
+ }
+
+ if (!use_suid_sandbox_for_adj_oom_score_) {
+ if (!base::AdjustOOMScore(pid, score))
+ PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid;
+ return;
+ }
+
+ if (selinux)
+ return;
+
+ // If heap profiling is running, these processes are not exiting, at least
+ // on ChromeOS. The easiest thing to do is not launch them when profiling.
+ // TODO(stevenjb): Investigate further and fix.
+ if (base::allocator::IsHeapProfilerRunning())
+ return;
+
+ std::vector<std::string> adj_oom_score_cmdline;
+ adj_oom_score_cmdline.push_back(sandbox_binary_);
+ adj_oom_score_cmdline.push_back(sandbox::kAdjustOOMScoreSwitch);
+ adj_oom_score_cmdline.push_back(base::NumberToString(pid));
+ adj_oom_score_cmdline.push_back(base::NumberToString(score));
+
+ // sandbox_helper_process is a setuid binary.
+ base::LaunchOptions options;
+ options.allow_new_privs = true;
+
+ base::Process sandbox_helper_process =
+ base::LaunchProcess(adj_oom_score_cmdline, options);
+ if (sandbox_helper_process.IsValid())
+ base::EnsureProcessGetsReaped(std::move(sandbox_helper_process));
+}
+#endif
+
+} // namespace content
diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.h b/chromium/content/browser/zygote_host/zygote_host_impl_linux.h
new file mode 100644
index 00000000000..21b8323eb49
--- /dev/null
+++ b/chromium/content/browser/zygote_host/zygote_host_impl_linux.h
@@ -0,0 +1,73 @@
+// 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_BROWSER_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_
+#define CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_
+
+#include <sys/types.h>
+
+#include <set>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/files/scoped_file.h"
+#include "base/process/launch.h"
+#include "base/process/process_handle.h"
+#include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/zygote_host/zygote_host_linux.h"
+
+namespace base {
+template <typename Type>
+struct DefaultSingletonTraits;
+} // namespace base
+
+namespace content {
+
+class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost {
+ public:
+ // Returns the singleton instance.
+ static ZygoteHostImpl* GetInstance();
+
+ void Init(const base::CommandLine& cmd_line);
+
+ // Returns whether or not this pid is the pid of a zygote.
+ bool IsZygotePid(pid_t pid) override;
+
+ void SetRendererSandboxStatus(int status);
+ int GetRendererSandboxStatus() override;
+
+ pid_t LaunchZygote(base::CommandLine* cmd_line,
+ base::ScopedFD* control_fd,
+ base::FileHandleMappingVector additional_remapped_fds);
+
+ void AdjustRendererOOMScore(base::ProcessHandle process_handle,
+ int score) override;
+ bool HasZygote() { return !zygote_pids_.empty(); }
+
+ private:
+ friend struct base::DefaultSingletonTraits<ZygoteHostImpl>;
+
+ ZygoteHostImpl();
+ ~ZygoteHostImpl() override;
+
+ // Tells the ZygoteHost the PIDs of all the zygotes.
+ void AddZygotePid(pid_t pid);
+
+ int renderer_sandbox_status_;
+
+ bool use_namespace_sandbox_;
+ bool use_suid_sandbox_;
+ bool use_suid_sandbox_for_adj_oom_score_;
+ std::string sandbox_binary_;
+
+ // This lock protects the |zygote_pids_| set.
+ base::Lock zygote_pids_lock_;
+ // This is a set of PIDs representing all the running zygotes.
+ std::set<pid_t> zygote_pids_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_