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/browser/nacl_process_host.h" 6 7 #include <algorithm> 8 #include <string> 9 #include <vector> 10 11 #include "base/base_switches.h" 12 #include "base/bind.h" 13 #include "base/command_line.h" 14 #include "base/file_util.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/metrics/histogram.h" 17 #include "base/path_service.h" 18 #include "base/process/launch.h" 19 #include "base/process/process_iterator.h" 20 #include "base/rand_util.h" 21 #include "base/strings/string_number_conversions.h" 22 #include "base/strings/string_split.h" 23 #include "base/strings/string_util.h" 24 #include "base/strings/stringprintf.h" 25 #include "base/strings/utf_string_conversions.h" 26 #include "base/threading/sequenced_worker_pool.h" 27 #include "base/win/windows_version.h" 28 #include "build/build_config.h" 29 #include "components/nacl/browser/nacl_browser.h" 30 #include "components/nacl/browser/nacl_host_message_filter.h" 31 #include "components/nacl/common/nacl_cmd_line.h" 32 #include "components/nacl/common/nacl_host_messages.h" 33 #include "components/nacl/common/nacl_messages.h" 34 #include "components/nacl/common/nacl_process_type.h" 35 #include "components/nacl/common/nacl_switches.h" 36 #include "content/public/browser/browser_child_process_host.h" 37 #include "content/public/browser/browser_ppapi_host.h" 38 #include "content/public/browser/child_process_data.h" 39 #include "content/public/browser/plugin_service.h" 40 #include "content/public/common/child_process_host.h" 41 #include "content/public/common/content_switches.h" 42 #include "content/public/common/process_type.h" 43 #include "ipc/ipc_channel.h" 44 #include "ipc/ipc_switches.h" 45 #include "native_client/src/shared/imc/nacl_imc_c.h" 46 #include "net/base/net_util.h" 47 #include "net/socket/tcp_listen_socket.h" 48 #include "ppapi/host/host_factory.h" 49 #include "ppapi/host/ppapi_host.h" 50 #include "ppapi/proxy/ppapi_messages.h" 51 #include "ppapi/shared_impl/ppapi_nacl_channel_args.h" 52 53 #if defined(OS_POSIX) 54 #include <fcntl.h> 55 56 #include "ipc/ipc_channel_posix.h" 57 #elif defined(OS_WIN) 58 #include <windows.h> 59 60 #include "base/threading/thread.h" 61 #include "base/win/scoped_handle.h" 62 #include "components/nacl/browser/nacl_broker_service_win.h" 63 #include "components/nacl/common/nacl_debug_exception_handler_win.h" 64 #include "content/public/common/sandbox_init.h" 65 #include "content/public/common/sandboxed_process_launcher_delegate.h" 66 #endif 67 68 using content::BrowserThread; 69 using content::ChildProcessData; 70 using content::ChildProcessHost; 71 using ppapi::proxy::SerializedHandle; 72 73 #if defined(OS_WIN) 74 75 namespace { 76 77 // Looks for the largest contiguous unallocated region of address 78 // space and returns it via |*out_addr| and |*out_size|. 79 void FindAddressSpace(base::ProcessHandle process, 80 char** out_addr, size_t* out_size) { 81 *out_addr = NULL; 82 *out_size = 0; 83 char* addr = 0; 84 while (true) { 85 MEMORY_BASIC_INFORMATION info; 86 size_t result = VirtualQueryEx(process, static_cast<void*>(addr), 87 &info, sizeof(info)); 88 if (result < sizeof(info)) 89 break; 90 if (info.State == MEM_FREE && info.RegionSize > *out_size) { 91 *out_addr = addr; 92 *out_size = info.RegionSize; 93 } 94 addr += info.RegionSize; 95 } 96 } 97 98 } // namespace 99 100 namespace nacl { 101 102 // Allocates |size| bytes of address space in the given process at a 103 // randomised address. 104 void* AllocateAddressSpaceASLR(base::ProcessHandle process, size_t size) { 105 char* addr; 106 size_t avail_size; 107 FindAddressSpace(process, &addr, &avail_size); 108 if (avail_size < size) 109 return NULL; 110 size_t offset = base::RandGenerator(avail_size - size); 111 const int kPageSize = 0x10000; 112 void* request_addr = 113 reinterpret_cast<void*>(reinterpret_cast<uint64>(addr + offset) 114 & ~(kPageSize - 1)); 115 return VirtualAllocEx(process, request_addr, size, 116 MEM_RESERVE, PAGE_NOACCESS); 117 } 118 119 } // namespace nacl 120 121 #endif // defined(OS_WIN) 122 123 namespace { 124 125 #if defined(OS_WIN) 126 bool RunningOnWOW64() { 127 return (base::win::OSInfo::GetInstance()->wow64_status() == 128 base::win::OSInfo::WOW64_ENABLED); 129 } 130 131 // NOTE: changes to this class need to be reviewed by the security team. 132 class NaClSandboxedProcessLauncherDelegate 133 : public content::SandboxedProcessLauncherDelegate { 134 public: 135 NaClSandboxedProcessLauncherDelegate() {} 136 virtual ~NaClSandboxedProcessLauncherDelegate() {} 137 138 virtual void PostSpawnTarget(base::ProcessHandle process) { 139 // For Native Client sel_ldr processes on 32-bit Windows, reserve 1 GB of 140 // address space to prevent later failure due to address space fragmentation 141 // from .dll loading. The NaCl process will attempt to locate this space by 142 // scanning the address space using VirtualQuery. 143 // TODO(bbudge) Handle the --no-sandbox case. 144 // http://code.google.com/p/nativeclient/issues/detail?id=2131 145 const SIZE_T kNaClSandboxSize = 1 << 30; 146 if (!nacl::AllocateAddressSpaceASLR(process, kNaClSandboxSize)) { 147 DLOG(WARNING) << "Failed to reserve address space for Native Client"; 148 } 149 } 150 }; 151 152 #endif // OS_WIN 153 154 void SetCloseOnExec(NaClHandle fd) { 155 #if defined(OS_POSIX) 156 int flags = fcntl(fd, F_GETFD); 157 CHECK_NE(flags, -1); 158 int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC); 159 CHECK_EQ(rc, 0); 160 #endif 161 } 162 163 bool ShareHandleToSelLdr( 164 base::ProcessHandle processh, 165 NaClHandle sourceh, 166 bool close_source, 167 std::vector<nacl::FileDescriptor> *handles_for_sel_ldr) { 168 #if defined(OS_WIN) 169 HANDLE channel; 170 int flags = DUPLICATE_SAME_ACCESS; 171 if (close_source) 172 flags |= DUPLICATE_CLOSE_SOURCE; 173 if (!DuplicateHandle(GetCurrentProcess(), 174 reinterpret_cast<HANDLE>(sourceh), 175 processh, 176 &channel, 177 0, // Unused given DUPLICATE_SAME_ACCESS. 178 FALSE, 179 flags)) { 180 LOG(ERROR) << "DuplicateHandle() failed"; 181 return false; 182 } 183 handles_for_sel_ldr->push_back( 184 reinterpret_cast<nacl::FileDescriptor>(channel)); 185 #else 186 nacl::FileDescriptor channel; 187 channel.fd = sourceh; 188 channel.auto_close = close_source; 189 handles_for_sel_ldr->push_back(channel); 190 #endif 191 return true; 192 } 193 194 ppapi::PpapiPermissions GetNaClPermissions(uint32 permission_bits) { 195 // Only allow NaCl plugins to request certain permissions. We don't want 196 // a compromised renderer to be able to start a nacl plugin with e.g. Flash 197 // permissions which may expand the surface area of the sandbox. 198 uint32 masked_bits = permission_bits & ppapi::PERMISSION_DEV; 199 return ppapi::PpapiPermissions::GetForCommandLine(masked_bits); 200 } 201 202 } // namespace 203 204 namespace nacl { 205 206 struct NaClProcessHost::NaClInternal { 207 NaClHandle socket_for_renderer; 208 NaClHandle socket_for_sel_ldr; 209 210 NaClInternal() 211 : socket_for_renderer(NACL_INVALID_HANDLE), 212 socket_for_sel_ldr(NACL_INVALID_HANDLE) { } 213 }; 214 215 // ----------------------------------------------------------------------------- 216 217 NaClProcessHost::PluginListener::PluginListener(NaClProcessHost* host) 218 : host_(host) { 219 } 220 221 bool NaClProcessHost::PluginListener::OnMessageReceived( 222 const IPC::Message& msg) { 223 return host_->OnUntrustedMessageForwarded(msg); 224 } 225 226 NaClProcessHost::NaClProcessHost(const GURL& manifest_url, 227 int render_view_id, 228 uint32 permission_bits, 229 bool uses_irt, 230 bool enable_dyncode_syscalls, 231 bool enable_exception_handling, 232 bool enable_crash_throttling, 233 bool off_the_record, 234 const base::FilePath& profile_directory) 235 : manifest_url_(manifest_url), 236 permissions_(GetNaClPermissions(permission_bits)), 237 #if defined(OS_WIN) 238 process_launched_by_broker_(false), 239 #endif 240 reply_msg_(NULL), 241 #if defined(OS_WIN) 242 debug_exception_handler_requested_(false), 243 #endif 244 internal_(new NaClInternal()), 245 weak_factory_(this), 246 uses_irt_(uses_irt), 247 enable_debug_stub_(false), 248 enable_dyncode_syscalls_(enable_dyncode_syscalls), 249 enable_exception_handling_(enable_exception_handling), 250 enable_crash_throttling_(enable_crash_throttling), 251 off_the_record_(off_the_record), 252 profile_directory_(profile_directory), 253 ipc_plugin_listener_(this), 254 render_view_id_(render_view_id) { 255 process_.reset(content::BrowserChildProcessHost::Create( 256 PROCESS_TYPE_NACL_LOADER, this)); 257 258 // Set the display name so the user knows what plugin the process is running. 259 // We aren't on the UI thread so getting the pref locale for language 260 // formatting isn't possible, so IDN will be lost, but this is probably OK 261 // for this use case. 262 process_->SetName(net::FormatUrl(manifest_url_, std::string())); 263 264 enable_debug_stub_ = CommandLine::ForCurrentProcess()->HasSwitch( 265 switches::kEnableNaClDebug); 266 } 267 268 NaClProcessHost::~NaClProcessHost() { 269 // Report exit status only if the process was successfully started. 270 if (process_->GetData().handle != base::kNullProcessHandle) { 271 int exit_code = 0; 272 process_->GetTerminationStatus(false /* known_dead */, &exit_code); 273 std::string message = 274 base::StringPrintf("NaCl process exited with status %i (0x%x)", 275 exit_code, exit_code); 276 if (exit_code == 0) { 277 VLOG(1) << message; 278 } else { 279 LOG(ERROR) << message; 280 } 281 } 282 283 if (internal_->socket_for_renderer != NACL_INVALID_HANDLE) { 284 if (NaClClose(internal_->socket_for_renderer) != 0) { 285 NOTREACHED() << "NaClClose() failed"; 286 } 287 } 288 289 if (internal_->socket_for_sel_ldr != NACL_INVALID_HANDLE) { 290 if (NaClClose(internal_->socket_for_sel_ldr) != 0) { 291 NOTREACHED() << "NaClClose() failed"; 292 } 293 } 294 295 if (reply_msg_) { 296 // The process failed to launch for some reason. 297 // Don't keep the renderer hanging. 298 reply_msg_->set_reply_error(); 299 nacl_host_message_filter_->Send(reply_msg_); 300 } 301 #if defined(OS_WIN) 302 if (process_launched_by_broker_) { 303 NaClBrokerService::GetInstance()->OnLoaderDied(); 304 } 305 #endif 306 } 307 308 void NaClProcessHost::OnProcessCrashed(int exit_status) { 309 if (enable_crash_throttling_ && 310 !CommandLine::ForCurrentProcess()->HasSwitch( 311 switches::kDisablePnaclCrashThrottling)) { 312 NaClBrowser::GetInstance()->OnProcessCrashed(); 313 } 314 } 315 316 // This is called at browser startup. 317 // static 318 void NaClProcessHost::EarlyStartup() { 319 NaClBrowser::GetInstance()->EarlyStartup(); 320 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 321 // Open the IRT file early to make sure that it isn't replaced out from 322 // under us by autoupdate. 323 NaClBrowser::GetInstance()->EnsureIrtAvailable(); 324 #endif 325 CommandLine* cmd = CommandLine::ForCurrentProcess(); 326 UMA_HISTOGRAM_BOOLEAN( 327 "NaCl.nacl-gdb", 328 !cmd->GetSwitchValuePath(switches::kNaClGdb).empty()); 329 UMA_HISTOGRAM_BOOLEAN( 330 "NaCl.nacl-gdb-script", 331 !cmd->GetSwitchValuePath(switches::kNaClGdbScript).empty()); 332 UMA_HISTOGRAM_BOOLEAN( 333 "NaCl.enable-nacl-debug", 334 cmd->HasSwitch(switches::kEnableNaClDebug)); 335 NaClBrowser::GetDelegate()->SetDebugPatterns( 336 cmd->GetSwitchValueASCII(switches::kNaClDebugMask)); 337 } 338 339 void NaClProcessHost::Launch( 340 NaClHostMessageFilter* nacl_host_message_filter, 341 IPC::Message* reply_msg, 342 const base::FilePath& manifest_path) { 343 nacl_host_message_filter_ = nacl_host_message_filter; 344 reply_msg_ = reply_msg; 345 manifest_path_ = manifest_path; 346 347 // Do not launch the requested NaCl module if NaCl is marked "unstable" due 348 // to too many crashes within a given time period. 349 if (enable_crash_throttling_ && 350 !CommandLine::ForCurrentProcess()->HasSwitch( 351 switches::kDisablePnaclCrashThrottling) && 352 NaClBrowser::GetInstance()->IsThrottled()) { 353 SendErrorToRenderer("Process creation was throttled due to excessive" 354 " crashes"); 355 delete this; 356 return; 357 } 358 359 const CommandLine* cmd = CommandLine::ForCurrentProcess(); 360 #if defined(OS_WIN) 361 if (cmd->HasSwitch(switches::kEnableNaClDebug) && 362 !cmd->HasSwitch(switches::kNoSandbox)) { 363 // We don't switch off sandbox automatically for security reasons. 364 SendErrorToRenderer("NaCl's GDB debug stub requires --no-sandbox flag" 365 " on Windows. See crbug.com/265624."); 366 delete this; 367 return; 368 } 369 #endif 370 if (cmd->HasSwitch(switches::kNaClGdb) && 371 !cmd->HasSwitch(switches::kEnableNaClDebug)) { 372 LOG(WARNING) << "--nacl-gdb flag requires --enable-nacl-debug flag"; 373 } 374 375 // Start getting the IRT open asynchronously while we launch the NaCl process. 376 // We'll make sure this actually finished in StartWithLaunchedProcess, below. 377 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); 378 nacl_browser->EnsureAllResourcesAvailable(); 379 if (!nacl_browser->IsOk()) { 380 SendErrorToRenderer("could not find all the resources needed" 381 " to launch the process"); 382 delete this; 383 return; 384 } 385 386 // Rather than creating a socket pair in the renderer, and passing 387 // one side through the browser to sel_ldr, socket pairs are created 388 // in the browser and then passed to the renderer and sel_ldr. 389 // 390 // This is mainly for the benefit of Windows, where sockets cannot 391 // be passed in messages, but are copied via DuplicateHandle(). 392 // This means the sandboxed renderer cannot send handles to the 393 // browser process. 394 395 NaClHandle pair[2]; 396 // Create a connected socket 397 if (NaClSocketPair(pair) == -1) { 398 SendErrorToRenderer("NaClSocketPair() failed"); 399 delete this; 400 return; 401 } 402 internal_->socket_for_renderer = pair[0]; 403 internal_->socket_for_sel_ldr = pair[1]; 404 SetCloseOnExec(pair[0]); 405 SetCloseOnExec(pair[1]); 406 407 // Launch the process 408 if (!LaunchSelLdr()) { 409 delete this; 410 } 411 } 412 413 void NaClProcessHost::OnChannelConnected(int32 peer_pid) { 414 if (!CommandLine::ForCurrentProcess()->GetSwitchValuePath( 415 switches::kNaClGdb).empty()) { 416 LaunchNaClGdb(); 417 } 418 } 419 420 #if defined(OS_WIN) 421 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) { 422 process_launched_by_broker_ = true; 423 process_->SetHandle(handle); 424 if (!StartWithLaunchedProcess()) 425 delete this; 426 } 427 428 void NaClProcessHost::OnDebugExceptionHandlerLaunchedByBroker(bool success) { 429 IPC::Message* reply = attach_debug_exception_handler_reply_msg_.release(); 430 NaClProcessMsg_AttachDebugExceptionHandler::WriteReplyParams(reply, success); 431 Send(reply); 432 } 433 #endif 434 435 // Needed to handle sync messages in OnMessageRecieved. 436 bool NaClProcessHost::Send(IPC::Message* msg) { 437 return process_->Send(msg); 438 } 439 440 bool NaClProcessHost::LaunchNaClGdb() { 441 #if defined(OS_WIN) 442 base::FilePath nacl_gdb = 443 CommandLine::ForCurrentProcess()->GetSwitchValuePath(switches::kNaClGdb); 444 CommandLine cmd_line(nacl_gdb); 445 #else 446 CommandLine::StringType nacl_gdb = 447 CommandLine::ForCurrentProcess()->GetSwitchValueNative( 448 switches::kNaClGdb); 449 CommandLine::StringVector argv; 450 // We don't support spaces inside arguments in --nacl-gdb switch. 451 base::SplitString(nacl_gdb, static_cast<CommandLine::CharType>(' '), &argv); 452 CommandLine cmd_line(argv); 453 #endif 454 cmd_line.AppendArg("--eval-command"); 455 base::FilePath::StringType irt_path( 456 NaClBrowser::GetInstance()->GetIrtFilePath().value()); 457 // Avoid back slashes because nacl-gdb uses posix escaping rules on Windows. 458 // See issue https://code.google.com/p/nativeclient/issues/detail?id=3482. 459 std::replace(irt_path.begin(), irt_path.end(), '\\', '/'); 460 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-irt \"") + irt_path + 461 FILE_PATH_LITERAL("\"")); 462 if (!manifest_path_.empty()) { 463 cmd_line.AppendArg("--eval-command"); 464 base::FilePath::StringType manifest_path_value(manifest_path_.value()); 465 std::replace(manifest_path_value.begin(), manifest_path_value.end(), 466 '\\', '/'); 467 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-manifest \"") + 468 manifest_path_value + FILE_PATH_LITERAL("\"")); 469 } 470 cmd_line.AppendArg("--eval-command"); 471 cmd_line.AppendArg("target remote :4014"); 472 base::FilePath script = CommandLine::ForCurrentProcess()->GetSwitchValuePath( 473 switches::kNaClGdbScript); 474 if (!script.empty()) { 475 cmd_line.AppendArg("--command"); 476 cmd_line.AppendArgNative(script.value()); 477 } 478 return base::LaunchProcess(cmd_line, base::LaunchOptions(), NULL); 479 } 480 481 bool NaClProcessHost::LaunchSelLdr() { 482 std::string channel_id = process_->GetHost()->CreateChannel(); 483 if (channel_id.empty()) { 484 SendErrorToRenderer("CreateChannel() failed"); 485 return false; 486 } 487 488 CommandLine::StringType nacl_loader_prefix; 489 #if defined(OS_POSIX) 490 nacl_loader_prefix = CommandLine::ForCurrentProcess()->GetSwitchValueNative( 491 switches::kNaClLoaderCmdPrefix); 492 #endif // defined(OS_POSIX) 493 494 // Build command line for nacl. 495 496 #if defined(OS_MACOSX) 497 // The Native Client process needs to be able to allocate a 1GB contiguous 498 // region to use as the client environment's virtual address space. ASLR 499 // (PIE) interferes with this by making it possible that no gap large enough 500 // to accomodate this request will exist in the child process' address 501 // space. Disable PIE for NaCl processes. See http://crbug.com/90221 and 502 // http://code.google.com/p/nativeclient/issues/detail?id=2043. 503 int flags = ChildProcessHost::CHILD_NO_PIE; 504 #elif defined(OS_LINUX) 505 int flags = nacl_loader_prefix.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : 506 ChildProcessHost::CHILD_NORMAL; 507 #else 508 int flags = ChildProcessHost::CHILD_NORMAL; 509 #endif 510 511 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); 512 if (exe_path.empty()) 513 return false; 514 515 #if defined(OS_WIN) 516 // On Windows 64-bit NaCl loader is called nacl64.exe instead of chrome.exe 517 if (RunningOnWOW64()) { 518 if (!NaClBrowser::GetInstance()->GetNaCl64ExePath(&exe_path)) { 519 SendErrorToRenderer("could not get path to nacl64.exe"); 520 return false; 521 } 522 } 523 #endif 524 525 scoped_ptr<CommandLine> cmd_line(new CommandLine(exe_path)); 526 CopyNaClCommandLineArguments(cmd_line.get()); 527 528 cmd_line->AppendSwitchASCII(switches::kProcessType, 529 switches::kNaClLoaderProcess); 530 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); 531 if (NaClBrowser::GetDelegate()->DialogsAreSuppressed()) 532 cmd_line->AppendSwitch(switches::kNoErrorDialogs); 533 534 if (!nacl_loader_prefix.empty()) 535 cmd_line->PrependWrapper(nacl_loader_prefix); 536 537 // On Windows we might need to start the broker process to launch a new loader 538 #if defined(OS_WIN) 539 if (RunningOnWOW64()) { 540 if (!NaClBrokerService::GetInstance()->LaunchLoader( 541 weak_factory_.GetWeakPtr(), channel_id)) { 542 SendErrorToRenderer("broker service did not launch process"); 543 return false; 544 } 545 } else { 546 process_->Launch(new NaClSandboxedProcessLauncherDelegate, 547 cmd_line.release()); 548 } 549 #elif defined(OS_POSIX) 550 process_->Launch(nacl_loader_prefix.empty(), // use_zygote 551 base::EnvironmentMap(), 552 cmd_line.release()); 553 #endif 554 555 return true; 556 } 557 558 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) { 559 bool handled = true; 560 IPC_BEGIN_MESSAGE_MAP(NaClProcessHost, msg) 561 IPC_MESSAGE_HANDLER(NaClProcessMsg_QueryKnownToValidate, 562 OnQueryKnownToValidate) 563 IPC_MESSAGE_HANDLER(NaClProcessMsg_SetKnownToValidate, 564 OnSetKnownToValidate) 565 IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClProcessMsg_ResolveFileToken, 566 OnResolveFileToken) 567 #if defined(OS_WIN) 568 IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClProcessMsg_AttachDebugExceptionHandler, 569 OnAttachDebugExceptionHandler) 570 #endif 571 IPC_MESSAGE_HANDLER(NaClProcessHostMsg_PpapiChannelCreated, 572 OnPpapiChannelCreated) 573 IPC_MESSAGE_UNHANDLED(handled = false) 574 IPC_END_MESSAGE_MAP() 575 return handled; 576 } 577 578 void NaClProcessHost::OnProcessLaunched() { 579 if (!StartWithLaunchedProcess()) 580 delete this; 581 } 582 583 // Called when the NaClBrowser singleton has been fully initialized. 584 void NaClProcessHost::OnResourcesReady() { 585 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); 586 if (!nacl_browser->IsReady()) { 587 SendErrorToRenderer("could not acquire shared resources needed by NaCl"); 588 delete this; 589 } else if (!SendStart()) { 590 delete this; 591 } 592 } 593 594 bool NaClProcessHost::ReplyToRenderer( 595 const IPC::ChannelHandle& channel_handle) { 596 #if defined(OS_WIN) 597 // If we are on 64-bit Windows, the NaCl process's sandbox is 598 // managed by a different process from the renderer's sandbox. We 599 // need to inform the renderer's sandbox about the NaCl process so 600 // that the renderer can send handles to the NaCl process using 601 // BrokerDuplicateHandle(). 602 if (RunningOnWOW64()) { 603 if (!content::BrokerAddTargetPeer(process_->GetData().handle)) { 604 SendErrorToRenderer("BrokerAddTargetPeer() failed"); 605 return false; 606 } 607 } 608 #endif 609 610 FileDescriptor handle_for_renderer; 611 #if defined(OS_WIN) 612 // Copy the handle into the renderer process. 613 HANDLE handle_in_renderer; 614 if (!DuplicateHandle(base::GetCurrentProcessHandle(), 615 reinterpret_cast<HANDLE>( 616 internal_->socket_for_renderer), 617 nacl_host_message_filter_->PeerHandle(), 618 &handle_in_renderer, 619 0, // Unused given DUPLICATE_SAME_ACCESS. 620 FALSE, 621 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { 622 SendErrorToRenderer("DuplicateHandle() failed"); 623 return false; 624 } 625 handle_for_renderer = reinterpret_cast<FileDescriptor>( 626 handle_in_renderer); 627 #else 628 // No need to dup the imc_handle - we don't pass it anywhere else so 629 // it cannot be closed. 630 FileDescriptor imc_handle; 631 imc_handle.fd = internal_->socket_for_renderer; 632 imc_handle.auto_close = true; 633 handle_for_renderer = imc_handle; 634 #endif 635 636 const ChildProcessData& data = process_->GetData(); 637 SendMessageToRenderer( 638 NaClLaunchResult(handle_for_renderer, 639 channel_handle, 640 base::GetProcId(data.handle), 641 data.id), 642 std::string() /* error_message */); 643 internal_->socket_for_renderer = NACL_INVALID_HANDLE; 644 return true; 645 } 646 647 void NaClProcessHost::SendErrorToRenderer(const std::string& error_message) { 648 LOG(ERROR) << "NaCl process launch failed: " << error_message; 649 SendMessageToRenderer(NaClLaunchResult(), error_message); 650 } 651 652 void NaClProcessHost::SendMessageToRenderer( 653 const NaClLaunchResult& result, 654 const std::string& error_message) { 655 DCHECK(nacl_host_message_filter_); 656 DCHECK(reply_msg_); 657 if (nacl_host_message_filter_ != NULL && reply_msg_ != NULL) { 658 NaClHostMsg_LaunchNaCl::WriteReplyParams( 659 reply_msg_, result, error_message); 660 nacl_host_message_filter_->Send(reply_msg_); 661 nacl_host_message_filter_ = NULL; 662 reply_msg_ = NULL; 663 } 664 } 665 666 // TCP port we chose for NaCl debug stub. It can be any other number. 667 static const int kDebugStubPort = 4014; 668 669 #if defined(OS_POSIX) 670 net::SocketDescriptor NaClProcessHost::GetDebugStubSocketHandle() { 671 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); 672 net::SocketDescriptor s = net::kInvalidSocket; 673 // We allocate currently unused TCP port for debug stub tests. The port 674 // number is passed to the test via debug stub port listener. 675 if (nacl_browser->HasGdbDebugStubPortListener()) { 676 int port; 677 s = net::TCPListenSocket::CreateAndBindAnyPort("127.0.0.1", &port); 678 if (s != net::kInvalidSocket) { 679 nacl_browser->FireGdbDebugStubPortOpened(port); 680 } 681 } else { 682 s = net::TCPListenSocket::CreateAndBind("127.0.0.1", kDebugStubPort); 683 } 684 if (s == net::kInvalidSocket) { 685 LOG(ERROR) << "failed to open socket for debug stub"; 686 return net::kInvalidSocket; 687 } 688 if (listen(s, 1)) { 689 LOG(ERROR) << "listen() failed on debug stub socket"; 690 if (IGNORE_EINTR(close(s)) < 0) 691 PLOG(ERROR) << "failed to close debug stub socket"; 692 return net::kInvalidSocket; 693 } 694 return s; 695 } 696 #endif 697 698 bool NaClProcessHost::StartNaClExecution() { 699 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); 700 701 NaClStartParams params; 702 params.validation_cache_enabled = nacl_browser->ValidationCacheIsEnabled(); 703 params.validation_cache_key = nacl_browser->GetValidationCacheKey(); 704 params.version = NaClBrowser::GetDelegate()->GetVersionString(); 705 params.enable_exception_handling = enable_exception_handling_; 706 params.enable_debug_stub = enable_debug_stub_ && 707 NaClBrowser::GetDelegate()->URLMatchesDebugPatterns(manifest_url_); 708 // Enable PPAPI proxy channel creation only for renderer processes. 709 params.enable_ipc_proxy = enable_ppapi_proxy(); 710 params.uses_irt = uses_irt_; 711 params.enable_dyncode_syscalls = enable_dyncode_syscalls_; 712 713 const ChildProcessData& data = process_->GetData(); 714 if (!ShareHandleToSelLdr(data.handle, 715 internal_->socket_for_sel_ldr, true, 716 ¶ms.handles)) { 717 return false; 718 } 719 720 if (params.uses_irt) { 721 base::PlatformFile irt_file = nacl_browser->IrtFile(); 722 CHECK_NE(irt_file, base::kInvalidPlatformFileValue); 723 // Send over the IRT file handle. We don't close our own copy! 724 if (!ShareHandleToSelLdr(data.handle, irt_file, false, ¶ms.handles)) 725 return false; 726 } 727 728 #if defined(OS_MACOSX) 729 // For dynamic loading support, NaCl requires a file descriptor that 730 // was created in /tmp, since those created with shm_open() are not 731 // mappable with PROT_EXEC. Rather than requiring an extra IPC 732 // round trip out of the sandbox, we create an FD here. 733 base::SharedMemory memory_buffer; 734 base::SharedMemoryCreateOptions options; 735 options.size = 1; 736 options.executable = true; 737 if (!memory_buffer.Create(options)) { 738 DLOG(ERROR) << "Failed to allocate memory buffer"; 739 return false; 740 } 741 FileDescriptor memory_fd; 742 memory_fd.fd = dup(memory_buffer.handle().fd); 743 if (memory_fd.fd < 0) { 744 DLOG(ERROR) << "Failed to dup() a file descriptor"; 745 return false; 746 } 747 memory_fd.auto_close = true; 748 params.handles.push_back(memory_fd); 749 #endif 750 751 #if defined(OS_POSIX) 752 if (params.enable_debug_stub) { 753 net::SocketDescriptor server_bound_socket = GetDebugStubSocketHandle(); 754 if (server_bound_socket != net::kInvalidSocket) { 755 params.debug_stub_server_bound_socket = 756 FileDescriptor(server_bound_socket, true); 757 } 758 } 759 #endif 760 761 process_->Send(new NaClProcessMsg_Start(params)); 762 763 internal_->socket_for_sel_ldr = NACL_INVALID_HANDLE; 764 return true; 765 } 766 767 bool NaClProcessHost::SendStart() { 768 if (!enable_ppapi_proxy()) { 769 if (!ReplyToRenderer(IPC::ChannelHandle())) 770 return false; 771 } 772 return StartNaClExecution(); 773 } 774 775 // This method is called when NaClProcessHostMsg_PpapiChannelCreated is 776 // received or PpapiHostMsg_ChannelCreated is forwarded by our plugin 777 // listener. 778 void NaClProcessHost::OnPpapiChannelCreated( 779 const IPC::ChannelHandle& channel_handle) { 780 // Only renderer processes should create a channel. 781 DCHECK(enable_ppapi_proxy()); 782 // If the proxy channel is null, this must be the initial NaCl-Browser IPC 783 // channel. 784 if (!ipc_proxy_channel_.get()) { 785 DCHECK_EQ(PROCESS_TYPE_NACL_LOADER, process_->GetData().process_type); 786 787 ipc_proxy_channel_.reset( 788 new IPC::ChannelProxy(channel_handle, 789 IPC::Channel::MODE_CLIENT, 790 &ipc_plugin_listener_, 791 base::MessageLoopProxy::current().get())); 792 // Create the browser ppapi host and enable PPAPI message dispatching to the 793 // browser process. 794 ppapi_host_.reset(content::BrowserPpapiHost::CreateExternalPluginProcess( 795 ipc_proxy_channel_.get(), // sender 796 permissions_, 797 process_->GetData().handle, 798 ipc_proxy_channel_.get(), 799 nacl_host_message_filter_->render_process_id(), 800 render_view_id_, 801 profile_directory_)); 802 803 ppapi::PpapiNaClChannelArgs args; 804 args.off_the_record = nacl_host_message_filter_->off_the_record(); 805 args.permissions = permissions_; 806 args.supports_dev_channel = 807 content::PluginService::GetInstance()->PpapiDevChannelSupported(); 808 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 809 DCHECK(cmdline); 810 std::string flag_whitelist[] = {switches::kV, switches::kVModule}; 811 for (size_t i = 0; i < arraysize(flag_whitelist); ++i) { 812 std::string value = cmdline->GetSwitchValueASCII(flag_whitelist[i]); 813 if (!value.empty()) { 814 args.switch_names.push_back(flag_whitelist[i]); 815 args.switch_values.push_back(value); 816 } 817 } 818 819 ppapi_host_->GetPpapiHost()->AddHostFactoryFilter( 820 scoped_ptr<ppapi::host::HostFactory>( 821 NaClBrowser::GetDelegate()->CreatePpapiHostFactory( 822 ppapi_host_.get()))); 823 824 // Send a message to create the NaCl-Renderer channel. The handle is just 825 // a place holder. 826 ipc_proxy_channel_->Send( 827 new PpapiMsg_CreateNaClChannel( 828 nacl_host_message_filter_->render_process_id(), 829 args, 830 SerializedHandle(SerializedHandle::CHANNEL_HANDLE, 831 IPC::InvalidPlatformFileForTransit()))); 832 } else if (reply_msg_) { 833 // Otherwise, this must be a renderer channel. 834 ReplyToRenderer(channel_handle); 835 } else { 836 // Attempt to open more than 1 renderer channel is not supported. 837 // Shut down the NaCl process. 838 process_->GetHost()->ForceShutdown(); 839 } 840 } 841 842 bool NaClProcessHost::OnUntrustedMessageForwarded(const IPC::Message& msg) { 843 // Handle messages that have been forwarded from our PluginListener. 844 // These messages come from untrusted code so should be handled with care. 845 bool handled = true; 846 IPC_BEGIN_MESSAGE_MAP(NaClProcessHost, msg) 847 IPC_MESSAGE_HANDLER(PpapiHostMsg_ChannelCreated, 848 OnPpapiChannelCreated) 849 IPC_MESSAGE_UNHANDLED(handled = false) 850 IPC_END_MESSAGE_MAP() 851 return handled; 852 } 853 854 bool NaClProcessHost::StartWithLaunchedProcess() { 855 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); 856 857 if (nacl_browser->IsReady()) { 858 return SendStart(); 859 } else if (nacl_browser->IsOk()) { 860 nacl_browser->WaitForResources( 861 base::Bind(&NaClProcessHost::OnResourcesReady, 862 weak_factory_.GetWeakPtr())); 863 return true; 864 } else { 865 SendErrorToRenderer("previously failed to acquire shared resources"); 866 return false; 867 } 868 } 869 870 void NaClProcessHost::OnQueryKnownToValidate(const std::string& signature, 871 bool* result) { 872 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); 873 *result = nacl_browser->QueryKnownToValidate(signature, off_the_record_); 874 } 875 876 void NaClProcessHost::OnSetKnownToValidate(const std::string& signature) { 877 NaClBrowser::GetInstance()->SetKnownToValidate( 878 signature, off_the_record_); 879 } 880 881 void NaClProcessHost::FileResolved( 882 const base::FilePath& file_path, 883 IPC::Message* reply_msg, 884 const base::PlatformFile& file) { 885 if (file != base::kInvalidPlatformFileValue) { 886 IPC::PlatformFileForTransit handle = IPC::GetFileHandleForProcess( 887 file, 888 process_->GetData().handle, 889 true /* close_source */); 890 NaClProcessMsg_ResolveFileToken::WriteReplyParams( 891 reply_msg, 892 handle, 893 file_path); 894 } else { 895 NaClProcessMsg_ResolveFileToken::WriteReplyParams( 896 reply_msg, 897 IPC::InvalidPlatformFileForTransit(), 898 base::FilePath()); 899 } 900 Send(reply_msg); 901 } 902 903 void NaClProcessHost::OnResolveFileToken(uint64 file_token_lo, 904 uint64 file_token_hi, 905 IPC::Message* reply_msg) { 906 // Was the file registered? 907 // 908 // Note that the file path cache is of bounded size, and old entries can get 909 // evicted. If a large number of NaCl modules are being launched at once, 910 // resolving the file_token may fail because the path cache was thrashed 911 // while the file_token was in flight. In this case the query fails, and we 912 // need to fall back to the slower path. 913 // 914 // However: each NaCl process will consume 2-3 entries as it starts up, this 915 // means that eviction will not happen unless you start up 33+ NaCl processes 916 // at the same time, and this still requires worst-case timing. As a 917 // practical matter, no entries should be evicted prematurely. 918 // The cache itself should take ~ (150 characters * 2 bytes/char + ~60 bytes 919 // data structure overhead) * 100 = 35k when full, so making it bigger should 920 // not be a problem, if needed. 921 // 922 // Each NaCl process will consume 2-3 entries because the manifest and main 923 // nexe are currently not resolved. Shared libraries will be resolved. They 924 // will be loaded sequentially, so they will only consume a single entry 925 // while the load is in flight. 926 // 927 // TODO(ncbray): track behavior with UMA. If entries are getting evicted or 928 // bogus keys are getting queried, this would be good to know. 929 base::FilePath file_path; 930 if (!NaClBrowser::GetInstance()->GetFilePath( 931 file_token_lo, file_token_hi, &file_path)) { 932 NaClProcessMsg_ResolveFileToken::WriteReplyParams( 933 reply_msg, 934 IPC::InvalidPlatformFileForTransit(), 935 base::FilePath()); 936 Send(reply_msg); 937 return; 938 } 939 940 // Open the file. 941 if (!base::PostTaskAndReplyWithResult( 942 content::BrowserThread::GetBlockingPool(), 943 FROM_HERE, 944 base::Bind(OpenNaClExecutableImpl, file_path), 945 base::Bind(&NaClProcessHost::FileResolved, 946 weak_factory_.GetWeakPtr(), 947 file_path, 948 reply_msg))) { 949 NaClProcessMsg_ResolveFileToken::WriteReplyParams( 950 reply_msg, 951 IPC::InvalidPlatformFileForTransit(), 952 base::FilePath()); 953 Send(reply_msg); 954 } 955 } 956 957 #if defined(OS_WIN) 958 void NaClProcessHost::OnAttachDebugExceptionHandler(const std::string& info, 959 IPC::Message* reply_msg) { 960 if (!AttachDebugExceptionHandler(info, reply_msg)) { 961 // Send failure message. 962 NaClProcessMsg_AttachDebugExceptionHandler::WriteReplyParams(reply_msg, 963 false); 964 Send(reply_msg); 965 } 966 } 967 968 bool NaClProcessHost::AttachDebugExceptionHandler(const std::string& info, 969 IPC::Message* reply_msg) { 970 if (!enable_exception_handling_ && !enable_debug_stub_) { 971 DLOG(ERROR) << 972 "Debug exception handler requested by NaCl process when not enabled"; 973 return false; 974 } 975 if (debug_exception_handler_requested_) { 976 // The NaCl process should not request this multiple times. 977 DLOG(ERROR) << "Multiple AttachDebugExceptionHandler requests received"; 978 return false; 979 } 980 debug_exception_handler_requested_ = true; 981 982 base::ProcessId nacl_pid = base::GetProcId(process_->GetData().handle); 983 base::ProcessHandle temp_handle; 984 // We cannot use process_->GetData().handle because it does not have 985 // the necessary access rights. We open the new handle here rather 986 // than in the NaCl broker process in case the NaCl loader process 987 // dies before the NaCl broker process receives the message we send. 988 // The debug exception handler uses DebugActiveProcess() to attach, 989 // but this takes a PID. We need to prevent the NaCl loader's PID 990 // from being reused before DebugActiveProcess() is called, and 991 // holding a process handle open achieves this. 992 if (!base::OpenProcessHandleWithAccess( 993 nacl_pid, 994 base::kProcessAccessQueryInformation | 995 base::kProcessAccessSuspendResume | 996 base::kProcessAccessTerminate | 997 base::kProcessAccessVMOperation | 998 base::kProcessAccessVMRead | 999 base::kProcessAccessVMWrite | 1000 base::kProcessAccessDuplicateHandle | 1001 base::kProcessAccessWaitForTermination, 1002 &temp_handle)) { 1003 LOG(ERROR) << "Failed to get process handle"; 1004 return false; 1005 } 1006 base::win::ScopedHandle process_handle(temp_handle); 1007 1008 attach_debug_exception_handler_reply_msg_.reset(reply_msg); 1009 // If the NaCl loader is 64-bit, the process running its debug 1010 // exception handler must be 64-bit too, so we use the 64-bit NaCl 1011 // broker process for this. Otherwise, on a 32-bit system, we use 1012 // the 32-bit browser process to run the debug exception handler. 1013 if (RunningOnWOW64()) { 1014 return NaClBrokerService::GetInstance()->LaunchDebugExceptionHandler( 1015 weak_factory_.GetWeakPtr(), nacl_pid, process_handle, info); 1016 } else { 1017 NaClStartDebugExceptionHandlerThread( 1018 process_handle.Take(), info, 1019 base::MessageLoopProxy::current(), 1020 base::Bind(&NaClProcessHost::OnDebugExceptionHandlerLaunchedByBroker, 1021 weak_factory_.GetWeakPtr())); 1022 return true; 1023 } 1024 } 1025 #endif 1026 1027 } // namespace nacl 1028