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