Don't preload rarely seen large images
[chromium-blink-merge.git] / components / nacl / browser / nacl_process_host.cc
blobcc292bfcb1028ef7a67b5b597120a988c7274f29
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.
5 #include "components/nacl/browser/nacl_process_host.h"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
11 #include "base/base_switches.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/files/file_util.h"
15 #include "base/location.h"
16 #include "base/metrics/histogram_macros.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/single_thread_task_runner.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/sys_byteorder.h"
28 #include "base/thread_task_runner_handle.h"
29 #include "base/threading/sequenced_worker_pool.h"
30 #include "base/win/windows_version.h"
31 #include "build/build_config.h"
32 #include "components/nacl/browser/nacl_browser.h"
33 #include "components/nacl/browser/nacl_browser_delegate.h"
34 #include "components/nacl/browser/nacl_host_message_filter.h"
35 #include "components/nacl/common/nacl_cmd_line.h"
36 #include "components/nacl/common/nacl_host_messages.h"
37 #include "components/nacl/common/nacl_messages.h"
38 #include "components/nacl/common/nacl_process_type.h"
39 #include "components/nacl/common/nacl_switches.h"
40 #include "content/public/browser/browser_child_process_host.h"
41 #include "content/public/browser/browser_ppapi_host.h"
42 #include "content/public/browser/child_process_data.h"
43 #include "content/public/browser/plugin_service.h"
44 #include "content/public/browser/render_process_host.h"
45 #include "content/public/browser/web_contents.h"
46 #include "content/public/common/child_process_host.h"
47 #include "content/public/common/content_switches.h"
48 #include "content/public/common/process_type.h"
49 #include "content/public/common/sandboxed_process_launcher_delegate.h"
50 #include "ipc/ipc_channel.h"
51 #include "ipc/ipc_switches.h"
52 #include "native_client/src/shared/imc/nacl_imc_c.h"
53 #include "net/base/net_util.h"
54 #include "net/socket/socket_descriptor.h"
55 #include "ppapi/host/host_factory.h"
56 #include "ppapi/host/ppapi_host.h"
57 #include "ppapi/proxy/ppapi_messages.h"
58 #include "ppapi/shared_impl/ppapi_constants.h"
59 #include "ppapi/shared_impl/ppapi_nacl_plugin_args.h"
61 #if defined(OS_POSIX)
63 #include <fcntl.h>
64 #include <netinet/in.h>
65 #include <sys/socket.h>
67 #include "ipc/ipc_channel_posix.h"
68 #elif defined(OS_WIN)
69 #include <windows.h>
71 #include "base/threading/thread.h"
72 #include "base/win/scoped_handle.h"
73 #include "components/nacl/browser/nacl_broker_service_win.h"
74 #include "components/nacl/common/nacl_debug_exception_handler_win.h"
75 #include "content/public/common/sandbox_init.h"
76 #endif
78 using content::BrowserThread;
79 using content::ChildProcessData;
80 using content::ChildProcessHost;
81 using ppapi::proxy::SerializedHandle;
83 namespace nacl {
85 #if defined(OS_WIN)
86 namespace {
88 // Looks for the largest contiguous unallocated region of address
89 // space and returns it via |*out_addr| and |*out_size|.
90 void FindAddressSpace(base::ProcessHandle process,
91 char** out_addr, size_t* out_size) {
92 *out_addr = NULL;
93 *out_size = 0;
94 char* addr = 0;
95 while (true) {
96 MEMORY_BASIC_INFORMATION info;
97 size_t result = VirtualQueryEx(process, static_cast<void*>(addr),
98 &info, sizeof(info));
99 if (result < sizeof(info))
100 break;
101 if (info.State == MEM_FREE && info.RegionSize > *out_size) {
102 *out_addr = addr;
103 *out_size = info.RegionSize;
105 addr += info.RegionSize;
109 #ifdef _DLL
111 bool IsInPath(const std::string& path_env_var, const std::string& dir) {
112 std::vector<std::string> split;
113 base::SplitString(path_env_var, ';', &split);
114 for (std::vector<std::string>::const_iterator i(split.begin());
115 i != split.end();
116 ++i) {
117 if (*i == dir)
118 return true;
120 return false;
123 #endif // _DLL
125 } // namespace
127 // Allocates |size| bytes of address space in the given process at a
128 // randomised address.
129 void* AllocateAddressSpaceASLR(base::ProcessHandle process, size_t size) {
130 char* addr;
131 size_t avail_size;
132 FindAddressSpace(process, &addr, &avail_size);
133 if (avail_size < size)
134 return NULL;
135 size_t offset = base::RandGenerator(avail_size - size);
136 const int kPageSize = 0x10000;
137 void* request_addr =
138 reinterpret_cast<void*>(reinterpret_cast<uint64>(addr + offset)
139 & ~(kPageSize - 1));
140 return VirtualAllocEx(process, request_addr, size,
141 MEM_RESERVE, PAGE_NOACCESS);
144 namespace {
146 bool RunningOnWOW64() {
147 return (base::win::OSInfo::GetInstance()->wow64_status() ==
148 base::win::OSInfo::WOW64_ENABLED);
151 } // namespace
153 #endif // defined(OS_WIN)
155 namespace {
157 // NOTE: changes to this class need to be reviewed by the security team.
158 class NaClSandboxedProcessLauncherDelegate
159 : public content::SandboxedProcessLauncherDelegate {
160 public:
161 NaClSandboxedProcessLauncherDelegate(ChildProcessHost* host)
162 #if defined(OS_POSIX)
163 : ipc_fd_(host->TakeClientFileDescriptor())
164 #endif
167 ~NaClSandboxedProcessLauncherDelegate() override {}
169 #if defined(OS_WIN)
170 void PostSpawnTarget(base::ProcessHandle process) override {
171 // For Native Client sel_ldr processes on 32-bit Windows, reserve 1 GB of
172 // address space to prevent later failure due to address space fragmentation
173 // from .dll loading. The NaCl process will attempt to locate this space by
174 // scanning the address space using VirtualQuery.
175 // TODO(bbudge) Handle the --no-sandbox case.
176 // http://code.google.com/p/nativeclient/issues/detail?id=2131
177 const SIZE_T kNaClSandboxSize = 1 << 30;
178 if (!nacl::AllocateAddressSpaceASLR(process, kNaClSandboxSize)) {
179 DLOG(WARNING) << "Failed to reserve address space for Native Client";
182 #elif defined(OS_POSIX)
183 bool ShouldUseZygote() override { return true; }
184 base::ScopedFD TakeIpcFd() override { return ipc_fd_.Pass(); }
185 #endif // OS_WIN
187 private:
188 #if defined(OS_POSIX)
189 base::ScopedFD ipc_fd_;
190 #endif // OS_POSIX
193 void 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
202 void CloseFile(base::File file) {
203 // The base::File destructor will close the file for us.
206 } // namespace
208 unsigned NaClProcessHost::keepalive_throttle_interval_milliseconds_ =
209 ppapi::kKeepaliveThrottleIntervalDefaultMilliseconds;
211 // Unfortunately, we cannot use ScopedGeneric directly for IPC::ChannelHandle,
212 // because there is neither operator== nor operator != definition for it.
213 // Instead, define a simple wrapper for IPC::ChannelHandle with an assumption
214 // that this only takes a transferred IPC::ChannelHandle or one to be
215 // transferred via IPC.
216 class NaClProcessHost::ScopedChannelHandle {
217 MOVE_ONLY_TYPE_FOR_CPP_03(ScopedChannelHandle, RValue);
218 public:
219 ScopedChannelHandle() {
221 explicit ScopedChannelHandle(const IPC::ChannelHandle& handle)
222 : handle_(handle) {
223 DCHECK(IsSupportedHandle(handle_));
225 ScopedChannelHandle(RValue other) : handle_(other.object->handle_) {
226 other.object->handle_ = IPC::ChannelHandle();
227 DCHECK(IsSupportedHandle(handle_));
229 ~ScopedChannelHandle() {
230 CloseIfNecessary();
233 const IPC::ChannelHandle& get() const { return handle_; }
234 IPC::ChannelHandle release() WARN_UNUSED_RESULT {
235 IPC::ChannelHandle result = handle_;
236 handle_ = IPC::ChannelHandle();
237 return result;
240 void reset(const IPC::ChannelHandle& handle = IPC::ChannelHandle()) {
241 DCHECK(IsSupportedHandle(handle));
242 #if defined(OS_POSIX)
243 // Following the manner of base::ScopedGeneric, we do not support
244 // reset() with same handle for simplicity of the implementation.
245 CHECK(handle.socket.fd == -1 || handle.socket.fd != handle_.socket.fd);
246 #endif
247 CloseIfNecessary();
248 handle_ = handle;
251 private:
252 // Returns true if the given handle is closable automatically by this
253 // class. This function is just a helper for validation.
254 static bool IsSupportedHandle(const IPC::ChannelHandle& handle) {
255 #if defined(OS_WIN)
256 // On Windows, it is not supported to marshal the |pipe.handle|.
257 // In our case, we wrap a transferred ChannelHandle (or one to be
258 // transferred) via IPC, so we can assume |handle.pipe.handle| is NULL.
259 return handle.pipe.handle == NULL;
260 #else
261 return true;
262 #endif
265 void CloseIfNecessary() {
266 #if defined(OS_POSIX)
267 if (handle_.socket.auto_close) {
268 // Defer closing task to the ScopedFD.
269 base::ScopedFD(handle_.socket.fd);
271 #endif
274 IPC::ChannelHandle handle_;
277 NaClProcessHost::NaClProcessHost(
278 const GURL& manifest_url,
279 base::File nexe_file,
280 const NaClFileToken& nexe_token,
281 const std::vector<NaClResourcePrefetchResult>& prefetched_resource_files,
282 ppapi::PpapiPermissions permissions,
283 int render_view_id,
284 uint32 permission_bits,
285 bool uses_nonsfi_mode,
286 bool off_the_record,
287 NaClAppProcessType process_type,
288 const base::FilePath& profile_directory)
289 : manifest_url_(manifest_url),
290 nexe_file_(nexe_file.Pass()),
291 nexe_token_(nexe_token),
292 prefetched_resource_files_(prefetched_resource_files),
293 permissions_(permissions),
294 #if defined(OS_WIN)
295 process_launched_by_broker_(false),
296 #endif
297 reply_msg_(NULL),
298 #if defined(OS_WIN)
299 debug_exception_handler_requested_(false),
300 #endif
301 uses_nonsfi_mode_(uses_nonsfi_mode),
302 enable_debug_stub_(false),
303 enable_crash_throttling_(false),
304 off_the_record_(off_the_record),
305 process_type_(process_type),
306 profile_directory_(profile_directory),
307 render_view_id_(render_view_id),
308 weak_factory_(this) {
309 process_.reset(content::BrowserChildProcessHost::Create(
310 static_cast<content::ProcessType>(PROCESS_TYPE_NACL_LOADER), this));
312 // Set the display name so the user knows what plugin the process is running.
313 // We aren't on the UI thread so getting the pref locale for language
314 // formatting isn't possible, so IDN will be lost, but this is probably OK
315 // for this use case.
316 process_->SetName(net::FormatUrl(manifest_url_, std::string()));
318 enable_debug_stub_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
319 switches::kEnableNaClDebug);
320 DCHECK(process_type_ != kUnknownNaClProcessType);
321 enable_crash_throttling_ = process_type_ != kNativeNaClProcessType;
324 NaClProcessHost::~NaClProcessHost() {
325 // Report exit status only if the process was successfully started.
326 if (process_->GetData().handle != base::kNullProcessHandle) {
327 int exit_code = 0;
328 process_->GetTerminationStatus(false /* known_dead */, &exit_code);
329 std::string message =
330 base::StringPrintf("NaCl process exited with status %i (0x%x)",
331 exit_code, exit_code);
332 if (exit_code == 0) {
333 VLOG(1) << message;
334 } else {
335 LOG(ERROR) << message;
337 NaClBrowser::GetInstance()->OnProcessEnd(process_->GetData().id);
340 // Note: this does not work on Windows, though we currently support this
341 // prefetching feature only on POSIX platforms, so it should be ok.
342 #if defined(OS_WIN)
343 DCHECK(prefetched_resource_files_.empty());
344 #else
345 for (size_t i = 0; i < prefetched_resource_files_.size(); ++i) {
346 // The process failed to launch for some reason. Close resource file
347 // handles.
348 base::File file(IPC::PlatformFileForTransitToFile(
349 prefetched_resource_files_[i].file));
350 content::BrowserThread::GetBlockingPool()->PostTask(
351 FROM_HERE,
352 base::Bind(&CloseFile, base::Passed(file.Pass())));
354 #endif
356 if (reply_msg_) {
357 // The process failed to launch for some reason.
358 // Don't keep the renderer hanging.
359 reply_msg_->set_reply_error();
360 nacl_host_message_filter_->Send(reply_msg_);
362 #if defined(OS_WIN)
363 if (process_launched_by_broker_) {
364 NaClBrokerService::GetInstance()->OnLoaderDied();
366 #endif
369 void NaClProcessHost::OnProcessCrashed(int exit_status) {
370 if (enable_crash_throttling_ &&
371 !base::CommandLine::ForCurrentProcess()->HasSwitch(
372 switches::kDisablePnaclCrashThrottling)) {
373 NaClBrowser::GetInstance()->OnProcessCrashed();
377 // This is called at browser startup.
378 // static
379 void NaClProcessHost::EarlyStartup() {
380 NaClBrowser::GetInstance()->EarlyStartup();
381 // Inform NaClBrowser that we exist and will have a debug port at some point.
382 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
383 // Open the IRT file early to make sure that it isn't replaced out from
384 // under us by autoupdate.
385 NaClBrowser::GetInstance()->EnsureIrtAvailable();
386 #endif
387 base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
388 UMA_HISTOGRAM_BOOLEAN(
389 "NaCl.nacl-gdb",
390 !cmd->GetSwitchValuePath(switches::kNaClGdb).empty());
391 UMA_HISTOGRAM_BOOLEAN(
392 "NaCl.nacl-gdb-script",
393 !cmd->GetSwitchValuePath(switches::kNaClGdbScript).empty());
394 UMA_HISTOGRAM_BOOLEAN(
395 "NaCl.enable-nacl-debug",
396 cmd->HasSwitch(switches::kEnableNaClDebug));
397 std::string nacl_debug_mask =
398 cmd->GetSwitchValueASCII(switches::kNaClDebugMask);
399 // By default, exclude debugging SSH and the PNaCl translator.
400 // about::flags only allows empty flags as the default, so replace
401 // the empty setting with the default. To debug all apps, use a wild-card.
402 if (nacl_debug_mask.empty()) {
403 nacl_debug_mask = "!*://*/*ssh_client.nmf,chrome://pnacl-translator/*";
405 NaClBrowser::GetDelegate()->SetDebugPatterns(nacl_debug_mask);
408 // static
409 void NaClProcessHost::SetPpapiKeepAliveThrottleForTesting(
410 unsigned milliseconds) {
411 keepalive_throttle_interval_milliseconds_ = milliseconds;
414 void NaClProcessHost::Launch(
415 NaClHostMessageFilter* nacl_host_message_filter,
416 IPC::Message* reply_msg,
417 const base::FilePath& manifest_path) {
418 nacl_host_message_filter_ = nacl_host_message_filter;
419 reply_msg_ = reply_msg;
420 manifest_path_ = manifest_path;
422 // Do not launch the requested NaCl module if NaCl is marked "unstable" due
423 // to too many crashes within a given time period.
424 if (enable_crash_throttling_ &&
425 !base::CommandLine::ForCurrentProcess()->HasSwitch(
426 switches::kDisablePnaclCrashThrottling) &&
427 NaClBrowser::GetInstance()->IsThrottled()) {
428 SendErrorToRenderer("Process creation was throttled due to excessive"
429 " crashes");
430 delete this;
431 return;
434 const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
435 #if defined(OS_WIN)
436 if (cmd->HasSwitch(switches::kEnableNaClDebug) &&
437 !cmd->HasSwitch(switches::kNoSandbox)) {
438 // We don't switch off sandbox automatically for security reasons.
439 SendErrorToRenderer("NaCl's GDB debug stub requires --no-sandbox flag"
440 " on Windows. See crbug.com/265624.");
441 delete this;
442 return;
444 #endif
445 if (cmd->HasSwitch(switches::kNaClGdb) &&
446 !cmd->HasSwitch(switches::kEnableNaClDebug)) {
447 LOG(WARNING) << "--nacl-gdb flag requires --enable-nacl-debug flag";
450 // Start getting the IRT open asynchronously while we launch the NaCl process.
451 // We'll make sure this actually finished in StartWithLaunchedProcess, below.
452 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
453 nacl_browser->EnsureAllResourcesAvailable();
454 if (!nacl_browser->IsOk()) {
455 SendErrorToRenderer("could not find all the resources needed"
456 " to launch the process");
457 delete this;
458 return;
461 if (uses_nonsfi_mode_) {
462 bool nonsfi_mode_forced_by_command_line = false;
463 bool nonsfi_mode_allowed = false;
464 #if defined(OS_LINUX)
465 nonsfi_mode_forced_by_command_line =
466 cmd->HasSwitch(switches::kEnableNaClNonSfiMode);
467 #if defined(OS_CHROMEOS) && \
468 (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL))
469 nonsfi_mode_allowed = NaClBrowser::GetDelegate()->IsNonSfiModeAllowed(
470 nacl_host_message_filter->profile_directory(), manifest_url_);
471 #endif
472 #endif
473 bool nonsfi_mode_enabled =
474 nonsfi_mode_forced_by_command_line || nonsfi_mode_allowed;
476 if (!nonsfi_mode_enabled) {
477 SendErrorToRenderer(
478 "NaCl non-SFI mode is not available for this platform"
479 " and NaCl module.");
480 delete this;
481 return;
484 if (!enable_ppapi_proxy()) {
485 SendErrorToRenderer(
486 "PPAPI proxy must be enabled on NaCl in Non-SFI mode.");
487 delete this;
488 return;
490 } else {
491 // Rather than creating a socket pair in the renderer, and passing
492 // one side through the browser to sel_ldr, socket pairs are created
493 // in the browser and then passed to the renderer and sel_ldr.
495 // This is mainly for the benefit of Windows, where sockets cannot
496 // be passed in messages, but are copied via DuplicateHandle().
497 // This means the sandboxed renderer cannot send handles to the
498 // browser process.
500 NaClHandle pair[2];
501 // Create a connected socket
502 if (NaClSocketPair(pair) == -1) {
503 SendErrorToRenderer("NaClSocketPair() failed");
504 delete this;
505 return;
507 socket_for_renderer_ = base::File(pair[0]);
508 socket_for_sel_ldr_ = base::File(pair[1]);
509 SetCloseOnExec(pair[0]);
510 SetCloseOnExec(pair[1]);
513 // Create a shared memory region that the renderer and plugin share for
514 // reporting crash information.
515 crash_info_shmem_.CreateAnonymous(kNaClCrashInfoShmemSize);
517 // Launch the process
518 if (!LaunchSelLdr()) {
519 delete this;
523 void NaClProcessHost::OnChannelConnected(int32 peer_pid) {
524 if (!base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
525 switches::kNaClGdb).empty()) {
526 LaunchNaClGdb();
530 #if defined(OS_WIN)
531 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) {
532 process_launched_by_broker_ = true;
533 process_->SetHandle(handle);
534 SetDebugStubPort(nacl::kGdbDebugStubPortUnknown);
535 if (!StartWithLaunchedProcess())
536 delete this;
539 void NaClProcessHost::OnDebugExceptionHandlerLaunchedByBroker(bool success) {
540 IPC::Message* reply = attach_debug_exception_handler_reply_msg_.release();
541 NaClProcessMsg_AttachDebugExceptionHandler::WriteReplyParams(reply, success);
542 Send(reply);
544 #endif
546 // Needed to handle sync messages in OnMessageReceived.
547 bool NaClProcessHost::Send(IPC::Message* msg) {
548 return process_->Send(msg);
551 void NaClProcessHost::LaunchNaClGdb() {
552 const base::CommandLine& command_line =
553 *base::CommandLine::ForCurrentProcess();
554 #if defined(OS_WIN)
555 base::FilePath nacl_gdb =
556 command_line.GetSwitchValuePath(switches::kNaClGdb);
557 base::CommandLine cmd_line(nacl_gdb);
558 #else
559 base::CommandLine::StringType nacl_gdb =
560 command_line.GetSwitchValueNative(switches::kNaClGdb);
561 base::CommandLine::StringVector argv;
562 // We don't support spaces inside arguments in --nacl-gdb switch.
563 base::SplitString(nacl_gdb, static_cast<base::CommandLine::CharType>(' '),
564 &argv);
565 base::CommandLine cmd_line(argv);
566 #endif
567 cmd_line.AppendArg("--eval-command");
568 base::FilePath::StringType irt_path(
569 NaClBrowser::GetInstance()->GetIrtFilePath().value());
570 // Avoid back slashes because nacl-gdb uses posix escaping rules on Windows.
571 // See issue https://code.google.com/p/nativeclient/issues/detail?id=3482.
572 std::replace(irt_path.begin(), irt_path.end(), '\\', '/');
573 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-irt \"") + irt_path +
574 FILE_PATH_LITERAL("\""));
575 if (!manifest_path_.empty()) {
576 cmd_line.AppendArg("--eval-command");
577 base::FilePath::StringType manifest_path_value(manifest_path_.value());
578 std::replace(manifest_path_value.begin(), manifest_path_value.end(),
579 '\\', '/');
580 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-manifest \"") +
581 manifest_path_value + FILE_PATH_LITERAL("\""));
583 cmd_line.AppendArg("--eval-command");
584 cmd_line.AppendArg("target remote :4014");
585 base::FilePath script =
586 command_line.GetSwitchValuePath(switches::kNaClGdbScript);
587 if (!script.empty()) {
588 cmd_line.AppendArg("--command");
589 cmd_line.AppendArgNative(script.value());
591 base::LaunchProcess(cmd_line, base::LaunchOptions());
594 bool NaClProcessHost::LaunchSelLdr() {
595 std::string channel_id = process_->GetHost()->CreateChannel();
596 if (channel_id.empty()) {
597 SendErrorToRenderer("CreateChannel() failed");
598 return false;
601 // Build command line for nacl.
603 #if defined(OS_MACOSX)
604 // The Native Client process needs to be able to allocate a 1GB contiguous
605 // region to use as the client environment's virtual address space. ASLR
606 // (PIE) interferes with this by making it possible that no gap large enough
607 // to accomodate this request will exist in the child process' address
608 // space. Disable PIE for NaCl processes. See http://crbug.com/90221 and
609 // http://code.google.com/p/nativeclient/issues/detail?id=2043.
610 int flags = ChildProcessHost::CHILD_NO_PIE;
611 #elif defined(OS_LINUX)
612 int flags = ChildProcessHost::CHILD_ALLOW_SELF;
613 #else
614 int flags = ChildProcessHost::CHILD_NORMAL;
615 #endif
617 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags);
618 if (exe_path.empty())
619 return false;
621 #if defined(OS_WIN)
622 // On Windows 64-bit NaCl loader is called nacl64.exe instead of chrome.exe
623 if (RunningOnWOW64()) {
624 if (!NaClBrowser::GetInstance()->GetNaCl64ExePath(&exe_path)) {
625 SendErrorToRenderer("could not get path to nacl64.exe");
626 return false;
629 #ifdef _DLL
630 // When using the DLL CRT on Windows, we need to amend the PATH to include
631 // the location of the x64 CRT DLLs. This is only the case when using a
632 // component=shared_library build (i.e. generally dev debug builds). The
633 // x86 CRT DLLs are in e.g. out\Debug for chrome.exe etc., so the x64 ones
634 // are put in out\Debug\x64 which we add to the PATH here so that loader
635 // can find them. See http://crbug.com/346034.
636 scoped_ptr<base::Environment> env(base::Environment::Create());
637 static const char kPath[] = "PATH";
638 std::string old_path;
639 base::FilePath module_path;
640 if (!PathService::Get(base::FILE_MODULE, &module_path)) {
641 SendErrorToRenderer("could not get path to current module");
642 return false;
644 std::string x64_crt_path =
645 base::WideToUTF8(module_path.DirName().Append(L"x64").value());
646 if (!env->GetVar(kPath, &old_path)) {
647 env->SetVar(kPath, x64_crt_path);
648 } else if (!IsInPath(old_path, x64_crt_path)) {
649 std::string new_path(old_path);
650 new_path.append(";");
651 new_path.append(x64_crt_path);
652 env->SetVar(kPath, new_path);
654 #endif // _DLL
656 #endif
658 scoped_ptr<base::CommandLine> cmd_line(new base::CommandLine(exe_path));
659 CopyNaClCommandLineArguments(cmd_line.get());
661 cmd_line->AppendSwitchASCII(switches::kProcessType,
662 (uses_nonsfi_mode_ ?
663 switches::kNaClLoaderNonSfiProcess :
664 switches::kNaClLoaderProcess));
665 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
666 if (NaClBrowser::GetDelegate()->DialogsAreSuppressed())
667 cmd_line->AppendSwitch(switches::kNoErrorDialogs);
669 // On Windows we might need to start the broker process to launch a new loader
670 #if defined(OS_WIN)
671 if (RunningOnWOW64()) {
672 if (!NaClBrokerService::GetInstance()->LaunchLoader(
673 weak_factory_.GetWeakPtr(), channel_id)) {
674 SendErrorToRenderer("broker service did not launch process");
675 return false;
677 return true;
679 #endif
680 process_->Launch(
681 new NaClSandboxedProcessLauncherDelegate(process_->GetHost()),
682 cmd_line.release(),
683 true);
684 return true;
687 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) {
688 if (uses_nonsfi_mode_) {
689 // IPC messages relating to NaCl's validation cache must not be exposed
690 // in Non-SFI Mode, otherwise a Non-SFI nexe could use SetKnownToValidate
691 // to create a hole in the SFI sandbox.
692 // In Non-SFI mode, no message is expected.
693 return false;
696 bool handled = true;
697 IPC_BEGIN_MESSAGE_MAP(NaClProcessHost, msg)
698 IPC_MESSAGE_HANDLER(NaClProcessMsg_QueryKnownToValidate,
699 OnQueryKnownToValidate)
700 IPC_MESSAGE_HANDLER(NaClProcessMsg_SetKnownToValidate,
701 OnSetKnownToValidate)
702 IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileToken,
703 OnResolveFileToken)
705 #if defined(OS_WIN)
706 IPC_MESSAGE_HANDLER_DELAY_REPLY(
707 NaClProcessMsg_AttachDebugExceptionHandler,
708 OnAttachDebugExceptionHandler)
709 IPC_MESSAGE_HANDLER(NaClProcessHostMsg_DebugStubPortSelected,
710 OnDebugStubPortSelected)
711 #endif
712 IPC_MESSAGE_HANDLER(NaClProcessHostMsg_PpapiChannelsCreated,
713 OnPpapiChannelsCreated)
714 IPC_MESSAGE_UNHANDLED(handled = false)
715 IPC_END_MESSAGE_MAP()
716 return handled;
719 void NaClProcessHost::OnProcessLaunched() {
720 if (!StartWithLaunchedProcess())
721 delete this;
724 // Called when the NaClBrowser singleton has been fully initialized.
725 void NaClProcessHost::OnResourcesReady() {
726 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
727 if (!nacl_browser->IsReady()) {
728 SendErrorToRenderer("could not acquire shared resources needed by NaCl");
729 delete this;
730 } else if (!StartNaClExecution()) {
731 delete this;
735 void NaClProcessHost::ReplyToRenderer(
736 ScopedChannelHandle ppapi_channel_handle,
737 ScopedChannelHandle trusted_channel_handle,
738 ScopedChannelHandle manifest_service_channel_handle) {
739 #if defined(OS_WIN)
740 // If we are on 64-bit Windows, the NaCl process's sandbox is
741 // managed by a different process from the renderer's sandbox. We
742 // need to inform the renderer's sandbox about the NaCl process so
743 // that the renderer can send handles to the NaCl process using
744 // BrokerDuplicateHandle().
745 if (RunningOnWOW64()) {
746 if (!content::BrokerAddTargetPeer(process_->GetData().handle)) {
747 SendErrorToRenderer("BrokerAddTargetPeer() failed");
748 return;
751 #endif
753 // First, create an |imc_channel_handle| for the renderer.
754 IPC::PlatformFileForTransit imc_handle_for_renderer =
755 IPC::TakeFileHandleForProcess(socket_for_renderer_.Pass(),
756 nacl_host_message_filter_->PeerHandle());
757 if (imc_handle_for_renderer == IPC::InvalidPlatformFileForTransit()) {
758 // Failed to create the handle.
759 SendErrorToRenderer("imc_channel_handle creation failed.");
760 return;
763 // Hereafter, we always send an IPC message with handles including imc_handle
764 // created above which, on Windows, are not closable in this process.
765 std::string error_message;
766 base::SharedMemoryHandle crash_info_shmem_renderer_handle;
767 if (!crash_info_shmem_.ShareToProcess(nacl_host_message_filter_->PeerHandle(),
768 &crash_info_shmem_renderer_handle)) {
769 // On error, we do not send "IPC::ChannelHandle"s to the renderer process.
770 // Note that some other FDs/handles still get sent to the renderer, but
771 // will be closed there.
772 ppapi_channel_handle.reset();
773 trusted_channel_handle.reset();
774 manifest_service_channel_handle.reset();
775 error_message = "ShareToProcess() failed";
778 const ChildProcessData& data = process_->GetData();
779 SendMessageToRenderer(
780 NaClLaunchResult(imc_handle_for_renderer,
781 ppapi_channel_handle.release(),
782 trusted_channel_handle.release(),
783 manifest_service_channel_handle.release(),
784 base::GetProcId(data.handle),
785 data.id,
786 crash_info_shmem_renderer_handle),
787 error_message);
789 // Now that the crash information shmem handles have been shared with the
790 // plugin and the renderer, the browser can close its handle.
791 crash_info_shmem_.Close();
794 void NaClProcessHost::SendErrorToRenderer(const std::string& error_message) {
795 LOG(ERROR) << "NaCl process launch failed: " << error_message;
796 SendMessageToRenderer(NaClLaunchResult(), error_message);
799 void NaClProcessHost::SendMessageToRenderer(
800 const NaClLaunchResult& result,
801 const std::string& error_message) {
802 DCHECK(nacl_host_message_filter_.get());
803 DCHECK(reply_msg_);
804 if (nacl_host_message_filter_.get() == NULL || reply_msg_ == NULL) {
805 // As DCHECKed above, this case should not happen in general.
806 // Though, in this case, unfortunately there is no proper way to release
807 // resources which are already created in |result|. We just give up on
808 // releasing them, and leak them.
809 return;
812 NaClHostMsg_LaunchNaCl::WriteReplyParams(reply_msg_, result, error_message);
813 nacl_host_message_filter_->Send(reply_msg_);
814 nacl_host_message_filter_ = NULL;
815 reply_msg_ = NULL;
818 void NaClProcessHost::SetDebugStubPort(int port) {
819 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
820 nacl_browser->SetProcessGdbDebugStubPort(process_->GetData().id, port);
823 #if defined(OS_POSIX)
824 // TCP port we chose for NaCl debug stub. It can be any other number.
825 static const uint16_t kInitialDebugStubPort = 4014;
827 net::SocketDescriptor NaClProcessHost::GetDebugStubSocketHandle() {
828 // We always try to allocate the default port first. If this fails, we then
829 // allocate any available port.
830 // On success, if the test system has register a handler
831 // (GdbDebugStubPortListener), we fire a notification.
832 uint16 port = kInitialDebugStubPort;
833 net::SocketDescriptor s =
834 net::CreatePlatformSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
835 if (s != net::kInvalidSocket) {
836 // Allow rapid reuse.
837 static const int kOn = 1;
838 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
840 sockaddr_in addr;
841 memset(&addr, 0, sizeof(addr));
842 addr.sin_family = AF_INET;
843 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
844 addr.sin_port = base::HostToNet16(port);
845 if (bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
846 // Try allocate any available port.
847 addr.sin_port = base::HostToNet16(0);
848 if (bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
849 close(s);
850 LOG(ERROR) << "Could not bind socket to port" << port;
851 s = net::kInvalidSocket;
852 } else {
853 sockaddr_in sock_addr;
854 socklen_t sock_addr_size = sizeof(sock_addr);
855 if (getsockname(s, reinterpret_cast<struct sockaddr*>(&sock_addr),
856 &sock_addr_size) != 0 ||
857 sock_addr_size != sizeof(sock_addr)) {
858 LOG(ERROR) << "Could not determine bound port, getsockname() failed";
859 close(s);
860 s = net::kInvalidSocket;
861 } else {
862 port = base::NetToHost16(sock_addr.sin_port);
868 if (s != net::kInvalidSocket) {
869 SetDebugStubPort(port);
871 if (s == net::kInvalidSocket) {
872 LOG(ERROR) << "failed to open socket for debug stub";
873 return net::kInvalidSocket;
874 } else {
875 LOG(WARNING) << "debug stub on port " << port;
877 if (listen(s, 1)) {
878 LOG(ERROR) << "listen() failed on debug stub socket";
879 if (IGNORE_EINTR(close(s)) < 0)
880 PLOG(ERROR) << "failed to close debug stub socket";
881 return net::kInvalidSocket;
883 return s;
885 #endif
887 #if defined(OS_WIN)
888 void NaClProcessHost::OnDebugStubPortSelected(uint16_t debug_stub_port) {
889 CHECK(!uses_nonsfi_mode_);
890 SetDebugStubPort(debug_stub_port);
892 #endif
894 bool NaClProcessHost::StartNaClExecution() {
895 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
897 NaClStartParams params;
899 // Enable PPAPI proxy channel creation only for renderer processes.
900 params.enable_ipc_proxy = enable_ppapi_proxy();
901 params.process_type = process_type_;
902 bool enable_nacl_debug = enable_debug_stub_ &&
903 NaClBrowser::GetDelegate()->URLMatchesDebugPatterns(manifest_url_);
904 if (uses_nonsfi_mode_) {
905 // Currently, non-SFI mode is supported only on Linux.
906 #if defined(OS_LINUX)
907 // In non-SFI mode, we do not use SRPC. Make sure that the socketpair is
908 // not created.
909 DCHECK(!socket_for_sel_ldr_.IsValid());
910 #endif
911 if (enable_nacl_debug) {
912 base::ProcessId pid = base::GetProcId(process_->GetData().handle);
913 LOG(WARNING) << "nonsfi nacl plugin running in " << pid;
915 } else {
916 params.validation_cache_enabled = nacl_browser->ValidationCacheIsEnabled();
917 params.validation_cache_key = nacl_browser->GetValidationCacheKey();
918 params.version = NaClBrowser::GetDelegate()->GetVersionString();
919 params.enable_debug_stub = enable_nacl_debug;
921 const ChildProcessData& data = process_->GetData();
922 params.imc_bootstrap_handle =
923 IPC::TakeFileHandleForProcess(socket_for_sel_ldr_.Pass(), data.handle);
924 if (params.imc_bootstrap_handle == IPC::InvalidPlatformFileForTransit()) {
925 return false;
928 const base::File& irt_file = nacl_browser->IrtFile();
929 CHECK(irt_file.IsValid());
930 // Send over the IRT file handle. We don't close our own copy!
931 params.irt_handle = IPC::GetFileHandleForProcess(
932 irt_file.GetPlatformFile(), data.handle, false);
933 if (params.irt_handle == IPC::InvalidPlatformFileForTransit()) {
934 return false;
937 #if defined(OS_MACOSX)
938 // For dynamic loading support, NaCl requires a file descriptor that
939 // was created in /tmp, since those created with shm_open() are not
940 // mappable with PROT_EXEC. Rather than requiring an extra IPC
941 // round trip out of the sandbox, we create an FD here.
942 base::SharedMemory memory_buffer;
943 base::SharedMemoryCreateOptions options;
944 options.size = 1;
945 options.executable = true;
946 if (!memory_buffer.Create(options)) {
947 DLOG(ERROR) << "Failed to allocate memory buffer";
948 return false;
950 base::SharedMemoryHandle duped_handle =
951 base::SharedMemory::DuplicateHandle(memory_buffer.handle());
952 base::ScopedFD memory_fd(
953 base::SharedMemory::GetFdFromSharedMemoryHandle(duped_handle));
954 if (!memory_fd.is_valid()) {
955 DLOG(ERROR) << "Failed to dup() a file descriptor";
956 return false;
958 params.mac_shm_fd = IPC::GetFileHandleForProcess(
959 memory_fd.release(), data.handle, true);
960 #endif
962 #if defined(OS_POSIX)
963 if (params.enable_debug_stub) {
964 net::SocketDescriptor server_bound_socket = GetDebugStubSocketHandle();
965 if (server_bound_socket != net::kInvalidSocket) {
966 params.debug_stub_server_bound_socket = IPC::GetFileHandleForProcess(
967 server_bound_socket, data.handle, true);
970 #endif
973 if (!crash_info_shmem_.ShareToProcess(process_->GetData().handle,
974 &params.crash_info_shmem_handle)) {
975 DLOG(ERROR) << "Failed to ShareToProcess() a shared memory buffer";
976 return false;
979 // Pass the pre-opened resource files to the loader. We do not have to reopen
980 // resource files here even for SFI mode because the descriptors are not from
981 // a renderer.
982 for (size_t i = 0; i < prefetched_resource_files_.size(); ++i) {
983 process_->Send(new NaClProcessMsg_AddPrefetchedResource(
984 NaClResourcePrefetchResult(
985 prefetched_resource_files_[i].file,
986 // For the same reason as the comment below, always use an empty
987 // base::FilePath for non-SFI mode.
988 (uses_nonsfi_mode_ ? base::FilePath() :
989 prefetched_resource_files_[i].file_path_metadata),
990 prefetched_resource_files_[i].file_key)));
992 prefetched_resource_files_.clear();
994 base::FilePath file_path;
995 if (uses_nonsfi_mode_) {
996 // Don't retrieve the file path when using nonsfi mode; there's no
997 // validation caching in that case, so it's unnecessary work, and would
998 // expose the file path to the plugin.
999 } else {
1000 if (NaClBrowser::GetInstance()->GetFilePath(nexe_token_.lo,
1001 nexe_token_.hi,
1002 &file_path)) {
1003 // We have to reopen the file in the browser process; we don't want a
1004 // compromised renderer to pass an arbitrary fd that could get loaded
1005 // into the plugin process.
1006 if (base::PostTaskAndReplyWithResult(
1007 content::BrowserThread::GetBlockingPool(),
1008 FROM_HERE,
1009 base::Bind(OpenNaClReadExecImpl,
1010 file_path,
1011 true /* is_executable */),
1012 base::Bind(&NaClProcessHost::StartNaClFileResolved,
1013 weak_factory_.GetWeakPtr(),
1014 params,
1015 file_path))) {
1016 return true;
1021 StartNaClFileResolved(params, base::FilePath(), base::File());
1022 return true;
1025 void NaClProcessHost::StartNaClFileResolved(
1026 NaClStartParams params,
1027 const base::FilePath& file_path,
1028 base::File checked_nexe_file) {
1029 if (checked_nexe_file.IsValid()) {
1030 // Release the file received from the renderer. This has to be done on a
1031 // thread where IO is permitted, though.
1032 content::BrowserThread::GetBlockingPool()->PostTask(
1033 FROM_HERE,
1034 base::Bind(&CloseFile, base::Passed(nexe_file_.Pass())));
1035 params.nexe_file_path_metadata = file_path;
1036 params.nexe_file = IPC::TakeFileHandleForProcess(
1037 checked_nexe_file.Pass(), process_->GetData().handle);
1038 } else {
1039 params.nexe_file = IPC::TakeFileHandleForProcess(
1040 nexe_file_.Pass(), process_->GetData().handle);
1043 #if defined(OS_LINUX)
1044 // In Non-SFI mode, create socket pairs for IPC channels here, unlike in
1045 // SFI-mode, in which those channels are created in nacl_listener.cc.
1046 // This is for security hardening. We can then prohibit the socketpair()
1047 // system call in nacl_helper and nacl_helper_nonsfi.
1048 if (uses_nonsfi_mode_) {
1049 // Note: here, because some FDs/handles for the NaCl loader process are
1050 // already opened, they are transferred to NaCl loader process even if
1051 // an error occurs first. It is because this is the simplest way to
1052 // ensure that these FDs/handles don't get leaked and that the NaCl loader
1053 // process will exit properly.
1054 bool has_error = false;
1056 // Note: this check is redundant. We check this earlier.
1057 DCHECK(params.enable_ipc_proxy);
1059 ScopedChannelHandle ppapi_browser_server_channel_handle;
1060 ScopedChannelHandle ppapi_browser_client_channel_handle;
1061 ScopedChannelHandle ppapi_renderer_server_channel_handle;
1062 ScopedChannelHandle ppapi_renderer_client_channel_handle;
1063 ScopedChannelHandle trusted_service_server_channel_handle;
1064 ScopedChannelHandle trusted_service_client_channel_handle;
1065 ScopedChannelHandle manifest_service_server_channel_handle;
1066 ScopedChannelHandle manifest_service_client_channel_handle;
1068 if (!CreateChannelHandlePair(&ppapi_browser_server_channel_handle,
1069 &ppapi_browser_client_channel_handle) ||
1070 !CreateChannelHandlePair(&ppapi_renderer_server_channel_handle,
1071 &ppapi_renderer_client_channel_handle) ||
1072 !CreateChannelHandlePair(&trusted_service_server_channel_handle,
1073 &trusted_service_client_channel_handle) ||
1074 !CreateChannelHandlePair(&manifest_service_server_channel_handle,
1075 &manifest_service_client_channel_handle)) {
1076 SendErrorToRenderer("Failed to create socket pairs.");
1077 has_error = true;
1080 if (!has_error &&
1081 !StartPPAPIProxy(ppapi_browser_client_channel_handle.Pass())) {
1082 SendErrorToRenderer("Failed to start browser PPAPI proxy.");
1083 has_error = true;
1086 if (!has_error) {
1087 // On success, send back a success message to the renderer process,
1088 // and transfer the channel handles for the NaCl loader process to
1089 // |params|.
1090 ReplyToRenderer(ppapi_renderer_client_channel_handle.Pass(),
1091 trusted_service_client_channel_handle.Pass(),
1092 manifest_service_client_channel_handle.Pass());
1093 params.ppapi_browser_channel_handle =
1094 ppapi_browser_server_channel_handle.release();
1095 params.ppapi_renderer_channel_handle =
1096 ppapi_renderer_server_channel_handle.release();
1097 params.trusted_service_channel_handle =
1098 trusted_service_server_channel_handle.release();
1099 params.manifest_service_channel_handle =
1100 manifest_service_server_channel_handle.release();
1103 #endif
1105 process_->Send(new NaClProcessMsg_Start(params));
1108 #if defined(OS_LINUX)
1109 // static
1110 bool NaClProcessHost::CreateChannelHandlePair(
1111 ScopedChannelHandle* channel_handle1,
1112 ScopedChannelHandle* channel_handle2) {
1113 DCHECK(channel_handle1);
1114 DCHECK(channel_handle2);
1116 int fd1 = -1;
1117 int fd2 = -1;
1118 if (!IPC::SocketPair(&fd1, &fd2)) {
1119 return false;
1122 IPC::ChannelHandle handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
1123 handle.socket = base::FileDescriptor(fd1, true);
1124 channel_handle1->reset(handle);
1125 handle.socket = base::FileDescriptor(fd2, true);
1126 channel_handle2->reset(handle);
1127 return true;
1129 #endif
1131 bool NaClProcessHost::StartPPAPIProxy(ScopedChannelHandle channel_handle) {
1132 if (ipc_proxy_channel_.get()) {
1133 // Attempt to open more than 1 browser channel is not supported.
1134 // Shut down the NaCl process.
1135 process_->GetHost()->ForceShutdown();
1136 return false;
1139 DCHECK_EQ(PROCESS_TYPE_NACL_LOADER, process_->GetData().process_type);
1141 ipc_proxy_channel_ = IPC::ChannelProxy::Create(
1142 channel_handle.release(), IPC::Channel::MODE_CLIENT, NULL,
1143 base::ThreadTaskRunnerHandle::Get().get());
1144 // Create the browser ppapi host and enable PPAPI message dispatching to the
1145 // browser process.
1146 ppapi_host_.reset(content::BrowserPpapiHost::CreateExternalPluginProcess(
1147 ipc_proxy_channel_.get(), // sender
1148 permissions_,
1149 process_->GetData().handle,
1150 ipc_proxy_channel_.get(),
1151 nacl_host_message_filter_->render_process_id(),
1152 render_view_id_,
1153 profile_directory_));
1154 ppapi_host_->SetOnKeepaliveCallback(
1155 NaClBrowser::GetDelegate()->GetOnKeepaliveCallback());
1157 ppapi::PpapiNaClPluginArgs args;
1158 args.off_the_record = nacl_host_message_filter_->off_the_record();
1159 args.permissions = permissions_;
1160 args.keepalive_throttle_interval_milliseconds =
1161 keepalive_throttle_interval_milliseconds_;
1162 base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
1163 DCHECK(cmdline);
1164 std::string flag_whitelist[] = {
1165 switches::kV,
1166 switches::kVModule,
1168 for (size_t i = 0; i < arraysize(flag_whitelist); ++i) {
1169 std::string value = cmdline->GetSwitchValueASCII(flag_whitelist[i]);
1170 if (!value.empty()) {
1171 args.switch_names.push_back(flag_whitelist[i]);
1172 args.switch_values.push_back(value);
1176 ppapi_host_->GetPpapiHost()->AddHostFactoryFilter(
1177 scoped_ptr<ppapi::host::HostFactory>(
1178 NaClBrowser::GetDelegate()->CreatePpapiHostFactory(
1179 ppapi_host_.get())));
1181 // Send a message to initialize the IPC dispatchers in the NaCl plugin.
1182 ipc_proxy_channel_->Send(new PpapiMsg_InitializeNaClDispatcher(args));
1183 return true;
1186 // This method is called when NaClProcessHostMsg_PpapiChannelCreated is
1187 // received.
1188 void NaClProcessHost::OnPpapiChannelsCreated(
1189 const IPC::ChannelHandle& raw_ppapi_browser_channel_handle,
1190 const IPC::ChannelHandle& raw_ppapi_renderer_channel_handle,
1191 const IPC::ChannelHandle& raw_trusted_renderer_channel_handle,
1192 const IPC::ChannelHandle& raw_manifest_service_channel_handle) {
1193 ScopedChannelHandle ppapi_browser_channel_handle(
1194 raw_ppapi_browser_channel_handle);
1195 ScopedChannelHandle ppapi_renderer_channel_handle(
1196 raw_ppapi_renderer_channel_handle);
1197 ScopedChannelHandle trusted_renderer_channel_handle(
1198 raw_trusted_renderer_channel_handle);
1199 ScopedChannelHandle manifest_service_channel_handle(
1200 raw_manifest_service_channel_handle);
1202 if (enable_ppapi_proxy()) {
1203 if (!StartPPAPIProxy(ppapi_browser_channel_handle.Pass())) {
1204 SendErrorToRenderer("Browser PPAPI proxy could not start.");
1205 return;
1207 } else {
1208 // If PPAPI proxy is disabled, channel handles should be invalid.
1209 DCHECK(ppapi_browser_channel_handle.get().name.empty());
1210 DCHECK(ppapi_renderer_channel_handle.get().name.empty());
1211 // Invalidate, just in case.
1212 ppapi_browser_channel_handle.reset();
1213 ppapi_renderer_channel_handle.reset();
1216 // Let the renderer know that the IPC channels are established.
1217 ReplyToRenderer(ppapi_renderer_channel_handle.Pass(),
1218 trusted_renderer_channel_handle.Pass(),
1219 manifest_service_channel_handle.Pass());
1222 bool NaClProcessHost::StartWithLaunchedProcess() {
1223 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
1225 if (nacl_browser->IsReady()) {
1226 return StartNaClExecution();
1227 } else if (nacl_browser->IsOk()) {
1228 nacl_browser->WaitForResources(
1229 base::Bind(&NaClProcessHost::OnResourcesReady,
1230 weak_factory_.GetWeakPtr()));
1231 return true;
1232 } else {
1233 SendErrorToRenderer("previously failed to acquire shared resources");
1234 return false;
1238 void NaClProcessHost::OnQueryKnownToValidate(const std::string& signature,
1239 bool* result) {
1240 CHECK(!uses_nonsfi_mode_);
1241 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
1242 *result = nacl_browser->QueryKnownToValidate(signature, off_the_record_);
1245 void NaClProcessHost::OnSetKnownToValidate(const std::string& signature) {
1246 CHECK(!uses_nonsfi_mode_);
1247 NaClBrowser::GetInstance()->SetKnownToValidate(
1248 signature, off_the_record_);
1251 void NaClProcessHost::OnResolveFileToken(uint64 file_token_lo,
1252 uint64 file_token_hi) {
1253 // Was the file registered?
1255 // Note that the file path cache is of bounded size, and old entries can get
1256 // evicted. If a large number of NaCl modules are being launched at once,
1257 // resolving the file_token may fail because the path cache was thrashed
1258 // while the file_token was in flight. In this case the query fails, and we
1259 // need to fall back to the slower path.
1261 // However: each NaCl process will consume 2-3 entries as it starts up, this
1262 // means that eviction will not happen unless you start up 33+ NaCl processes
1263 // at the same time, and this still requires worst-case timing. As a
1264 // practical matter, no entries should be evicted prematurely.
1265 // The cache itself should take ~ (150 characters * 2 bytes/char + ~60 bytes
1266 // data structure overhead) * 100 = 35k when full, so making it bigger should
1267 // not be a problem, if needed.
1269 // Each NaCl process will consume 2-3 entries because the manifest and main
1270 // nexe are currently not resolved. Shared libraries will be resolved. They
1271 // will be loaded sequentially, so they will only consume a single entry
1272 // while the load is in flight.
1274 // TODO(ncbray): track behavior with UMA. If entries are getting evicted or
1275 // bogus keys are getting queried, this would be good to know.
1276 CHECK(!uses_nonsfi_mode_);
1277 base::FilePath file_path;
1278 if (!NaClBrowser::GetInstance()->GetFilePath(
1279 file_token_lo, file_token_hi, &file_path)) {
1280 Send(new NaClProcessMsg_ResolveFileTokenReply(
1281 file_token_lo,
1282 file_token_hi,
1283 IPC::PlatformFileForTransit(),
1284 base::FilePath()));
1285 return;
1288 // Open the file.
1289 if (!base::PostTaskAndReplyWithResult(
1290 content::BrowserThread::GetBlockingPool(),
1291 FROM_HERE,
1292 base::Bind(OpenNaClReadExecImpl, file_path, true /* is_executable */),
1293 base::Bind(&NaClProcessHost::FileResolved,
1294 weak_factory_.GetWeakPtr(),
1295 file_token_lo,
1296 file_token_hi,
1297 file_path))) {
1298 Send(new NaClProcessMsg_ResolveFileTokenReply(
1299 file_token_lo,
1300 file_token_hi,
1301 IPC::PlatformFileForTransit(),
1302 base::FilePath()));
1306 void NaClProcessHost::FileResolved(
1307 uint64_t file_token_lo,
1308 uint64_t file_token_hi,
1309 const base::FilePath& file_path,
1310 base::File file) {
1311 base::FilePath out_file_path;
1312 IPC::PlatformFileForTransit out_handle;
1313 if (file.IsValid()) {
1314 out_file_path = file_path;
1315 out_handle = IPC::TakeFileHandleForProcess(
1316 file.Pass(),
1317 process_->GetData().handle);
1318 } else {
1319 out_handle = IPC::InvalidPlatformFileForTransit();
1321 Send(new NaClProcessMsg_ResolveFileTokenReply(
1322 file_token_lo,
1323 file_token_hi,
1324 out_handle,
1325 out_file_path));
1328 #if defined(OS_WIN)
1329 void NaClProcessHost::OnAttachDebugExceptionHandler(const std::string& info,
1330 IPC::Message* reply_msg) {
1331 CHECK(!uses_nonsfi_mode_);
1332 if (!AttachDebugExceptionHandler(info, reply_msg)) {
1333 // Send failure message.
1334 NaClProcessMsg_AttachDebugExceptionHandler::WriteReplyParams(reply_msg,
1335 false);
1336 Send(reply_msg);
1340 bool NaClProcessHost::AttachDebugExceptionHandler(const std::string& info,
1341 IPC::Message* reply_msg) {
1342 bool enable_exception_handling = process_type_ == kNativeNaClProcessType;
1343 if (!enable_exception_handling && !enable_debug_stub_) {
1344 DLOG(ERROR) <<
1345 "Debug exception handler requested by NaCl process when not enabled";
1346 return false;
1348 if (debug_exception_handler_requested_) {
1349 // The NaCl process should not request this multiple times.
1350 DLOG(ERROR) << "Multiple AttachDebugExceptionHandler requests received";
1351 return false;
1353 debug_exception_handler_requested_ = true;
1355 base::ProcessId nacl_pid = base::GetProcId(process_->GetData().handle);
1356 // We cannot use process_->GetData().handle because it does not have
1357 // the necessary access rights. We open the new handle here rather
1358 // than in the NaCl broker process in case the NaCl loader process
1359 // dies before the NaCl broker process receives the message we send.
1360 // The debug exception handler uses DebugActiveProcess() to attach,
1361 // but this takes a PID. We need to prevent the NaCl loader's PID
1362 // from being reused before DebugActiveProcess() is called, and
1363 // holding a process handle open achieves this.
1364 base::Process process =
1365 base::Process::OpenWithAccess(nacl_pid,
1366 PROCESS_QUERY_INFORMATION |
1367 PROCESS_SUSPEND_RESUME |
1368 PROCESS_TERMINATE |
1369 PROCESS_VM_OPERATION |
1370 PROCESS_VM_READ |
1371 PROCESS_VM_WRITE |
1372 PROCESS_DUP_HANDLE |
1373 SYNCHRONIZE);
1374 if (!process.IsValid()) {
1375 LOG(ERROR) << "Failed to get process handle";
1376 return false;
1379 attach_debug_exception_handler_reply_msg_.reset(reply_msg);
1380 // If the NaCl loader is 64-bit, the process running its debug
1381 // exception handler must be 64-bit too, so we use the 64-bit NaCl
1382 // broker process for this. Otherwise, on a 32-bit system, we use
1383 // the 32-bit browser process to run the debug exception handler.
1384 if (RunningOnWOW64()) {
1385 return NaClBrokerService::GetInstance()->LaunchDebugExceptionHandler(
1386 weak_factory_.GetWeakPtr(), nacl_pid, process.Handle(),
1387 info);
1388 } else {
1389 NaClStartDebugExceptionHandlerThread(
1390 process.Pass(), info, base::ThreadTaskRunnerHandle::Get(),
1391 base::Bind(&NaClProcessHost::OnDebugExceptionHandlerLaunchedByBroker,
1392 weak_factory_.GetWeakPtr()));
1393 return true;
1396 #endif
1398 } // namespace nacl