Home | History | Annotate | Download | only in nacl_host
      1 // Copyright (c) 2011 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 "build/build_config.h"
      6 
      7 #include "chrome/browser/nacl_host/nacl_process_host.h"
      8 
      9 #if defined(OS_POSIX)
     10 #include <fcntl.h>
     11 #endif
     12 
     13 #include "base/command_line.h"
     14 #include "base/metrics/nacl_histogram.h"
     15 #include "base/utf_string_conversions.h"
     16 #include "base/win/windows_version.h"
     17 #include "chrome/common/chrome_switches.h"
     18 #include "chrome/common/logging_chrome.h"
     19 #include "chrome/common/nacl_cmd_line.h"
     20 #include "chrome/common/nacl_messages.h"
     21 #include "chrome/common/render_messages.h"
     22 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
     23 #include "ipc/ipc_switches.h"
     24 #include "native_client/src/shared/imc/nacl_imc.h"
     25 
     26 #if defined(OS_POSIX)
     27 #include "ipc/ipc_channel_posix.h"
     28 #elif defined(OS_WIN)
     29 #include "chrome/browser/nacl_host/nacl_broker_service_win.h"
     30 #endif
     31 
     32 namespace {
     33 
     34 #if !defined(DISABLE_NACL)
     35 void SetCloseOnExec(nacl::Handle fd) {
     36 #if defined(OS_POSIX)
     37   int flags = fcntl(fd, F_GETFD);
     38   CHECK(flags != -1);
     39   int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
     40   CHECK(rc == 0);
     41 #endif
     42 }
     43 #endif
     44 
     45 }  // namespace
     46 
     47 struct NaClProcessHost::NaClInternal {
     48   std::vector<nacl::Handle> sockets_for_renderer;
     49   std::vector<nacl::Handle> sockets_for_sel_ldr;
     50 };
     51 
     52 NaClProcessHost::NaClProcessHost(const std::wstring& url)
     53     : BrowserChildProcessHost(NACL_LOADER_PROCESS),
     54       reply_msg_(NULL),
     55       internal_(new NaClInternal()),
     56       running_on_wow64_(false) {
     57   set_name(url);
     58 #if defined(OS_WIN)
     59   running_on_wow64_ = (base::win::OSInfo::GetInstance()->wow64_status() ==
     60       base::win::OSInfo::WOW64_ENABLED);
     61 #endif
     62 }
     63 
     64 NaClProcessHost::~NaClProcessHost() {
     65   if (!reply_msg_)
     66     return;
     67 
     68   // nacl::Close() is not available at link time if DISABLE_NACL is
     69   // defined, but we still compile a bunch of other code from this
     70   // file anyway.  TODO(mseaborn): Make this less messy.
     71 #ifndef DISABLE_NACL
     72   for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) {
     73     nacl::Close(internal_->sockets_for_renderer[i]);
     74   }
     75   for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) {
     76     nacl::Close(internal_->sockets_for_sel_ldr[i]);
     77   }
     78 #endif
     79 
     80   // OnProcessLaunched didn't get called because the process couldn't launch.
     81   // Don't keep the renderer hanging.
     82   reply_msg_->set_reply_error();
     83   chrome_render_message_filter_->Send(reply_msg_);
     84 }
     85 
     86 bool NaClProcessHost::Launch(
     87     ChromeRenderMessageFilter* chrome_render_message_filter,
     88     int socket_count,
     89     IPC::Message* reply_msg) {
     90 #ifdef DISABLE_NACL
     91   NOTIMPLEMENTED() << "Native Client disabled at build time";
     92   return false;
     93 #else
     94   // Place an arbitrary limit on the number of sockets to limit
     95   // exposure in case the renderer is compromised.  We can increase
     96   // this if necessary.
     97   if (socket_count > 8) {
     98     return false;
     99   }
    100 
    101   // Rather than creating a socket pair in the renderer, and passing
    102   // one side through the browser to sel_ldr, socket pairs are created
    103   // in the browser and then passed to the renderer and sel_ldr.
    104   //
    105   // This is mainly for the benefit of Windows, where sockets cannot
    106   // be passed in messages, but are copied via DuplicateHandle().
    107   // This means the sandboxed renderer cannot send handles to the
    108   // browser process.
    109 
    110   for (int i = 0; i < socket_count; i++) {
    111     nacl::Handle pair[2];
    112     // Create a connected socket
    113     if (nacl::SocketPair(pair) == -1)
    114       return false;
    115     internal_->sockets_for_renderer.push_back(pair[0]);
    116     internal_->sockets_for_sel_ldr.push_back(pair[1]);
    117     SetCloseOnExec(pair[0]);
    118     SetCloseOnExec(pair[1]);
    119   }
    120 
    121   // Launch the process
    122   if (!LaunchSelLdr()) {
    123     return false;
    124   }
    125   UmaNaclHistogramEnumeration(NACL_STARTED);
    126   chrome_render_message_filter_ = chrome_render_message_filter;
    127   reply_msg_ = reply_msg;
    128 
    129   return true;
    130 #endif  // DISABLE_NACL
    131 }
    132 
    133 bool NaClProcessHost::LaunchSelLdr() {
    134   if (!CreateChannel())
    135     return false;
    136 
    137   // Build command line for nacl.
    138   FilePath exe_path = GetChildPath(true);
    139   if (exe_path.empty())
    140     return false;
    141 
    142   CommandLine* cmd_line = new CommandLine(exe_path);
    143   nacl::CopyNaClCommandLineArguments(cmd_line);
    144 
    145   cmd_line->AppendSwitchASCII(switches::kProcessType,
    146                               switches::kNaClLoaderProcess);
    147 
    148   cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id());
    149 
    150   SetCrashReporterCommandLine(cmd_line);
    151 
    152   // On Windows we might need to start the broker process to launch a new loader
    153 #if defined(OS_WIN)
    154   if (running_on_wow64_) {
    155     return NaClBrokerService::GetInstance()->LaunchLoader(
    156         this, ASCIIToWide(channel_id()));
    157   } else {
    158     BrowserChildProcessHost::Launch(FilePath(), cmd_line);
    159   }
    160 #elif defined(OS_POSIX)
    161   BrowserChildProcessHost::Launch(true,  // use_zygote
    162                                   base::environment_vector(),
    163                                   cmd_line);
    164 #endif
    165 
    166   return true;
    167 }
    168 
    169 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) {
    170   set_handle(handle);
    171   OnProcessLaunched();
    172 }
    173 
    174 base::TerminationStatus NaClProcessHost::GetChildTerminationStatus(
    175     int* exit_code) {
    176   if (running_on_wow64_)
    177     return base::GetTerminationStatus(handle(), exit_code);
    178   return BrowserChildProcessHost::GetChildTerminationStatus(exit_code);
    179 }
    180 
    181 void NaClProcessHost::OnChildDied() {
    182 #if defined(OS_WIN)
    183   NaClBrokerService::GetInstance()->OnLoaderDied();
    184 #endif
    185   BrowserChildProcessHost::OnChildDied();
    186 }
    187 
    188 void NaClProcessHost::OnProcessLaunched() {
    189   std::vector<nacl::FileDescriptor> handles_for_renderer;
    190   base::ProcessHandle nacl_process_handle;
    191 
    192   for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) {
    193 #if defined(OS_WIN)
    194     // Copy the handle into the renderer process.
    195     HANDLE handle_in_renderer;
    196     DuplicateHandle(base::GetCurrentProcessHandle(),
    197                     reinterpret_cast<HANDLE>(
    198                         internal_->sockets_for_renderer[i]),
    199                     chrome_render_message_filter_->peer_handle(),
    200                     &handle_in_renderer,
    201                     GENERIC_READ | GENERIC_WRITE,
    202                     FALSE,
    203                     DUPLICATE_CLOSE_SOURCE);
    204     handles_for_renderer.push_back(
    205         reinterpret_cast<nacl::FileDescriptor>(handle_in_renderer));
    206 #else
    207     // No need to dup the imc_handle - we don't pass it anywhere else so
    208     // it cannot be closed.
    209     nacl::FileDescriptor imc_handle;
    210     imc_handle.fd = internal_->sockets_for_renderer[i];
    211     imc_handle.auto_close = true;
    212     handles_for_renderer.push_back(imc_handle);
    213 #endif
    214   }
    215 
    216 #if defined(OS_WIN)
    217   // Copy the process handle into the renderer process.
    218   DuplicateHandle(base::GetCurrentProcessHandle(),
    219                   handle(),
    220                   chrome_render_message_filter_->peer_handle(),
    221                   &nacl_process_handle,
    222                   PROCESS_DUP_HANDLE,
    223                   FALSE,
    224                   0);
    225 #else
    226   // We use pid as process handle on Posix
    227   nacl_process_handle = handle();
    228 #endif
    229 
    230   // Get the pid of the NaCl process
    231   base::ProcessId nacl_process_id = base::GetProcId(handle());
    232 
    233   ViewHostMsg_LaunchNaCl::WriteReplyParams(
    234       reply_msg_, handles_for_renderer, nacl_process_handle, nacl_process_id);
    235   chrome_render_message_filter_->Send(reply_msg_);
    236   chrome_render_message_filter_ = NULL;
    237   reply_msg_ = NULL;
    238   internal_->sockets_for_renderer.clear();
    239 
    240   SendStartMessage();
    241 }
    242 
    243 void NaClProcessHost::SendStartMessage() {
    244   std::vector<nacl::FileDescriptor> handles_for_sel_ldr;
    245   for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) {
    246 #if defined(OS_WIN)
    247     HANDLE channel;
    248     if (!DuplicateHandle(GetCurrentProcess(),
    249                          reinterpret_cast<HANDLE>(
    250                              internal_->sockets_for_sel_ldr[i]),
    251                          handle(),
    252                          &channel,
    253                          GENERIC_READ | GENERIC_WRITE,
    254                          FALSE, DUPLICATE_CLOSE_SOURCE)) {
    255       return;
    256     }
    257     handles_for_sel_ldr.push_back(
    258         reinterpret_cast<nacl::FileDescriptor>(channel));
    259 #else
    260     nacl::FileDescriptor channel;
    261     channel.fd = dup(internal_->sockets_for_sel_ldr[i]);
    262     if (channel.fd < 0) {
    263       LOG(ERROR) << "Failed to dup() a file descriptor";
    264       return;
    265     }
    266     channel.auto_close = true;
    267     handles_for_sel_ldr.push_back(channel);
    268 #endif
    269   }
    270 
    271 #if defined(OS_MACOSX)
    272   // For dynamic loading support, NaCl requires a file descriptor that
    273   // was created in /tmp, since those created with shm_open() are not
    274   // mappable with PROT_EXEC.  Rather than requiring an extra IPC
    275   // round trip out of the sandbox, we create an FD here.
    276   base::SharedMemory memory_buffer;
    277   if (!memory_buffer.CreateAnonymous(/* size= */ 1)) {
    278     LOG(ERROR) << "Failed to allocate memory buffer";
    279     return;
    280   }
    281   nacl::FileDescriptor memory_fd;
    282   memory_fd.fd = dup(memory_buffer.handle().fd);
    283   if (memory_fd.fd < 0) {
    284     LOG(ERROR) << "Failed to dup() a file descriptor";
    285     return;
    286   }
    287   memory_fd.auto_close = true;
    288   handles_for_sel_ldr.push_back(memory_fd);
    289 #endif
    290 
    291   Send(new NaClProcessMsg_Start(handles_for_sel_ldr));
    292   internal_->sockets_for_sel_ldr.clear();
    293 }
    294 
    295 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) {
    296   NOTREACHED() << "Invalid message with type = " << msg.type();
    297   return false;
    298 }
    299 
    300 bool NaClProcessHost::CanShutdown() {
    301   return true;
    302 }
    303