Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/nacl_host/nacl_process_host.h" | 5 #include "chrome/browser/nacl_host/nacl_process_host.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/memory/mru_cache.h" | 12 #include "base/memory/mru_cache.h" |
| 13 #include "base/memory/singleton.h" | 13 #include "base/memory/singleton.h" |
| 14 #include "base/message_loop.h" | |
| 14 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
| 15 #include "base/path_service.h" | 16 #include "base/path_service.h" |
| 17 #include "base/string_number_conversions.h" | |
| 18 #include "base/string_split.h" | |
| 16 #include "base/string_util.h" | 19 #include "base/string_util.h" |
| 17 #include "base/rand_util.h" | 20 #include "base/rand_util.h" |
| 18 #include "base/stringprintf.h" | 21 #include "base/stringprintf.h" |
| 19 #include "base/utf_string_conversions.h" | 22 #include "base/utf_string_conversions.h" |
| 20 #include "base/win/windows_version.h" | 23 #include "base/win/windows_version.h" |
| 21 #include "build/build_config.h" | 24 #include "build/build_config.h" |
| 22 #include "chrome/browser/extensions/extension_info_map.h" | 25 #include "chrome/browser/extensions/extension_info_map.h" |
| 23 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" | 26 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" |
| 24 #include "chrome/common/chrome_constants.h" | 27 #include "chrome/common/chrome_constants.h" |
| 25 #include "chrome/common/chrome_paths.h" | 28 #include "chrome/common/chrome_paths.h" |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 261 static bool RunningOnWOW64() { | 264 static bool RunningOnWOW64() { |
| 262 return (base::win::OSInfo::GetInstance()->wow64_status() == | 265 return (base::win::OSInfo::GetInstance()->wow64_status() == |
| 263 base::win::OSInfo::WOW64_ENABLED); | 266 base::win::OSInfo::WOW64_ENABLED); |
| 264 } | 267 } |
| 265 #endif | 268 #endif |
| 266 | 269 |
| 267 NaClProcessHost::NaClProcessHost(const std::wstring& url) | 270 NaClProcessHost::NaClProcessHost(const std::wstring& url) |
| 268 : | 271 : |
| 269 #if defined(OS_WIN) | 272 #if defined(OS_WIN) |
| 270 process_launched_by_broker_(false), | 273 process_launched_by_broker_(false), |
| 274 #elif defined(OS_LINUX) | |
| 275 wait_for_nacl_gdb_(false), | |
| 271 #endif | 276 #endif |
| 272 reply_msg_(NULL), | 277 reply_msg_(NULL), |
| 273 internal_(new NaClInternal()), | 278 internal_(new NaClInternal()), |
| 274 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 279 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| 275 enable_exception_handling_(false) { | 280 enable_exception_handling_(false) { |
| 276 process_.reset(content::BrowserChildProcessHost::Create( | 281 process_.reset(content::BrowserChildProcessHost::Create( |
| 277 content::PROCESS_TYPE_NACL_LOADER, this)); | 282 content::PROCESS_TYPE_NACL_LOADER, this)); |
| 278 process_->SetName(WideToUTF16Hack(url)); | 283 process_->SetName(WideToUTF16Hack(url)); |
| 279 | 284 |
| 280 // We allow untrusted hardware exception handling to be enabled via | 285 // We allow untrusted hardware exception handling to be enabled via |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 401 internal_->sockets_for_sel_ldr.push_back(pair[1]); | 406 internal_->sockets_for_sel_ldr.push_back(pair[1]); |
| 402 SetCloseOnExec(pair[0]); | 407 SetCloseOnExec(pair[0]); |
| 403 SetCloseOnExec(pair[1]); | 408 SetCloseOnExec(pair[1]); |
| 404 } | 409 } |
| 405 | 410 |
| 406 // Launch the process | 411 // Launch the process |
| 407 if (!LaunchSelLdr()) { | 412 if (!LaunchSelLdr()) { |
| 408 delete this; | 413 delete this; |
| 409 } | 414 } |
| 410 } | 415 } |
| 411 | 416 #if defined(OS_WIN) |
| 412 scoped_ptr<CommandLine> NaClProcessHost::LaunchWithNaClGdb( | 417 scoped_ptr<CommandLine> NaClProcessHost::LaunchWithNaClGdb( |
| 413 const FilePath& nacl_gdb, | 418 const FilePath& nacl_gdb, |
| 414 CommandLine* line, | 419 CommandLine* line) { |
| 415 const FilePath& manifest_path) { | |
| 416 CommandLine* cmd_line = new CommandLine(nacl_gdb); | 420 CommandLine* cmd_line = new CommandLine(nacl_gdb); |
| 417 // We can't use PrependWrapper because our parameters contain spaces. | 421 // We can't use PrependWrapper because our parameters contain spaces. |
| 418 cmd_line->AppendArg("--eval-command"); | 422 cmd_line->AppendArg("--eval-command"); |
| 419 const FilePath::StringType& irt_path = | 423 const FilePath::StringType& irt_path = |
| 420 NaClBrowser::GetInstance()->GetIrtFilePath().value(); | 424 NaClBrowser::GetInstance()->GetIrtFilePath().value(); |
| 421 cmd_line->AppendArgNative(FILE_PATH_LITERAL("nacl-irt ") + irt_path); | 425 cmd_line->AppendArgNative(FILE_PATH_LITERAL("nacl-irt ") + irt_path); |
| 426 FilePath manifest_path = GetManifestPath(); | |
| 422 if (!manifest_path.empty()) { | 427 if (!manifest_path.empty()) { |
| 423 cmd_line->AppendArg("--eval-command"); | 428 cmd_line->AppendArg("--eval-command"); |
| 424 cmd_line->AppendArgNative(FILE_PATH_LITERAL("nacl-manifest ") + | 429 cmd_line->AppendArgNative(FILE_PATH_LITERAL("nacl-manifest ") + |
| 425 manifest_path.value()); | 430 manifest_path.value()); |
| 426 } | 431 } |
| 427 cmd_line->AppendArg("--args"); | 432 cmd_line->AppendArg("--args"); |
| 428 const CommandLine::StringVector& argv = line->argv(); | 433 const CommandLine::StringVector& argv = line->argv(); |
| 429 for (size_t i = 0; i < argv.size(); i++) { | 434 for (size_t i = 0; i < argv.size(); i++) { |
| 430 cmd_line->AppendArgNative(argv[i]); | 435 cmd_line->AppendArgNative(argv[i]); |
| 431 } | 436 } |
| 432 return scoped_ptr<CommandLine>(cmd_line); | 437 return scoped_ptr<CommandLine>(cmd_line); |
| 433 } | 438 } |
| 439 #elif defined(OS_LINUX) | |
| 440 namespace { | |
| 441 class NaClGdbWatchDelegate : public MessageLoopForIO::Watcher { | |
| 442 public: | |
| 443 // fd_write_ is used by nacl-gdb via /proc/browser_PID/fd/fd_write_ | |
| 444 NaClGdbWatchDelegate(int fd_read, int fd_write, | |
| 445 const base::Closure& reply) | |
| 446 : fd_read_(fd_read), | |
| 447 fd_write_(fd_write), | |
| 448 reply_(reply) {} | |
| 449 | |
| 450 ~NaClGdbWatchDelegate() { | |
| 451 if (HANDLE_EINTR(close(fd_read_)) != 0) | |
| 452 DLOG(ERROR) << "close(fd_read_) failed"; | |
| 453 if (HANDLE_EINTR(close(fd_write_)) != 0) | |
| 454 DLOG(ERROR) << "close(fd_write_) failed"; | |
| 455 } | |
| 456 | |
| 457 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; | |
| 458 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {} | |
| 459 | |
| 460 private: | |
| 461 int fd_read_; | |
| 462 int fd_write_; | |
| 463 base::Closure reply_; | |
| 464 }; | |
| 465 | |
| 466 void NaClGdbWatchDelegate::OnFileCanReadWithoutBlocking(int fd) { | |
| 467 char buf; | |
| 468 if (HANDLE_EINTR(read(fd_read_, &buf, 1)) != 1 || buf != '\0') | |
| 469 LOG(ERROR) << "Failed to sync with nacl-gdb"; | |
| 470 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, reply_); | |
| 471 } | |
| 472 } // namespace | |
| 473 | |
| 474 bool NaClProcessHost::LaunchNaClGdb(base::ProcessId pid) { | |
| 475 CommandLine::StringType nacl_gdb = | |
| 476 CommandLine::ForCurrentProcess()->GetSwitchValueNative( | |
| 477 switches::kNaClGdb); | |
| 478 CommandLine::StringVector argv; | |
| 479 // We don't support spaces inside arguments in --nacl-gdb switch. | |
| 480 base::SplitString(nacl_gdb, static_cast<CommandLine::CharType>(' '), &argv); | |
| 481 CommandLine cmd_line(argv); | |
| 482 cmd_line.AppendArg("--eval-command"); | |
| 483 const FilePath::StringType& irt_path = | |
| 484 NaClBrowser::GetInstance()->GetIrtFilePath().value(); | |
| 485 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-irt ") + irt_path); | |
| 486 FilePath manifest_path = GetManifestPath(); | |
| 487 if (!manifest_path.empty()) { | |
| 488 cmd_line.AppendArg("--eval-command"); | |
| 489 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-manifest ") + | |
| 490 manifest_path.value()); | |
| 491 } | |
| 492 cmd_line.AppendArg("--eval-command"); | |
| 493 cmd_line.AppendArg("attach " + base::IntToString(pid)); | |
| 494 int fds[2]; | |
| 495 if (pipe(fds) != 0) | |
| 496 return false; | |
| 497 // Tell debugger to send a byte to write end of the pipe. | |
|
Mark Seaborn
2012/04/05 15:03:12
'the debugger'; 'to write to the end'?
halyavin
2012/04/05 15:07:32
Done.
| |
| 498 // We use file descriptor in our process because debugger will be typically | |
|
Mark Seaborn
2012/04/05 15:03:12
'a file descriptor'; 'the debugger'
halyavin
2012/04/05 15:07:32
Done.
| |
| 499 // launched in a separate terminal, and a lot of terminals close all file | |
| 500 // descriptors before launching external programs. | |
| 501 cmd_line.AppendArg("--eval-command"); | |
| 502 cmd_line.AppendArg("dump binary value /proc/" + | |
| 503 base::IntToString(base::GetCurrentProcId()) + | |
| 504 "/fd/" + base::IntToString(fds[1]) + " (char)0"); | |
| 505 // wait on fds[0] | |
| 506 // If debugger crashes before attaching to the NaCl process, user can release | |
|
Mark Seaborn
2012/04/05 15:03:12
'the debugger'; 'the user'
halyavin
2012/04/05 15:07:32
Done.
| |
| 507 // resources by terminating NaCl loader in Chrome Task Manager. | |
|
Mark Seaborn
2012/04/05 15:03:12
'the NaCl loader'
halyavin
2012/04/05 15:07:32
Done.
| |
| 508 nacl_gdb_watcher_delegate_.reset( | |
| 509 new NaClGdbWatchDelegate( | |
| 510 fds[0], fds[1], | |
| 511 base::Bind(&NaClProcessHost::OnNaClGdbAttached, | |
| 512 weak_factory_.GetWeakPtr()))); | |
| 513 MessageLoopForIO::current()->WatchFileDescriptor( | |
| 514 fds[0], | |
| 515 true, | |
| 516 MessageLoopForIO::WATCH_READ, | |
| 517 &nacl_gdb_watcher_, | |
| 518 nacl_gdb_watcher_delegate_.get()); | |
| 519 return base::LaunchProcess(cmd_line, base::LaunchOptions(), NULL); | |
| 520 } | |
| 521 | |
| 522 void NaClProcessHost::OnNaClGdbAttached() { | |
| 523 wait_for_nacl_gdb_ = false; | |
| 524 nacl_gdb_watcher_.StopWatchingFileDescriptor(); | |
| 525 nacl_gdb_watcher_delegate_.reset(); | |
| 526 OnProcessLaunched(); | |
| 527 } | |
| 528 #endif | |
| 529 | |
| 530 FilePath NaClProcessHost::GetManifestPath() { | |
| 531 GURL manifest_url = GURL(process_->GetData().name); | |
| 532 const Extension* extension = extension_info_map_->extensions() | |
| 533 .GetExtensionOrAppByURL(ExtensionURLInfo(manifest_url)); | |
| 534 if (extension != NULL && manifest_url.SchemeIs(chrome::kExtensionScheme)) { | |
| 535 std::string path = manifest_url.path(); | |
| 536 TrimString(path, "/", &path); // Remove first slash | |
| 537 return extension->path().AppendASCII(path); | |
| 538 } | |
| 539 return FilePath(); | |
| 540 } | |
| 434 | 541 |
| 435 bool NaClProcessHost::LaunchSelLdr() { | 542 bool NaClProcessHost::LaunchSelLdr() { |
| 436 std::string channel_id = process_->GetHost()->CreateChannel(); | 543 std::string channel_id = process_->GetHost()->CreateChannel(); |
| 437 if (channel_id.empty()) | 544 if (channel_id.empty()) |
| 438 return false; | 545 return false; |
| 439 | 546 |
| 440 CommandLine::StringType nacl_loader_prefix; | 547 CommandLine::StringType nacl_loader_prefix; |
| 441 #if defined(OS_POSIX) | 548 #if defined(OS_POSIX) |
| 442 nacl_loader_prefix = CommandLine::ForCurrentProcess()->GetSwitchValueNative( | 549 nacl_loader_prefix = CommandLine::ForCurrentProcess()->GetSwitchValueNative( |
| 443 switches::kNaClLoaderCmdPrefix); | 550 switches::kNaClLoaderCmdPrefix); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 482 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); | 589 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); |
| 483 if (logging::DialogsAreSuppressed()) | 590 if (logging::DialogsAreSuppressed()) |
| 484 cmd_line->AppendSwitch(switches::kNoErrorDialogs); | 591 cmd_line->AppendSwitch(switches::kNoErrorDialogs); |
| 485 | 592 |
| 486 if (!nacl_loader_prefix.empty()) | 593 if (!nacl_loader_prefix.empty()) |
| 487 cmd_line->PrependWrapper(nacl_loader_prefix); | 594 cmd_line->PrependWrapper(nacl_loader_prefix); |
| 488 | 595 |
| 489 FilePath nacl_gdb = CommandLine::ForCurrentProcess()->GetSwitchValuePath( | 596 FilePath nacl_gdb = CommandLine::ForCurrentProcess()->GetSwitchValuePath( |
| 490 switches::kNaClGdb); | 597 switches::kNaClGdb); |
| 491 if (!nacl_gdb.empty()) { | 598 if (!nacl_gdb.empty()) { |
| 492 GURL manifest_url = GURL(process_->GetData().name); | 599 #if defined(OS_WIN) |
| 493 FilePath manifest_path; | |
| 494 const Extension* extension = extension_info_map_->extensions(). | |
| 495 GetExtensionOrAppByURL(ExtensionURLInfo(manifest_url)); | |
| 496 if (extension != NULL && manifest_url.SchemeIs(chrome::kExtensionScheme)) { | |
| 497 std::string path = manifest_url.path(); | |
| 498 TrimString(path, "/", &path); // Remove first slash | |
| 499 manifest_path = extension->path().AppendASCII(path); | |
| 500 } | |
| 501 cmd_line->AppendSwitch(switches::kNoSandbox); | 600 cmd_line->AppendSwitch(switches::kNoSandbox); |
| 502 scoped_ptr<CommandLine> gdb_cmd_line( | 601 scoped_ptr<CommandLine> gdb_cmd_line( |
| 503 LaunchWithNaClGdb(nacl_gdb, cmd_line.get(), manifest_path)); | 602 LaunchWithNaClGdb(nacl_gdb, cmd_line.get())); |
| 504 // We can't use process_->Launch() because OnProcessLaunched will be called | 603 // We can't use process_->Launch() because OnProcessLaunched will be called |
| 505 // with process_->GetData().handle filled by handle of gdb process. This | 604 // with process_->GetData().handle filled by handle of gdb process. This |
| 506 // handle will be used to duplicate handles for NaCl process and as | 605 // handle will be used to duplicate handles for NaCl process and as |
| 507 // a result NaCl process will not be able to use them. | 606 // a result NaCl process will not be able to use them. |
| 508 // | 607 // |
| 509 // So we don't fill process_->GetData().handle and wait for | 608 // So we don't fill process_->GetData().handle and wait for |
| 510 // OnChannelConnected to get handle of NaCl process from its pid. Then we | 609 // OnChannelConnected to get handle of NaCl process from its pid. Then we |
| 511 // call OnProcessLaunched. | 610 // call OnProcessLaunched. |
| 512 return base::LaunchProcess(*gdb_cmd_line, base::LaunchOptions(), NULL); | 611 return base::LaunchProcess(*gdb_cmd_line, base::LaunchOptions(), NULL); |
| 612 #elif defined(OS_LINUX) | |
| 613 wait_for_nacl_gdb_ = true; | |
| 614 #endif | |
| 513 } | 615 } |
| 514 | 616 |
| 515 // On Windows we might need to start the broker process to launch a new loader | 617 // On Windows we might need to start the broker process to launch a new loader |
| 516 #if defined(OS_WIN) | 618 #if defined(OS_WIN) |
| 517 if (RunningOnWOW64()) { | 619 if (RunningOnWOW64()) { |
| 518 return NaClBrokerService::GetInstance()->LaunchLoader(this, channel_id); | 620 return NaClBrokerService::GetInstance()->LaunchLoader(this, channel_id); |
| 519 } else { | 621 } else { |
| 520 process_->Launch(FilePath(), cmd_line.release()); | 622 process_->Launch(FilePath(), cmd_line.release()); |
| 521 } | 623 } |
| 522 #elif defined(OS_POSIX) | 624 #elif defined(OS_POSIX) |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 617 NULL, | 719 NULL, |
| 618 &error_code); | 720 &error_code); |
| 619 if (error_code != base::PLATFORM_FILE_OK) { | 721 if (error_code != base::PLATFORM_FILE_OK) { |
| 620 LOG(ERROR) << "Failed to open NaCl IRT file \"" | 722 LOG(ERROR) << "Failed to open NaCl IRT file \"" |
| 621 << irt_filepath_.LossyDisplayName() | 723 << irt_filepath_.LossyDisplayName() |
| 622 << "\": " << error_code; | 724 << "\": " << error_code; |
| 623 } | 725 } |
| 624 } | 726 } |
| 625 | 727 |
| 626 void NaClProcessHost::OnProcessLaunched() { | 728 void NaClProcessHost::OnProcessLaunched() { |
| 729 #if defined(OS_LINUX) | |
| 730 if (wait_for_nacl_gdb_) { | |
| 731 if (LaunchNaClGdb(base::GetProcId(process_->GetData().handle))) { | |
| 732 // OnProcessLaunched will be called with wait_for_nacl_gdb_ = false once | |
| 733 // debugger is attached to the program. | |
| 734 return; | |
| 735 } | |
| 736 LOG(ERROR) << "Failed to launch debugger"; | |
| 737 // Continue execution without debugger. | |
| 738 } | |
| 739 #endif | |
| 627 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); | 740 NaClBrowser* nacl_browser = NaClBrowser::GetInstance(); |
| 628 | 741 |
| 629 if (nacl_browser->IrtAvailable()) { | 742 if (nacl_browser->IrtAvailable()) { |
| 630 // The IRT is already open. Away we go. | 743 // The IRT is already open. Away we go. |
| 631 SendStart(); | 744 SendStart(); |
| 632 } else { | 745 } else { |
| 633 // We're waiting for the IRT to be open. | 746 // We're waiting for the IRT to be open. |
| 634 if (!nacl_browser->MakeIrtAvailable( | 747 if (!nacl_browser->MakeIrtAvailable( |
| 635 base::Bind(&NaClProcessHost::IrtReady, | 748 base::Bind(&NaClProcessHost::IrtReady, |
| 636 weak_factory_.GetWeakPtr()))) | 749 weak_factory_.GetWeakPtr()))) |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 914 bool handled = true; | 1027 bool handled = true; |
| 915 IPC_BEGIN_MESSAGE_MAP(NaClProcessHost, msg) | 1028 IPC_BEGIN_MESSAGE_MAP(NaClProcessHost, msg) |
| 916 IPC_MESSAGE_HANDLER(NaClProcessMsg_QueryKnownToValidate, | 1029 IPC_MESSAGE_HANDLER(NaClProcessMsg_QueryKnownToValidate, |
| 917 OnQueryKnownToValidate) | 1030 OnQueryKnownToValidate) |
| 918 IPC_MESSAGE_HANDLER(NaClProcessMsg_SetKnownToValidate, | 1031 IPC_MESSAGE_HANDLER(NaClProcessMsg_SetKnownToValidate, |
| 919 OnSetKnownToValidate) | 1032 OnSetKnownToValidate) |
| 920 IPC_MESSAGE_UNHANDLED(handled = false) | 1033 IPC_MESSAGE_UNHANDLED(handled = false) |
| 921 IPC_END_MESSAGE_MAP() | 1034 IPC_END_MESSAGE_MAP() |
| 922 return handled; | 1035 return handled; |
| 923 } | 1036 } |
| OLD | NEW |