// 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 #include #include #include #include #include #include #include #include #include #include #include #include "base/bind.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/memory/protected_memory.h" #include "base/memory/protected_memory_cfi.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/sys_info.h" #include "build/build_config.h" #include "content/common/zygote_commands_linux.h" #include "content/public/common/common_sandbox_support_linux.h" #include "content/public/common/content_descriptors.h" #include "content/public/common/content_switches.h" #include "content/public/common/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/sandbox/linux/sandbox_debug_handling_linux.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" namespace content { namespace { void CloseFds(const std::vector& 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 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> fork_delegates) { sandbox::SetAmZygoteOrRenderer(true, GetSandboxFD()); auto* linux_sandbox = service_manager::SandboxLinux::GetInstance(); // Skip pre-initializing sandbox under --no-sandbox for crbug.com/444900. if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kNoSandbox)) { // 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())) { // 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. http://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(kSandboxIPCChannel), GetSandboxFD())); // This function call can return multiple times, once per fork(). return zygote.ProcessRequests(); } } // namespace content