1 // Copyright 2013 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 // A mini-zygote specifically for Native Client. 6 7 #include "components/nacl/loader/nacl_helper_linux.h" 8 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <link.h> 12 #include <signal.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <sys/socket.h> 16 #include <sys/stat.h> 17 #include <sys/types.h> 18 19 #include <string> 20 #include <vector> 21 22 #include "base/at_exit.h" 23 #include "base/command_line.h" 24 #include "base/files/scoped_file.h" 25 #include "base/logging.h" 26 #include "base/memory/scoped_ptr.h" 27 #include "base/memory/scoped_vector.h" 28 #include "base/message_loop/message_loop.h" 29 #include "base/posix/eintr_wrapper.h" 30 #include "base/posix/global_descriptors.h" 31 #include "base/posix/unix_domain_socket_linux.h" 32 #include "base/process/kill.h" 33 #include "base/process/process_handle.h" 34 #include "base/rand_util.h" 35 #include "components/nacl/common/nacl_switches.h" 36 #include "components/nacl/loader/nacl_listener.h" 37 #include "components/nacl/loader/nonsfi/irt_exception_handling.h" 38 #include "components/nacl/loader/nonsfi/nonsfi_listener.h" 39 #include "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h" 40 #include "content/public/common/child_process_sandbox_support_linux.h" 41 #include "content/public/common/content_descriptors.h" 42 #include "content/public/common/zygote_fork_delegate_linux.h" 43 #include "crypto/nss_util.h" 44 #include "ipc/ipc_descriptors.h" 45 #include "ipc/ipc_switches.h" 46 #include "sandbox/linux/services/libc_urandom_override.h" 47 48 namespace { 49 50 struct NaClLoaderSystemInfo { 51 size_t prereserved_sandbox_size; 52 long number_of_cores; 53 }; 54 55 // Replace |file_descriptor| with the reading end of a closed pipe. 56 void ReplaceFDWithDummy(int file_descriptor) { 57 // Make sure that file_descriptor is an open descriptor. 58 PCHECK(-1 != fcntl(file_descriptor, F_GETFD, 0)); 59 int pipefd[2]; 60 PCHECK(0 == pipe(pipefd)); 61 PCHECK(-1 != dup2(pipefd[0], file_descriptor)); 62 PCHECK(0 == IGNORE_EINTR(close(pipefd[0]))); 63 PCHECK(0 == IGNORE_EINTR(close(pipefd[1]))); 64 } 65 66 // The child must mimic the behavior of zygote_main_linux.cc on the child 67 // side of the fork. See zygote_main_linux.cc:HandleForkRequest from 68 // if (!child) { 69 void BecomeNaClLoader(base::ScopedFD browser_fd, 70 const NaClLoaderSystemInfo& system_info, 71 bool uses_nonsfi_mode, 72 nacl::NaClSandbox* nacl_sandbox) { 73 DCHECK(nacl_sandbox); 74 VLOG(1) << "NaCl loader: setting up IPC descriptor"; 75 // Close or shutdown IPC channels that we don't need anymore. 76 PCHECK(0 == IGNORE_EINTR(close(kNaClZygoteDescriptor))); 77 // In Non-SFI mode, it's important to close any non-expected IPC channels. 78 if (uses_nonsfi_mode) { 79 // The low-level kSandboxIPCChannel is used by renderers and NaCl for 80 // various operations. See the LinuxSandbox::METHOD_* methods. NaCl uses 81 // LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT in SFI mode, so this 82 // should only be closed in Non-SFI mode. 83 // This file descriptor is insidiously used by a number of APIs. Closing it 84 // could lead to difficult to debug issues. Instead of closing it, replace 85 // it with a dummy. 86 const int sandbox_ipc_channel = 87 base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel; 88 89 ReplaceFDWithDummy(sandbox_ipc_channel); 90 91 // Install crash signal handlers before disallowing system calls. 92 nacl::nonsfi::InitializeSignalHandler(); 93 } 94 95 // Always ignore SIGPIPE, for consistency with other Chrome processes and 96 // because some IPC code, such as sync_socket_posix.cc, requires this. 97 // We do this before seccomp-bpf is initialized. 98 PCHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR); 99 100 // Finish layer-1 sandbox initialization and initialize the layer-2 sandbox. 101 CHECK(!nacl_sandbox->HasOpenDirectory()); 102 nacl_sandbox->InitializeLayerTwoSandbox(uses_nonsfi_mode); 103 nacl_sandbox->SealLayerOneSandbox(); 104 nacl_sandbox->CheckSandboxingStateWithPolicy(); 105 106 base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel, 107 browser_fd.release()); 108 109 base::MessageLoopForIO main_message_loop; 110 if (uses_nonsfi_mode) { 111 nacl::nonsfi::NonSfiListener listener; 112 listener.Listen(); 113 } else { 114 NaClListener listener; 115 listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size); 116 listener.set_number_of_cores(system_info.number_of_cores); 117 listener.Listen(); 118 } 119 _exit(0); 120 } 121 122 // Start the NaCl loader in a child created by the NaCl loader Zygote. 123 void ChildNaClLoaderInit(ScopedVector<base::ScopedFD> child_fds, 124 const NaClLoaderSystemInfo& system_info, 125 bool uses_nonsfi_mode, 126 nacl::NaClSandbox* nacl_sandbox, 127 const std::string& channel_id) { 128 DCHECK(child_fds.size() > 129 std::max(content::ZygoteForkDelegate::kPIDOracleFDIndex, 130 content::ZygoteForkDelegate::kBrowserFDIndex)); 131 132 // Ping the PID oracle socket. 133 CHECK(content::SendZygoteChildPing( 134 child_fds[content::ZygoteForkDelegate::kPIDOracleFDIndex]->get())); 135 136 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 137 switches::kProcessChannelID, channel_id); 138 139 // Save the browser socket and close the rest. 140 base::ScopedFD browser_fd( 141 child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]->Pass()); 142 child_fds.clear(); 143 144 BecomeNaClLoader( 145 browser_fd.Pass(), system_info, uses_nonsfi_mode, nacl_sandbox); 146 _exit(1); 147 } 148 149 // Handle a fork request from the Zygote. 150 // Some of this code was lifted from 151 // content/browser/zygote_main_linux.cc:ForkWithRealPid() 152 bool HandleForkRequest(ScopedVector<base::ScopedFD> child_fds, 153 const NaClLoaderSystemInfo& system_info, 154 nacl::NaClSandbox* nacl_sandbox, 155 PickleIterator* input_iter, 156 Pickle* output_pickle) { 157 bool uses_nonsfi_mode; 158 if (!input_iter->ReadBool(&uses_nonsfi_mode)) { 159 LOG(ERROR) << "Could not read uses_nonsfi_mode status"; 160 return false; 161 } 162 163 std::string channel_id; 164 if (!input_iter->ReadString(&channel_id)) { 165 LOG(ERROR) << "Could not read channel_id string"; 166 return false; 167 } 168 169 if (content::ZygoteForkDelegate::kNumPassedFDs != child_fds.size()) { 170 LOG(ERROR) << "nacl_helper: unexpected number of fds, got " 171 << child_fds.size(); 172 return false; 173 } 174 175 VLOG(1) << "nacl_helper: forking"; 176 pid_t child_pid = fork(); 177 if (child_pid < 0) { 178 PLOG(ERROR) << "*** fork() failed."; 179 } 180 181 if (child_pid == 0) { 182 ChildNaClLoaderInit(child_fds.Pass(), 183 system_info, 184 uses_nonsfi_mode, 185 nacl_sandbox, 186 channel_id); 187 NOTREACHED(); 188 } 189 190 // I am the parent. 191 // First, close the dummy_fd so the sandbox won't find me when 192 // looking for the child's pid in /proc. Also close other fds. 193 child_fds.clear(); 194 VLOG(1) << "nacl_helper: child_pid is " << child_pid; 195 196 // Now send child_pid (eventually -1 if fork failed) to the Chrome Zygote. 197 output_pickle->WriteInt(child_pid); 198 return true; 199 } 200 201 bool HandleGetTerminationStatusRequest(PickleIterator* input_iter, 202 Pickle* output_pickle) { 203 pid_t child_to_wait; 204 if (!input_iter->ReadInt(&child_to_wait)) { 205 LOG(ERROR) << "Could not read pid to wait for"; 206 return false; 207 } 208 209 bool known_dead; 210 if (!input_iter->ReadBool(&known_dead)) { 211 LOG(ERROR) << "Could not read known_dead status"; 212 return false; 213 } 214 // TODO(jln): With NaCl, known_dead seems to never be set to true (unless 215 // called from the Zygote's kZygoteCommandReap command). This means that we 216 // will sometimes detect the process as still running when it's not. Fix 217 // this! 218 219 int exit_code; 220 base::TerminationStatus status; 221 if (known_dead) 222 status = base::GetKnownDeadTerminationStatus(child_to_wait, &exit_code); 223 else 224 status = base::GetTerminationStatus(child_to_wait, &exit_code); 225 output_pickle->WriteInt(static_cast<int>(status)); 226 output_pickle->WriteInt(exit_code); 227 return true; 228 } 229 230 // Honor a command |command_type|. Eventual command parameters are 231 // available in |input_iter| and eventual file descriptors attached to 232 // the command are in |attached_fds|. 233 // Reply to the command on |reply_fds|. 234 bool HonorRequestAndReply(int reply_fd, 235 int command_type, 236 ScopedVector<base::ScopedFD> attached_fds, 237 const NaClLoaderSystemInfo& system_info, 238 nacl::NaClSandbox* nacl_sandbox, 239 PickleIterator* input_iter) { 240 Pickle write_pickle; 241 bool have_to_reply = false; 242 // Commands must write anything to send back to |write_pickle|. 243 switch (command_type) { 244 case nacl::kNaClForkRequest: 245 have_to_reply = HandleForkRequest(attached_fds.Pass(), 246 system_info, 247 nacl_sandbox, 248 input_iter, 249 &write_pickle); 250 break; 251 case nacl::kNaClGetTerminationStatusRequest: 252 have_to_reply = 253 HandleGetTerminationStatusRequest(input_iter, &write_pickle); 254 break; 255 default: 256 LOG(ERROR) << "Unsupported command from Zygote"; 257 return false; 258 } 259 if (!have_to_reply) 260 return false; 261 const std::vector<int> empty; // We never send file descriptors back. 262 if (!UnixDomainSocket::SendMsg(reply_fd, write_pickle.data(), 263 write_pickle.size(), empty)) { 264 LOG(ERROR) << "*** send() to zygote failed"; 265 return false; 266 } 267 return true; 268 } 269 270 // Read a request from the Zygote from |zygote_ipc_fd| and handle it. 271 // Die on EOF from |zygote_ipc_fd|. 272 bool HandleZygoteRequest(int zygote_ipc_fd, 273 const NaClLoaderSystemInfo& system_info, 274 nacl::NaClSandbox* nacl_sandbox) { 275 ScopedVector<base::ScopedFD> fds; 276 char buf[kNaClMaxIPCMessageLength]; 277 const ssize_t msglen = UnixDomainSocket::RecvMsg(zygote_ipc_fd, 278 &buf, sizeof(buf), &fds); 279 // If the Zygote has started handling requests, we should be sandboxed via 280 // the setuid sandbox. 281 if (!nacl_sandbox->layer_one_enabled()) { 282 LOG(ERROR) << "NaCl helper process running without a sandbox!\n" 283 << "Most likely you need to configure your SUID sandbox " 284 << "correctly"; 285 } 286 if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) { 287 // EOF from the browser. Goodbye! 288 _exit(0); 289 } 290 if (msglen < 0) { 291 PLOG(ERROR) << "nacl_helper: receive from zygote failed"; 292 return false; 293 } 294 295 Pickle read_pickle(buf, msglen); 296 PickleIterator read_iter(read_pickle); 297 int command_type; 298 if (!read_iter.ReadInt(&command_type)) { 299 LOG(ERROR) << "Unable to read command from Zygote"; 300 return false; 301 } 302 return HonorRequestAndReply(zygote_ipc_fd, 303 command_type, 304 fds.Pass(), 305 system_info, 306 nacl_sandbox, 307 &read_iter); 308 } 309 310 static const char kNaClHelperReservedAtZero[] = "reserved_at_zero"; 311 static const char kNaClHelperRDebug[] = "r_debug"; 312 313 // Since we were started by nacl_helper_bootstrap rather than in the 314 // usual way, the debugger cannot figure out where our executable 315 // or the dynamic linker or the shared libraries are in memory, 316 // so it won't find any symbols. But we can fake it out to find us. 317 // 318 // The zygote passes --r_debug=0xXXXXXXXXXXXXXXXX. 319 // nacl_helper_bootstrap replaces the Xs with the address of its _r_debug 320 // structure. The debugger will look for that symbol by name to 321 // discover the addresses of key dynamic linker data structures. 322 // Since all it knows about is the original main executable, which 323 // is the bootstrap program, it finds the symbol defined there. The 324 // dynamic linker's structure is somewhere else, but it is filled in 325 // after initialization. The parts that really matter to the 326 // debugger never change. So we just copy the contents of the 327 // dynamic linker's structure into the address provided by the option. 328 // Hereafter, if someone attaches a debugger (or examines a core dump), 329 // the debugger will find all the symbols in the normal way. 330 static void CheckRDebug(char* argv0) { 331 std::string r_debug_switch_value = 332 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kNaClHelperRDebug); 333 if (!r_debug_switch_value.empty()) { 334 char* endp; 335 uintptr_t r_debug_addr = strtoul(r_debug_switch_value.c_str(), &endp, 0); 336 if (r_debug_addr != 0 && *endp == '\0') { 337 r_debug* bootstrap_r_debug = reinterpret_cast<r_debug*>(r_debug_addr); 338 *bootstrap_r_debug = _r_debug; 339 340 // Since the main executable (the bootstrap program) does not 341 // have a dynamic section, the debugger will not skip the 342 // first element of the link_map list as it usually would for 343 // an executable or PIE that was loaded normally. But the 344 // dynamic linker has set l_name for the PIE to "" as is 345 // normal for the main executable. So the debugger doesn't 346 // know which file it is. Fill in the actual file name, which 347 // came in as our argv[0]. 348 link_map* l = _r_debug.r_map; 349 if (l->l_name[0] == '\0') 350 l->l_name = argv0; 351 } 352 } 353 } 354 355 // The zygote passes --reserved_at_zero=0xXXXXXXXXXXXXXXXX. 356 // nacl_helper_bootstrap replaces the Xs with the amount of prereserved 357 // sandbox memory. 358 // 359 // CheckReservedAtZero parses the value of the argument reserved_at_zero 360 // and returns the amount of prereserved sandbox memory. 361 static size_t CheckReservedAtZero() { 362 size_t prereserved_sandbox_size = 0; 363 std::string reserved_at_zero_switch_value = 364 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 365 kNaClHelperReservedAtZero); 366 if (!reserved_at_zero_switch_value.empty()) { 367 char* endp; 368 prereserved_sandbox_size = 369 strtoul(reserved_at_zero_switch_value.c_str(), &endp, 0); 370 if (*endp != '\0') 371 LOG(ERROR) << "Could not parse reserved_at_zero argument value of " 372 << reserved_at_zero_switch_value; 373 } 374 return prereserved_sandbox_size; 375 } 376 377 } // namespace 378 379 #if defined(ADDRESS_SANITIZER) 380 // Do not install the SIGSEGV handler in ASan. This should make the NaCl 381 // platform qualification test pass. 382 static const char kAsanDefaultOptionsNaCl[] = "handle_segv=0"; 383 384 // Override the default ASan options for the NaCl helper. 385 // __asan_default_options should not be instrumented, because it is called 386 // before ASan is initialized. 387 extern "C" 388 __attribute__((no_sanitize_address)) 389 // The function isn't referenced from the executable itself. Make sure it isn't 390 // stripped by the linker. 391 __attribute__((used)) 392 __attribute__((visibility("default"))) 393 const char* __asan_default_options() { 394 return kAsanDefaultOptionsNaCl; 395 } 396 #endif 397 398 int main(int argc, char* argv[]) { 399 CommandLine::Init(argc, argv); 400 base::AtExitManager exit_manager; 401 base::RandUint64(); // acquire /dev/urandom fd before sandbox is raised 402 // Allows NSS to fopen() /dev/urandom. 403 sandbox::InitLibcUrandomOverrides(); 404 #if defined(USE_NSS) 405 // Configure NSS for use inside the NaCl process. 406 // The fork check has not caused problems for NaCl, but this appears to be 407 // best practice (see other places LoadNSSLibraries is called.) 408 crypto::DisableNSSForkCheck(); 409 // Without this line on Linux, HMAC::Init will instantiate a singleton that 410 // in turn attempts to open a file. Disabling this behavior avoids a ~70 ms 411 // stall the first time HMAC is used. 412 crypto::ForceNSSNoDBInit(); 413 // Load shared libraries before sandbox is raised. 414 // NSS is needed to perform hashing for validation caching. 415 crypto::LoadNSSLibraries(); 416 #endif 417 const NaClLoaderSystemInfo system_info = { 418 CheckReservedAtZero(), 419 sysconf(_SC_NPROCESSORS_ONLN) 420 }; 421 422 CheckRDebug(argv[0]); 423 424 scoped_ptr<nacl::NaClSandbox> nacl_sandbox(new nacl::NaClSandbox); 425 // Make sure that the early initialization did not start any spurious 426 // threads. 427 #if !defined(THREAD_SANITIZER) 428 CHECK(nacl_sandbox->IsSingleThreaded()); 429 #endif 430 431 const bool is_init_process = 1 == getpid(); 432 nacl_sandbox->InitializeLayerOneSandbox(); 433 CHECK_EQ(is_init_process, nacl_sandbox->layer_one_enabled()); 434 435 const std::vector<int> empty; 436 // Send the zygote a message to let it know we are ready to help 437 if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor, 438 kNaClHelperStartupAck, 439 sizeof(kNaClHelperStartupAck), empty)) { 440 LOG(ERROR) << "*** send() to zygote failed"; 441 } 442 443 // Now handle requests from the Zygote. 444 while (true) { 445 bool request_handled = HandleZygoteRequest( 446 kNaClZygoteDescriptor, system_info, nacl_sandbox.get()); 447 // Do not turn this into a CHECK() without thinking about robustness 448 // against malicious IPC requests. 449 DCHECK(request_handled); 450 } 451 NOTREACHED(); 452 } 453