Home | History | Annotate | Download | only in zygote
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/nacl/zygote/nacl_fork_delegate_linux.h"
      6 
      7 #include <signal.h>
      8 #include <stdlib.h>
      9 #include <sys/resource.h>
     10 #include <sys/socket.h>
     11 
     12 #include <set>
     13 
     14 #include "base/basictypes.h"
     15 #include "base/command_line.h"
     16 #include "base/cpu.h"
     17 #include "base/files/file_path.h"
     18 #include "base/logging.h"
     19 #include "base/path_service.h"
     20 #include "base/posix/eintr_wrapper.h"
     21 #include "base/posix/unix_domain_socket_linux.h"
     22 #include "base/process/launch.h"
     23 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
     24 #include "components/nacl/common/nacl_helper_linux.h"
     25 #include "components/nacl/common/nacl_paths.h"
     26 #include "components/nacl/common/nacl_switches.h"
     27 #include "content/public/common/content_switches.h"
     28 
     29 namespace {
     30 
     31 // Note these need to match up with their counterparts in nacl_helper_linux.c
     32 // and nacl_helper_bootstrap_linux.c.
     33 const char kNaClHelperReservedAtZero[] =
     34     "--reserved_at_zero=0xXXXXXXXXXXXXXXXX";
     35 const char kNaClHelperRDebug[] = "--r_debug=0xXXXXXXXXXXXXXXXX";
     36 
     37 #if defined(ARCH_CPU_X86)
     38 bool NonZeroSegmentBaseIsSlow() {
     39   base::CPU cpuid;
     40   // Using a non-zero segment base is known to be very slow on Intel
     41   // Atom CPUs.  See "Segmentation-based Memory Protection Mechanism
     42   // on Intel Atom Microarchitecture: Coding Optimizations" (Leonardo
     43   // Potenza, Intel).
     44   //
     45   // The following list of CPU model numbers is taken from:
     46   // "Intel 64 and IA-32 Architectures Software Developer's Manual"
     47   // (http://download.intel.com/products/processor/manual/325462.pdf),
     48   // "Table 35-1. CPUID Signature Values of DisplayFamily_DisplayModel"
     49   // (Volume 3C, 35-1), which contains:
     50   //   "06_36H - Intel Atom S Processor Family
     51   //    06_1CH, 06_26H, 06_27H, 06_35, 06_36 - Intel Atom Processor Family"
     52   if (cpuid.family() == 6) {
     53     switch (cpuid.model()) {
     54       case 0x1c:
     55       case 0x26:
     56       case 0x27:
     57       case 0x35:
     58       case 0x36:
     59         return true;
     60     }
     61   }
     62   return false;
     63 }
     64 #endif
     65 
     66 }  // namespace.
     67 
     68 NaClForkDelegate::NaClForkDelegate()
     69     : status_(kNaClHelperUnused),
     70       fd_(-1) {}
     71 
     72 void NaClForkDelegate::Init(const int sandboxdesc) {
     73   VLOG(1) << "NaClForkDelegate::Init()";
     74   int fds[2];
     75 
     76   // Confirm a hard-wired assumption.
     77   // The NaCl constant is from chrome/nacl/nacl_linux_helper.h
     78   DCHECK(kNaClSandboxDescriptor == sandboxdesc);
     79 
     80   CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
     81   base::FileHandleMappingVector fds_to_map;
     82   fds_to_map.push_back(std::make_pair(fds[1], kNaClZygoteDescriptor));
     83   fds_to_map.push_back(std::make_pair(sandboxdesc, kNaClSandboxDescriptor));
     84 
     85   // Using nacl_helper_bootstrap is not necessary on x86-64 because
     86   // NaCl's x86-64 sandbox is not zero-address-based.  Starting
     87   // nacl_helper through nacl_helper_bootstrap works on x86-64, but it
     88   // leaves nacl_helper_bootstrap mapped at a fixed address at the
     89   // bottom of the address space, which is undesirable because it
     90   // effectively defeats ASLR.
     91 #if defined(ARCH_CPU_X86_64)
     92   bool kUseNaClBootstrap = false;
     93 #elif defined(ARCH_CPU_X86)
     94   // Performance vs. security trade-off: We prefer using a
     95   // non-zero-address-based sandbox on x86-32 because it provides some
     96   // ASLR and so is more secure.  However, on Atom CPUs, using a
     97   // non-zero segment base is very slow, so we use a zero-based
     98   // sandbox on those.
     99   bool kUseNaClBootstrap = NonZeroSegmentBaseIsSlow();
    100 #else
    101   bool kUseNaClBootstrap = true;
    102 #endif
    103 
    104   status_ = kNaClHelperUnused;
    105   base::FilePath helper_exe;
    106   base::FilePath helper_bootstrap_exe;
    107   if (!PathService::Get(nacl::FILE_NACL_HELPER, &helper_exe)) {
    108     status_ = kNaClHelperMissing;
    109   } else if (kUseNaClBootstrap &&
    110              !PathService::Get(nacl::FILE_NACL_HELPER_BOOTSTRAP,
    111                                &helper_bootstrap_exe)) {
    112     status_ = kNaClHelperBootstrapMissing;
    113   } else if (RunningOnValgrind()) {
    114     status_ = kNaClHelperValgrind;
    115   } else {
    116     CommandLine::StringVector argv_to_launch;
    117     {
    118       CommandLine cmd_line(CommandLine::NO_PROGRAM);
    119       if (kUseNaClBootstrap)
    120         cmd_line.SetProgram(helper_bootstrap_exe);
    121       else
    122         cmd_line.SetProgram(helper_exe);
    123 
    124       // Append any switches that need to be forwarded to the NaCl helper.
    125       static const char* kForwardSwitches[] = {
    126         switches::kDisableSeccompFilterSandbox,
    127         switches::kNoSandbox,
    128       };
    129       const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
    130       cmd_line.CopySwitchesFrom(current_cmd_line, kForwardSwitches,
    131                                 arraysize(kForwardSwitches));
    132 
    133       // The command line needs to be tightly controlled to use
    134       // |helper_bootstrap_exe|. So from now on, argv_to_launch should be
    135       // modified directly.
    136       argv_to_launch = cmd_line.argv();
    137     }
    138     if (kUseNaClBootstrap) {
    139       // Arguments to the bootstrap helper which need to be at the start
    140       // of the command line, right after the helper's path.
    141       CommandLine::StringVector bootstrap_prepend;
    142       bootstrap_prepend.push_back(helper_exe.value());
    143       bootstrap_prepend.push_back(kNaClHelperReservedAtZero);
    144       bootstrap_prepend.push_back(kNaClHelperRDebug);
    145       argv_to_launch.insert(argv_to_launch.begin() + 1,
    146                             bootstrap_prepend.begin(),
    147                             bootstrap_prepend.end());
    148     }
    149     base::LaunchOptions options;
    150     options.fds_to_remap = &fds_to_map;
    151     options.clone_flags = CLONE_FS | SIGCHLD;
    152 
    153     // The NaCl processes spawned may need to exceed the ambient soft limit
    154     // on RLIMIT_AS to allocate the untrusted address space and its guard
    155     // regions.  The nacl_helper itself cannot just raise its own limit,
    156     // because the existing limit may prevent the initial exec of
    157     // nacl_helper_bootstrap from succeeding, with its large address space
    158     // reservation.
    159     std::set<int> max_these_limits;
    160     max_these_limits.insert(RLIMIT_AS);
    161     options.maximize_rlimits = &max_these_limits;
    162 
    163     if (!base::LaunchProcess(argv_to_launch, options, NULL))
    164       status_ = kNaClHelperLaunchFailed;
    165     // parent and error cases are handled below
    166   }
    167   if (HANDLE_EINTR(close(fds[1])) != 0)
    168     LOG(ERROR) << "close(fds[1]) failed";
    169   if (status_ == kNaClHelperUnused) {
    170     const ssize_t kExpectedLength = strlen(kNaClHelperStartupAck);
    171     char buf[kExpectedLength];
    172 
    173     // Wait for ack from nacl_helper, indicating it is ready to help
    174     const ssize_t nread = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
    175     if (nread == kExpectedLength &&
    176         memcmp(buf, kNaClHelperStartupAck, nread) == 0) {
    177       // all is well
    178       status_ = kNaClHelperSuccess;
    179       fd_ = fds[0];
    180       return;
    181     }
    182 
    183     status_ = kNaClHelperAckFailed;
    184     LOG(ERROR) << "Bad NaCl helper startup ack (" << nread << " bytes)";
    185   }
    186   // TODO(bradchen): Make this LOG(ERROR) when the NaCl helper
    187   // becomes the default.
    188   fd_ = -1;
    189   if (HANDLE_EINTR(close(fds[0])) != 0)
    190     LOG(ERROR) << "close(fds[0]) failed";
    191 }
    192 
    193 void NaClForkDelegate::InitialUMA(std::string* uma_name,
    194                                   int* uma_sample,
    195                                   int* uma_boundary_value) {
    196   *uma_name = "NaCl.Client.Helper.InitState";
    197   *uma_sample = status_;
    198   *uma_boundary_value = kNaClHelperStatusBoundary;
    199 }
    200 
    201 NaClForkDelegate::~NaClForkDelegate() {
    202   // side effect of close: delegate process will terminate
    203   if (status_ == kNaClHelperSuccess) {
    204     if (HANDLE_EINTR(close(fd_)) != 0)
    205       LOG(ERROR) << "close(fd_) failed";
    206   }
    207 }
    208 
    209 bool NaClForkDelegate::CanHelp(const std::string& process_type,
    210                                std::string* uma_name,
    211                                int* uma_sample,
    212                                int* uma_boundary_value) {
    213   if (process_type != switches::kNaClLoaderProcess)
    214     return false;
    215   *uma_name = "NaCl.Client.Helper.StateOnFork";
    216   *uma_sample = status_;
    217   *uma_boundary_value = kNaClHelperStatusBoundary;
    218   return status_ == kNaClHelperSuccess;
    219 }
    220 
    221 pid_t NaClForkDelegate::Fork(const std::vector<int>& fds) {
    222   base::ProcessId naclchild;
    223   VLOG(1) << "NaClForkDelegate::Fork";
    224 
    225   DCHECK(fds.size() == kNaClParentFDIndex + 1);
    226   if (!UnixDomainSocket::SendMsg(fd_, kNaClForkRequest,
    227                                  strlen(kNaClForkRequest), fds)) {
    228     LOG(ERROR) << "NaClForkDelegate::Fork: SendMsg failed";
    229     return -1;
    230   }
    231   int nread = HANDLE_EINTR(read(fd_, &naclchild, sizeof(naclchild)));
    232   if (nread != sizeof(naclchild)) {
    233     LOG(ERROR) << "NaClForkDelegate::Fork: read failed";
    234     return -1;
    235   }
    236   VLOG(1) << "nacl_child is " << naclchild << " (" << nread << " bytes)";
    237   return naclchild;
    238 }
    239 
    240 bool NaClForkDelegate::AckChild(const int fd,
    241                                 const std::string& channel_switch) {
    242   int nwritten = HANDLE_EINTR(write(fd, channel_switch.c_str(),
    243                                     channel_switch.length()));
    244   if (nwritten != static_cast<int>(channel_switch.length())) {
    245     return false;
    246   }
    247   return true;
    248 }
    249