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