Home | History | Annotate | Download | only in nacl_host
      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                            &params.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, &params.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