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