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