diff options
author | Ben Murdoch <benm@google.com> | 2013-07-31 10:55:33 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2013-07-31 10:55:33 +0100 |
commit | d3868032626d59662ff73b372b5d584c1d144c53 (patch) | |
tree | b53968078879cdade155a2e869a58b2ec568a06d /components | |
parent | e5b4422c968a7a35d5d4fbae38f8786f61e98bc8 (diff) | |
download | chromium_org-d3868032626d59662ff73b372b5d584c1d144c53.tar.gz |
Merge from Chromium at DEPS revision r214391
This commit was generated by merge_to_master.py.
Change-Id: Iad15fada300ebc6cf9cbcebfc484b1a5f5f372e5
Diffstat (limited to 'components')
-rw-r--r-- | components/nacl/nacl_defines.gypi | 61 | ||||
-rw-r--r-- | components/nacl/zygote/nacl_fork_delegate_linux.cc | 248 | ||||
-rw-r--r-- | components/nacl/zygote/nacl_fork_delegate_linux.h | 53 |
3 files changed, 362 insertions, 0 deletions
diff --git a/components/nacl/nacl_defines.gypi b/components/nacl/nacl_defines.gypi new file mode 100644 index 0000000000..e00abaa35b --- /dev/null +++ b/components/nacl/nacl_defines.gypi @@ -0,0 +1,61 @@ +# Copyright 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. + +{ + 'variables': { + 'conditions': [ + ['OS=="win"', { + 'nacl_defines': [ + 'NACL_WINDOWS=1', + 'NACL_LINUX=0', + 'NACL_OSX=0', + ], + }], + ['OS=="linux"', { + 'nacl_defines': [ + 'NACL_WINDOWS=0', + 'NACL_LINUX=1', + 'NACL_OSX=0', + ], + }], + ['OS=="mac"', { + 'nacl_defines': [ + 'NACL_WINDOWS=0', + 'NACL_LINUX=0', + 'NACL_OSX=1', + ], + }], + # TODO(mcgrathr): This duplicates native_client/build/common.gypi; + # we should figure out a way to unify the settings. + ['target_arch=="ia32"', { + 'nacl_defines': [ + 'NACL_TARGET_SUBARCH=32', + 'NACL_TARGET_ARCH=x86', + 'NACL_BUILD_SUBARCH=32', + 'NACL_BUILD_ARCH=x86', + ], + }], + ['target_arch=="x64"', { + 'nacl_defines': [ + 'NACL_TARGET_SUBARCH=64', + 'NACL_TARGET_ARCH=x86', + 'NACL_BUILD_SUBARCH=64', + 'NACL_BUILD_ARCH=x86', + ], + }], + ['target_arch=="arm"', { + 'nacl_defines': [ + 'NACL_BUILD_ARCH=arm', + 'NACL_BUILD_SUBARCH=32', + 'NACL_TARGET_ARCH=arm', + 'NACL_TARGET_SUBARCH=32', + ], + }], + ['target_arch=="mipsel"', { + 'nacl_defines': [ + ], + }], + ], + } +} diff --git a/components/nacl/zygote/nacl_fork_delegate_linux.cc b/components/nacl/zygote/nacl_fork_delegate_linux.cc new file mode 100644 index 0000000000..8445342fda --- /dev/null +++ b/components/nacl/zygote/nacl_fork_delegate_linux.cc @@ -0,0 +1,248 @@ +// 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 "components/nacl/zygote/nacl_fork_delegate_linux.h" + +#include <signal.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/socket.h> + +#include <set> + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/cpu.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/posix/eintr_wrapper.h" +#include "base/posix/unix_domain_socket_linux.h" +#include "base/process/launch.h" +#include "base/third_party/dynamic_annotations/dynamic_annotations.h" +#include "components/nacl/common/nacl_helper_linux.h" +#include "components/nacl/common/nacl_paths.h" +#include "components/nacl/common/nacl_switches.h" +#include "content/public/common/content_switches.h" + +namespace { + +// Note these need to match up with their counterparts in nacl_helper_linux.c +// and nacl_helper_bootstrap_linux.c. +const char kNaClHelperReservedAtZero[] = + "--reserved_at_zero=0xXXXXXXXXXXXXXXXX"; +const char kNaClHelperRDebug[] = "--r_debug=0xXXXXXXXXXXXXXXXX"; + +#if defined(ARCH_CPU_X86) +bool NonZeroSegmentBaseIsSlow() { + base::CPU cpuid; + // Using a non-zero segment base is known to be very slow on Intel + // Atom CPUs. See "Segmentation-based Memory Protection Mechanism + // on Intel Atom Microarchitecture: Coding Optimizations" (Leonardo + // Potenza, Intel). + // + // The following list of CPU model numbers is taken from: + // "Intel 64 and IA-32 Architectures Software Developer's Manual" + // (http://download.intel.com/products/processor/manual/325462.pdf), + // "Table 35-1. CPUID Signature Values of DisplayFamily_DisplayModel" + // (Volume 3C, 35-1), which contains: + // "06_36H - Intel Atom S Processor Family + // 06_1CH, 06_26H, 06_27H, 06_35, 06_36 - Intel Atom Processor Family" + if (cpuid.family() == 6) { + switch (cpuid.model()) { + case 0x1c: + case 0x26: + case 0x27: + case 0x35: + case 0x36: + return true; + } + } + return false; +} +#endif + +} // namespace. + +NaClForkDelegate::NaClForkDelegate() + : status_(kNaClHelperUnused), + fd_(-1) {} + +void NaClForkDelegate::Init(const int sandboxdesc) { + VLOG(1) << "NaClForkDelegate::Init()"; + int fds[2]; + + // Confirm a hard-wired assumption. + // The NaCl constant is from chrome/nacl/nacl_linux_helper.h + DCHECK(kNaClSandboxDescriptor == sandboxdesc); + + CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); + base::FileHandleMappingVector fds_to_map; + fds_to_map.push_back(std::make_pair(fds[1], kNaClZygoteDescriptor)); + fds_to_map.push_back(std::make_pair(sandboxdesc, kNaClSandboxDescriptor)); + + // Using nacl_helper_bootstrap is not necessary on x86-64 because + // NaCl's x86-64 sandbox is not zero-address-based. Starting + // nacl_helper through nacl_helper_bootstrap works on x86-64, but it + // leaves nacl_helper_bootstrap mapped at a fixed address at the + // bottom of the address space, which is undesirable because it + // effectively defeats ASLR. +#if defined(ARCH_CPU_X86_64) + bool kUseNaClBootstrap = false; +#elif defined(ARCH_CPU_X86) + // Performance vs. security trade-off: We prefer using a + // non-zero-address-based sandbox on x86-32 because it provides some + // ASLR and so is more secure. However, on Atom CPUs, using a + // non-zero segment base is very slow, so we use a zero-based + // sandbox on those. + bool kUseNaClBootstrap = NonZeroSegmentBaseIsSlow(); +#else + bool kUseNaClBootstrap = true; +#endif + + status_ = kNaClHelperUnused; + base::FilePath helper_exe; + base::FilePath helper_bootstrap_exe; + if (!PathService::Get(nacl::FILE_NACL_HELPER, &helper_exe)) { + status_ = kNaClHelperMissing; + } else if (kUseNaClBootstrap && + !PathService::Get(nacl::FILE_NACL_HELPER_BOOTSTRAP, + &helper_bootstrap_exe)) { + status_ = kNaClHelperBootstrapMissing; + } else if (RunningOnValgrind()) { + status_ = kNaClHelperValgrind; + } else { + CommandLine::StringVector argv_to_launch; + { + CommandLine cmd_line(CommandLine::NO_PROGRAM); + if (kUseNaClBootstrap) + cmd_line.SetProgram(helper_bootstrap_exe); + else + cmd_line.SetProgram(helper_exe); + + // Append any switches that need to be forwarded to the NaCl helper. + static const char* kForwardSwitches[] = { + switches::kDisableSeccompFilterSandbox, + switches::kNoSandbox, + }; + const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess(); + cmd_line.CopySwitchesFrom(current_cmd_line, kForwardSwitches, + arraysize(kForwardSwitches)); + + // The command line needs to be tightly controlled to use + // |helper_bootstrap_exe|. So from now on, argv_to_launch should be + // modified directly. + argv_to_launch = cmd_line.argv(); + } + if (kUseNaClBootstrap) { + // Arguments to the bootstrap helper which need to be at the start + // of the command line, right after the helper's path. + CommandLine::StringVector bootstrap_prepend; + bootstrap_prepend.push_back(helper_exe.value()); + bootstrap_prepend.push_back(kNaClHelperReservedAtZero); + bootstrap_prepend.push_back(kNaClHelperRDebug); + argv_to_launch.insert(argv_to_launch.begin() + 1, + bootstrap_prepend.begin(), + bootstrap_prepend.end()); + } + base::LaunchOptions options; + options.fds_to_remap = &fds_to_map; + options.clone_flags = CLONE_FS | SIGCHLD; + + // The NaCl processes spawned may need to exceed the ambient soft limit + // on RLIMIT_AS to allocate the untrusted address space and its guard + // regions. The nacl_helper itself cannot just raise its own limit, + // because the existing limit may prevent the initial exec of + // nacl_helper_bootstrap from succeeding, with its large address space + // reservation. + std::set<int> max_these_limits; + max_these_limits.insert(RLIMIT_AS); + options.maximize_rlimits = &max_these_limits; + + if (!base::LaunchProcess(argv_to_launch, options, NULL)) + status_ = kNaClHelperLaunchFailed; + // parent and error cases are handled below + } + if (HANDLE_EINTR(close(fds[1])) != 0) + LOG(ERROR) << "close(fds[1]) failed"; + if (status_ == kNaClHelperUnused) { + const ssize_t kExpectedLength = strlen(kNaClHelperStartupAck); + char buf[kExpectedLength]; + + // Wait for ack from nacl_helper, indicating it is ready to help + const ssize_t nread = HANDLE_EINTR(read(fds[0], buf, sizeof(buf))); + if (nread == kExpectedLength && + memcmp(buf, kNaClHelperStartupAck, nread) == 0) { + // all is well + status_ = kNaClHelperSuccess; + fd_ = fds[0]; + return; + } + + status_ = kNaClHelperAckFailed; + LOG(ERROR) << "Bad NaCl helper startup ack (" << nread << " bytes)"; + } + // TODO(bradchen): Make this LOG(ERROR) when the NaCl helper + // becomes the default. + fd_ = -1; + if (HANDLE_EINTR(close(fds[0])) != 0) + LOG(ERROR) << "close(fds[0]) failed"; +} + +void NaClForkDelegate::InitialUMA(std::string* uma_name, + int* uma_sample, + int* uma_boundary_value) { + *uma_name = "NaCl.Client.Helper.InitState"; + *uma_sample = status_; + *uma_boundary_value = kNaClHelperStatusBoundary; +} + +NaClForkDelegate::~NaClForkDelegate() { + // side effect of close: delegate process will terminate + if (status_ == kNaClHelperSuccess) { + if (HANDLE_EINTR(close(fd_)) != 0) + LOG(ERROR) << "close(fd_) failed"; + } +} + +bool NaClForkDelegate::CanHelp(const std::string& process_type, + std::string* uma_name, + int* uma_sample, + int* uma_boundary_value) { + if (process_type != switches::kNaClLoaderProcess) + return false; + *uma_name = "NaCl.Client.Helper.StateOnFork"; + *uma_sample = status_; + *uma_boundary_value = kNaClHelperStatusBoundary; + return status_ == kNaClHelperSuccess; +} + +pid_t NaClForkDelegate::Fork(const std::vector<int>& fds) { + base::ProcessId naclchild; + VLOG(1) << "NaClForkDelegate::Fork"; + + DCHECK(fds.size() == kNaClParentFDIndex + 1); + if (!UnixDomainSocket::SendMsg(fd_, kNaClForkRequest, + strlen(kNaClForkRequest), fds)) { + LOG(ERROR) << "NaClForkDelegate::Fork: SendMsg failed"; + return -1; + } + int nread = HANDLE_EINTR(read(fd_, &naclchild, sizeof(naclchild))); + if (nread != sizeof(naclchild)) { + LOG(ERROR) << "NaClForkDelegate::Fork: read failed"; + return -1; + } + VLOG(1) << "nacl_child is " << naclchild << " (" << nread << " bytes)"; + return naclchild; +} + +bool NaClForkDelegate::AckChild(const int fd, + const std::string& channel_switch) { + int nwritten = HANDLE_EINTR(write(fd, channel_switch.c_str(), + channel_switch.length())); + if (nwritten != static_cast<int>(channel_switch.length())) { + return false; + } + return true; +} diff --git a/components/nacl/zygote/nacl_fork_delegate_linux.h b/components/nacl/zygote/nacl_fork_delegate_linux.h new file mode 100644 index 0000000000..5812f33fe2 --- /dev/null +++ b/components/nacl/zygote/nacl_fork_delegate_linux.h @@ -0,0 +1,53 @@ +// 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 COMPONENTS_NACL_ZYGOTE_NACL_FORK_DELEGATE_LINUX_H_ +#define COMPONENTS_NACL_ZYGOTE_NACL_FORK_DELEGATE_LINUX_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "content/public/common/zygote_fork_delegate_linux.h" + +// The NaClForkDelegate is created during Chrome linux zygote +// initialization, and provides "fork()" functionality with +// NaCl specific process characteristics (specifically address +// space layout) as an alternative to forking the zygote. +// A new delegate is passed in as an argument to ZygoteMain(). +class NaClForkDelegate : public content::ZygoteForkDelegate { + public: + NaClForkDelegate(); + virtual ~NaClForkDelegate(); + + virtual void Init(int sandboxdesc) OVERRIDE; + virtual void InitialUMA(std::string* uma_name, + int* uma_sample, + int* uma_boundary_value) OVERRIDE; + virtual bool CanHelp(const std::string& process_type, std::string* uma_name, + int* uma_sample, int* uma_boundary_value) OVERRIDE; + virtual pid_t Fork(const std::vector<int>& fds) OVERRIDE; + virtual bool AckChild(int fd, + const std::string& channel_switch) OVERRIDE; + + private: + // These values are reported via UMA and hence they become permanent + // constants. Old values cannot be reused, only new ones added. + enum NaClHelperStatus { + kNaClHelperUnused = 0, + kNaClHelperMissing = 1, + kNaClHelperBootstrapMissing = 2, + kNaClHelperValgrind = 3, + kNaClHelperLaunchFailed = 4, + kNaClHelperAckFailed = 5, + kNaClHelperSuccess = 6, + kNaClHelperStatusBoundary // Must be one greater than highest value used. + }; + + NaClHelperStatus status_; + int fd_; +}; + +#endif // COMPONENTS_NACL_ZYGOTE_NACL_FORK_DELEGATE_LINUX_H_ |