Roll src/third_party/WebKit 9f7fb92:f103b33 (svn 202621:202622)
[chromium-blink-merge.git] / components / nacl / browser / nacl_process_host.cc
blobe5a507ffea418a7b7c579ef1a3915bb5c84b3ddd
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 "components/url_formatter/url_formatter.h"
41 #include "content/public/browser/browser_child_process_host.h"
42 #include "content/public/browser/browser_ppapi_host.h"
43 #include "content/public/browser/child_process_data.h"
44 #include "content/public/browser/plugin_service.h"
45 #include "content/public/browser/render_process_host.h"
46 #include "content/public/browser/web_contents.h"
47 #include "content/public/common/child_process_host.h"
48 #include "content/public/common/content_switches.h"
49 #include "content/public/common/process_type.h"
50 #include "content/public/common/sandboxed_process_launcher_delegate.h"
51 #include "ipc/ipc_channel.h"
52 #include "ipc/ipc_switches.h"
53 #include "native_client/src/shared/imc/nacl_imc_c.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 for (const base::StringPiece& cur : base::SplitStringPiece(
113 path_env_var, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
114 if (cur == dir)
115 return true;
117 return false;
120 #endif // _DLL
122 } // namespace
124 // Allocates |size| bytes of address space in the given process at a
125 // randomised address.
126 void* AllocateAddressSpaceASLR(base::ProcessHandle process, size_t size) {
127 char* addr;
128 size_t avail_size;
129 FindAddressSpace(process, &addr, &avail_size);
130 if (avail_size < size)
131 return NULL;
132 size_t offset = base::RandGenerator(avail_size - size);
133 const int kPageSize = 0x10000;
134 void* request_addr =
135 reinterpret_cast<void*>(reinterpret_cast<uint64>(addr + offset)
136 & ~(kPageSize - 1));
137 return VirtualAllocEx(process, request_addr, size,
138 MEM_RESERVE, PAGE_NOACCESS);
141 namespace {
143 bool RunningOnWOW64() {
144 return (base::win::OSInfo::GetInstance()->wow64_status() ==
145 base::win::OSInfo::WOW64_ENABLED);
148 } // namespace
150 #endif // defined(OS_WIN)
152 namespace {
154 // NOTE: changes to this class need to be reviewed by the security team.
155 class NaClSandboxedProcessLauncherDelegate
156 : public content::SandboxedProcessLauncherDelegate {
157 public:
158 NaClSandboxedProcessLauncherDelegate(ChildProcessHost* host)
159 #if defined(OS_POSIX)
160 : ipc_fd_(host->TakeClientFileDescriptor())
161 #endif
164 ~NaClSandboxedProcessLauncherDelegate() override {}
166 #if defined(OS_WIN)
167 void PostSpawnTarget(base::ProcessHandle process) override {
168 // For Native Client sel_ldr processes on 32-bit Windows, reserve 1 GB of
169 // address space to prevent later failure due to address space fragmentation
170 // from .dll loading. The NaCl process will attempt to locate this space by
171 // scanning the address space using VirtualQuery.
172 // TODO(bbudge) Handle the --no-sandbox case.
173 // http://code.google.com/p/nativeclient/issues/detail?id=2131
174 const SIZE_T kNaClSandboxSize = 1 << 30;
175 if (!nacl::AllocateAddressSpaceASLR(process, kNaClSandboxSize)) {
176 DLOG(WARNING) << "Failed to reserve address space for Native Client";
179 #elif defined(OS_POSIX)
180 bool ShouldUseZygote() override { return true; }
181 base::ScopedFD TakeIpcFd() override { return ipc_fd_.Pass(); }
182 #endif // OS_WIN
184 private:
185 #if defined(OS_POSIX)
186 base::ScopedFD ipc_fd_;
187 #endif // OS_POSIX
190 void SetCloseOnExec(NaClHandle fd) {
191 #if defined(OS_POSIX)
192 int flags = fcntl(fd, F_GETFD);
193 CHECK_NE(flags, -1);
194 int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
195 CHECK_EQ(rc, 0);
196 #endif
199 void CloseFile(base::File file) {
200 // The base::File destructor will close the file for us.
203 } // namespace
205 unsigned NaClProcessHost::keepalive_throttle_interval_milliseconds_ =
206 ppapi::kKeepaliveThrottleIntervalDefaultMilliseconds;
208 // Unfortunately, we cannot use ScopedGeneric directly for IPC::ChannelHandle,
209 // because there is neither operator== nor operator != definition for it.
210 // Instead, define a simple wrapper for IPC::ChannelHandle with an assumption
211 // that this only takes a transferred IPC::ChannelHandle or one to be
212 // transferred via IPC.
213 class NaClProcessHost::ScopedChannelHandle {
214 MOVE_ONLY_TYPE_FOR_CPP_03(ScopedChannelHandle, RValue);
215 public:
216 ScopedChannelHandle() {
218 explicit ScopedChannelHandle(const IPC::ChannelHandle& handle)
219 : handle_(handle) {
220 DCHECK(IsSupportedHandle(handle_));
222 ScopedChannelHandle(RValue other) : handle_(other.object->handle_) {
223 other.object->handle_ = IPC::ChannelHandle();
224 DCHECK(IsSupportedHandle(handle_));
226 ~ScopedChannelHandle() {
227 CloseIfNecessary();
230 const IPC::ChannelHandle& get() const { return handle_; }
231 IPC::ChannelHandle release() WARN_UNUSED_RESULT {
232 IPC::ChannelHandle result = handle_;
233 handle_ = IPC::ChannelHandle();
234 return result;
237 void reset(const IPC::ChannelHandle& handle = IPC::ChannelHandle()) {
238 DCHECK(IsSupportedHandle(handle));
239 #if defined(OS_POSIX)
240 // Following the manner of base::ScopedGeneric, we do not support
241 // reset() with same handle for simplicity of the implementation.
242 CHECK(handle.socket.fd == -1 || handle.socket.fd != handle_.socket.fd);
243 #endif
244 CloseIfNecessary();
245 handle_ = handle;
248 private:
249 // Returns true if the given handle is closable automatically by this
250 // class. This function is just a helper for validation.
251 static bool IsSupportedHandle(const IPC::ChannelHandle& handle) {
252 #if defined(OS_WIN)
253 // On Windows, it is not supported to marshal the |pipe.handle|.
254 // In our case, we wrap a transferred ChannelHandle (or one to be
255 // transferred) via IPC, so we can assume |handle.pipe.handle| is NULL.
256 return handle.pipe.handle == NULL;
257 #else
258 return true;
259 #endif
262 void CloseIfNecessary() {
263 #if defined(OS_POSIX)
264 if (handle_.socket.auto_close) {
265 // Defer closing task to the ScopedFD.
266 base::ScopedFD(handle_.socket.fd);
268 #endif
271 IPC::ChannelHandle handle_;
274 NaClProcessHost::NaClProcessHost(
275 const GURL& manifest_url,
276 base::File nexe_file,
277 const NaClFileToken& nexe_token,
278 const std::vector<NaClResourcePrefetchResult>& prefetched_resource_files,
279 ppapi::PpapiPermissions permissions,
280 int render_view_id,
281 uint32 permission_bits,
282 bool uses_nonsfi_mode,
283 bool off_the_record,
284 NaClAppProcessType process_type,
285 const base::FilePath& profile_directory)
286 : manifest_url_(manifest_url),
287 nexe_file_(nexe_file.Pass()),
288 nexe_token_(nexe_token),
289 prefetched_resource_files_(prefetched_resource_files),
290 permissions_(permissions),
291 #if defined(OS_WIN)
292 process_launched_by_broker_(false),
293 #endif
294 reply_msg_(NULL),
295 #if defined(OS_WIN)
296 debug_exception_handler_requested_(false),
297 #endif
298 uses_nonsfi_mode_(uses_nonsfi_mode),
299 enable_debug_stub_(false),
300 enable_crash_throttling_(false),
301 off_the_record_(off_the_record),
302 process_type_(process_type),
303 profile_directory_(profile_directory),
304 render_view_id_(render_view_id),
305 weak_factory_(this) {
306 process_.reset(content::BrowserChildProcessHost::Create(
307 static_cast<content::ProcessType>(PROCESS_TYPE_NACL_LOADER), this));
309 // Set the display name so the user knows what plugin the process is running.
310 // We aren't on the UI thread so getting the pref locale for language
311 // formatting isn't possible, so IDN will be lost, but this is probably OK
312 // for this use case.
313 process_->SetName(url_formatter::FormatUrl(manifest_url_, std::string()));
315 enable_debug_stub_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
316 switches::kEnableNaClDebug);
317 DCHECK(process_type_ != kUnknownNaClProcessType);
318 enable_crash_throttling_ = process_type_ != kNativeNaClProcessType;
321 NaClProcessHost::~NaClProcessHost() {
322 // Report exit status only if the process was successfully started.
323 if (process_->GetData().handle != base::kNullProcessHandle) {
324 int exit_code = 0;
325 process_->GetTerminationStatus(false /* known_dead */, &exit_code);
326 std::string message =
327 base::StringPrintf("NaCl process exited with status %i (0x%x)",
328 exit_code, exit_code);
329 if (exit_code == 0) {
330 VLOG(1) << message;
331 } else {
332 LOG(ERROR) << message;
334 NaClBrowser::GetInstance()->OnProcessEnd(process_->GetData().id);
337 // Note: this does not work on Windows, though we currently support this
338 // prefetching feature only on POSIX platforms, so it should be ok.
339 #if defined(OS_WIN)
340 DCHECK(prefetched_resource_files_.empty());
341 #else
342 for (size_t i = 0; i < prefetched_resource_files_.size(); ++i) {
343 // The process failed to launch for some reason. Close resource file
344 // handles.
345 base::File file(IPC::PlatformFileForTransitToFile(
346 prefetched_resource_files_[i].file));
347 content::BrowserThread::GetBlockingPool()->PostTask(
348 FROM_HERE,
349 base::Bind(&CloseFile, base::Passed(file.Pass())));
351 #endif
353 if (reply_msg_) {
354 // The process failed to launch for some reason.
355 // Don't keep the renderer hanging.
356 reply_msg_->set_reply_error();
357 nacl_host_message_filter_->Send(reply_msg_);
359 #if defined(OS_WIN)
360 if (process_launched_by_broker_) {
361 NaClBrokerService::GetInstance()->OnLoaderDied();
363 #endif
366 void NaClProcessHost::OnProcessCrashed(int exit_status) {
367 if (enable_crash_throttling_ &&
368 !base::CommandLine::ForCurrentProcess()->HasSwitch(
369 switches::kDisablePnaclCrashThrottling)) {
370 NaClBrowser::GetInstance()->OnProcessCrashed();
374 // This is called at browser startup.
375 // static
376 void NaClProcessHost::EarlyStartup() {
377 NaClBrowser::GetInstance()->EarlyStartup();
378 // Inform NaClBrowser that we exist and will have a debug port at some point.
379 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
380 // Open the IRT file early to make sure that it isn't replaced out from
381 // under us by autoupdate.
382 NaClBrowser::GetInstance()->EnsureIrtAvailable();
383 #endif
384 base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
385 UMA_HISTOGRAM_BOOLEAN(
386 "NaCl.nacl-gdb",
387 !cmd->GetSwitchValuePath(switches::kNaClGdb).empty());
388 UMA_HISTOGRAM_BOOLEAN(
389 "NaCl.nacl-gdb-script",
390 !cmd->GetSwitchValuePath(switches::kNaClGdbScript).empty());
391 UMA_HISTOGRAM_BOOLEAN(
392 "NaCl.enable-nacl-debug",
393 cmd->HasSwitch(switches::kEnableNaClDebug));
394 std::string nacl_debug_mask =
395 cmd->GetSwitchValueASCII(switches::kNaClDebugMask);
396 // By default, exclude debugging SSH and the PNaCl translator.
397 // about::flags only allows empty flags as the default, so replace
398 // the empty setting with the default. To debug all apps, use a wild-card.
399 if (nacl_debug_mask.empty()) {
400 nacl_debug_mask = "!*://*/*ssh_client.nmf,chrome://pnacl-translator/*";
402 NaClBrowser::GetDelegate()->SetDebugPatterns(nacl_debug_mask);
405 // static
406 void NaClProcessHost::SetPpapiKeepAliveThrottleForTesting(
407 unsigned milliseconds) {
408 keepalive_throttle_interval_milliseconds_ = milliseconds;
411 void NaClProcessHost::Launch(
412 NaClHostMessageFilter* nacl_host_message_filter,
413 IPC::Message* reply_msg,
414 const base::FilePath& manifest_path) {
415 nacl_host_message_filter_ = nacl_host_message_filter;
416 reply_msg_ = reply_msg;
417 manifest_path_ = manifest_path;
419 // Do not launch the requested NaCl module if NaCl is marked "unstable" due
420 // to too many crashes within a given time period.
421 if (enable_crash_throttling_ &&
422 !base::CommandLine::ForCurrentProcess()->HasSwitch(
423 switches::kDisablePnaclCrashThrottling) &&
424 NaClBrowser::GetInstance()->IsThrottled()) {
425 SendErrorToRenderer("Process creation was throttled due to excessive"
426 " crashes");
427 delete this;
428 return;
431 const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
432 #if defined(OS_WIN)
433 if (cmd->HasSwitch(switches::kEnableNaClDebug) &&
434 !cmd->HasSwitch(switches::kNoSandbox)) {
435 // We don't switch off sandbox automatically for security reasons.
436 SendErrorToRenderer("NaCl's GDB debug stub requires --no-sandbox flag"
437 " on Windows. See crbug.com/265624.");
438 delete this;
439 return;
441 #endif
442 if (cmd->HasSwitch(switches::kNaClGdb) &&
443 !cmd->HasSwitch(switches::kEnableNaClDebug)) {
444 LOG(WARNING) << "--nacl-gdb flag requires --enable-nacl-debug flag";
447 // Start getting the IRT open asynchronously while we launch the NaCl process.
448 // We'll make sure this actually finished in StartWithLaunchedProcess, below.
449 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
450 nacl_browser->EnsureAllResourcesAvailable();
451 if (!nacl_browser->IsOk()) {
452 SendErrorToRenderer("could not find all the resources needed"
453 " to launch the process");
454 delete this;
455 return;
458 if (uses_nonsfi_mode_) {
459 bool nonsfi_mode_forced_by_command_line = false;
460 bool nonsfi_mode_allowed = false;
461 #if defined(OS_LINUX)
462 nonsfi_mode_forced_by_command_line =
463 cmd->HasSwitch(switches::kEnableNaClNonSfiMode);
464 #if defined(OS_CHROMEOS) && \
465 (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL))
466 nonsfi_mode_allowed = NaClBrowser::GetDelegate()->IsNonSfiModeAllowed(
467 nacl_host_message_filter->profile_directory(), manifest_url_);
468 #endif
469 #endif
470 bool nonsfi_mode_enabled =
471 nonsfi_mode_forced_by_command_line || nonsfi_mode_allowed;
473 if (!nonsfi_mode_enabled) {
474 SendErrorToRenderer(
475 "NaCl non-SFI mode is not available for this platform"
476 " and NaCl module.");
477 delete this;
478 return;
481 if (!enable_ppapi_proxy()) {
482 SendErrorToRenderer(
483 "PPAPI proxy must be enabled on NaCl in Non-SFI mode.");
484 delete this;
485 return;
487 } else {
488 // Rather than creating a socket pair in the renderer, and passing
489 // one side through the browser to sel_ldr, socket pairs are created
490 // in the browser and then passed to the renderer and sel_ldr.
492 // This is mainly for the benefit of Windows, where sockets cannot
493 // be passed in messages, but are copied via DuplicateHandle().
494 // This means the sandboxed renderer cannot send handles to the
495 // browser process.
497 NaClHandle pair[2];
498 // Create a connected socket
499 if (NaClSocketPair(pair) == -1) {
500 SendErrorToRenderer("NaClSocketPair() failed");
501 delete this;
502 return;
504 socket_for_renderer_ = base::File(pair[0]);
505 socket_for_sel_ldr_ = base::File(pair[1]);
506 SetCloseOnExec(pair[0]);
507 SetCloseOnExec(pair[1]);
510 // Create a shared memory region that the renderer and plugin share for
511 // reporting crash information.
512 crash_info_shmem_.CreateAnonymous(kNaClCrashInfoShmemSize);
514 // Launch the process
515 if (!LaunchSelLdr()) {
516 delete this;
520 void NaClProcessHost::OnChannelConnected(int32 peer_pid) {
521 if (!base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
522 switches::kNaClGdb).empty()) {
523 LaunchNaClGdb();
527 #if defined(OS_WIN)
528 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) {
529 process_launched_by_broker_ = true;
530 process_->SetHandle(handle);
531 SetDebugStubPort(nacl::kGdbDebugStubPortUnknown);
532 if (!StartWithLaunchedProcess())
533 delete this;
536 void NaClProcessHost::OnDebugExceptionHandlerLaunchedByBroker(bool success) {
537 IPC::Message* reply = attach_debug_exception_handler_reply_msg_.release();
538 NaClProcessMsg_AttachDebugExceptionHandler::WriteReplyParams(reply, success);
539 Send(reply);
541 #endif
543 // Needed to handle sync messages in OnMessageReceived.
544 bool NaClProcessHost::Send(IPC::Message* msg) {
545 return process_->Send(msg);
548 void NaClProcessHost::LaunchNaClGdb() {
549 const base::CommandLine& command_line =
550 *base::CommandLine::ForCurrentProcess();
551 #if defined(OS_WIN)
552 base::FilePath nacl_gdb =
553 command_line.GetSwitchValuePath(switches::kNaClGdb);
554 base::CommandLine cmd_line(nacl_gdb);
555 #else
556 base::CommandLine::StringType nacl_gdb =
557 command_line.GetSwitchValueNative(switches::kNaClGdb);
558 // We don't support spaces inside arguments in --nacl-gdb switch.
559 base::CommandLine cmd_line(base::SplitString(
560 nacl_gdb, base::CommandLine::StringType(1, ' '),
561 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL));
562 #endif
563 cmd_line.AppendArg("--eval-command");
564 base::FilePath::StringType irt_path(
565 NaClBrowser::GetInstance()->GetIrtFilePath().value());
566 // Avoid back slashes because nacl-gdb uses posix escaping rules on Windows.
567 // See issue https://code.google.com/p/nativeclient/issues/detail?id=3482.
568 std::replace(irt_path.begin(), irt_path.end(), '\\', '/');
569 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-irt \"") + irt_path +
570 FILE_PATH_LITERAL("\""));
571 if (!manifest_path_.empty()) {
572 cmd_line.AppendArg("--eval-command");
573 base::FilePath::StringType manifest_path_value(manifest_path_.value());
574 std::replace(manifest_path_value.begin(), manifest_path_value.end(),
575 '\\', '/');
576 cmd_line.AppendArgNative(FILE_PATH_LITERAL("nacl-manifest \"") +
577 manifest_path_value + FILE_PATH_LITERAL("\""));
579 cmd_line.AppendArg("--eval-command");
580 cmd_line.AppendArg("target remote :4014");
581 base::FilePath script =
582 command_line.GetSwitchValuePath(switches::kNaClGdbScript);
583 if (!script.empty()) {
584 cmd_line.AppendArg("--command");
585 cmd_line.AppendArgNative(script.value());
587 base::LaunchProcess(cmd_line, base::LaunchOptions());
590 bool NaClProcessHost::LaunchSelLdr() {
591 std::string channel_id = process_->GetHost()->CreateChannel();
592 if (channel_id.empty()) {
593 SendErrorToRenderer("CreateChannel() failed");
594 return false;
597 // Build command line for nacl.
599 #if defined(OS_LINUX)
600 int flags = ChildProcessHost::CHILD_ALLOW_SELF;
601 #else
602 int flags = ChildProcessHost::CHILD_NORMAL;
603 #endif
605 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags);
606 if (exe_path.empty())
607 return false;
609 #if defined(OS_WIN)
610 // On Windows 64-bit NaCl loader is called nacl64.exe instead of chrome.exe
611 if (RunningOnWOW64()) {
612 if (!NaClBrowser::GetInstance()->GetNaCl64ExePath(&exe_path)) {
613 SendErrorToRenderer("could not get path to nacl64.exe");
614 return false;
617 #ifdef _DLL
618 // When using the DLL CRT on Windows, we need to amend the PATH to include
619 // the location of the x64 CRT DLLs. This is only the case when using a
620 // component=shared_library build (i.e. generally dev debug builds). The
621 // x86 CRT DLLs are in e.g. out\Debug for chrome.exe etc., so the x64 ones
622 // are put in out\Debug\x64 which we add to the PATH here so that loader
623 // can find them. See http://crbug.com/346034.
624 scoped_ptr<base::Environment> env(base::Environment::Create());
625 static const char kPath[] = "PATH";
626 std::string old_path;
627 base::FilePath module_path;
628 if (!PathService::Get(base::FILE_MODULE, &module_path)) {
629 SendErrorToRenderer("could not get path to current module");
630 return false;
632 std::string x64_crt_path =
633 base::WideToUTF8(module_path.DirName().Append(L"x64").value());
634 if (!env->GetVar(kPath, &old_path)) {
635 env->SetVar(kPath, x64_crt_path);
636 } else if (!IsInPath(old_path, x64_crt_path)) {
637 std::string new_path(old_path);
638 new_path.append(";");
639 new_path.append(x64_crt_path);
640 env->SetVar(kPath, new_path);
642 #endif // _DLL
644 #endif
646 scoped_ptr<base::CommandLine> cmd_line(new base::CommandLine(exe_path));
647 CopyNaClCommandLineArguments(cmd_line.get());
649 cmd_line->AppendSwitchASCII(switches::kProcessType,
650 (uses_nonsfi_mode_ ?
651 switches::kNaClLoaderNonSfiProcess :
652 switches::kNaClLoaderProcess));
653 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
654 if (NaClBrowser::GetDelegate()->DialogsAreSuppressed())
655 cmd_line->AppendSwitch(switches::kNoErrorDialogs);
657 // On Windows we might need to start the broker process to launch a new loader
658 #if defined(OS_WIN)
659 if (RunningOnWOW64()) {
660 if (!NaClBrokerService::GetInstance()->LaunchLoader(
661 weak_factory_.GetWeakPtr(), channel_id)) {
662 SendErrorToRenderer("broker service did not launch process");
663 return false;
665 return true;
667 #endif
668 process_->Launch(
669 new NaClSandboxedProcessLauncherDelegate(process_->GetHost()),
670 cmd_line.release(),
671 true);
672 return true;
675 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) {
676 if (uses_nonsfi_mode_) {
677 // IPC messages relating to NaCl's validation cache must not be exposed
678 // in Non-SFI Mode, otherwise a Non-SFI nexe could use SetKnownToValidate
679 // to create a hole in the SFI sandbox.
680 // In Non-SFI mode, no message is expected.
681 return false;
684 bool handled = true;
685 IPC_BEGIN_MESSAGE_MAP(NaClProcessHost, msg)
686 IPC_MESSAGE_HANDLER(NaClProcessMsg_QueryKnownToValidate,
687 OnQueryKnownToValidate)
688 IPC_MESSAGE_HANDLER(NaClProcessMsg_SetKnownToValidate,
689 OnSetKnownToValidate)
690 IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileToken,
691 OnResolveFileToken)
693 #if defined(OS_WIN)
694 IPC_MESSAGE_HANDLER_DELAY_REPLY(
695 NaClProcessMsg_AttachDebugExceptionHandler,
696 OnAttachDebugExceptionHandler)
697 IPC_MESSAGE_HANDLER(NaClProcessHostMsg_DebugStubPortSelected,
698 OnDebugStubPortSelected)
699 #endif
700 IPC_MESSAGE_HANDLER(NaClProcessHostMsg_PpapiChannelsCreated,
701 OnPpapiChannelsCreated)
702 IPC_MESSAGE_UNHANDLED(handled = false)
703 IPC_END_MESSAGE_MAP()
704 return handled;
707 void NaClProcessHost::OnProcessLaunched() {
708 if (!StartWithLaunchedProcess())
709 delete this;
712 // Called when the NaClBrowser singleton has been fully initialized.
713 void NaClProcessHost::OnResourcesReady() {
714 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
715 if (!nacl_browser->IsReady()) {
716 SendErrorToRenderer("could not acquire shared resources needed by NaCl");
717 delete this;
718 } else if (!StartNaClExecution()) {
719 delete this;
723 void NaClProcessHost::ReplyToRenderer(
724 ScopedChannelHandle ppapi_channel_handle,
725 ScopedChannelHandle trusted_channel_handle,
726 ScopedChannelHandle manifest_service_channel_handle) {
727 #if defined(OS_WIN)
728 // If we are on 64-bit Windows, the NaCl process's sandbox is
729 // managed by a different process from the renderer's sandbox. We
730 // need to inform the renderer's sandbox about the NaCl process so
731 // that the renderer can send handles to the NaCl process using
732 // BrokerDuplicateHandle().
733 if (RunningOnWOW64()) {
734 if (!content::BrokerAddTargetPeer(process_->GetData().handle)) {
735 SendErrorToRenderer("BrokerAddTargetPeer() failed");
736 return;
739 #endif
741 // First, create an |imc_channel_handle| for the renderer.
742 IPC::PlatformFileForTransit imc_handle_for_renderer =
743 IPC::TakeFileHandleForProcess(socket_for_renderer_.Pass(),
744 nacl_host_message_filter_->PeerHandle());
745 if (imc_handle_for_renderer == IPC::InvalidPlatformFileForTransit()) {
746 // Failed to create the handle.
747 SendErrorToRenderer("imc_channel_handle creation failed.");
748 return;
751 // Hereafter, we always send an IPC message with handles including imc_handle
752 // created above which, on Windows, are not closable in this process.
753 std::string error_message;
754 base::SharedMemoryHandle crash_info_shmem_renderer_handle;
755 if (!crash_info_shmem_.ShareToProcess(nacl_host_message_filter_->PeerHandle(),
756 &crash_info_shmem_renderer_handle)) {
757 // On error, we do not send "IPC::ChannelHandle"s to the renderer process.
758 // Note that some other FDs/handles still get sent to the renderer, but
759 // will be closed there.
760 ppapi_channel_handle.reset();
761 trusted_channel_handle.reset();
762 manifest_service_channel_handle.reset();
763 error_message = "ShareToProcess() failed";
766 const ChildProcessData& data = process_->GetData();
767 SendMessageToRenderer(
768 NaClLaunchResult(imc_handle_for_renderer,
769 ppapi_channel_handle.release(),
770 trusted_channel_handle.release(),
771 manifest_service_channel_handle.release(),
772 base::GetProcId(data.handle),
773 data.id,
774 crash_info_shmem_renderer_handle),
775 error_message);
777 // Now that the crash information shmem handles have been shared with the
778 // plugin and the renderer, the browser can close its handle.
779 crash_info_shmem_.Close();
782 void NaClProcessHost::SendErrorToRenderer(const std::string& error_message) {
783 LOG(ERROR) << "NaCl process launch failed: " << error_message;
784 SendMessageToRenderer(NaClLaunchResult(), error_message);
787 void NaClProcessHost::SendMessageToRenderer(
788 const NaClLaunchResult& result,
789 const std::string& error_message) {
790 DCHECK(nacl_host_message_filter_.get());
791 DCHECK(reply_msg_);
792 if (nacl_host_message_filter_.get() == NULL || reply_msg_ == NULL) {
793 // As DCHECKed above, this case should not happen in general.
794 // Though, in this case, unfortunately there is no proper way to release
795 // resources which are already created in |result|. We just give up on
796 // releasing them, and leak them.
797 return;
800 NaClHostMsg_LaunchNaCl::WriteReplyParams(reply_msg_, result, error_message);
801 nacl_host_message_filter_->Send(reply_msg_);
802 nacl_host_message_filter_ = NULL;
803 reply_msg_ = NULL;
806 void NaClProcessHost::SetDebugStubPort(int port) {
807 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
808 nacl_browser->SetProcessGdbDebugStubPort(process_->GetData().id, port);
811 #if defined(OS_POSIX)
812 // TCP port we chose for NaCl debug stub. It can be any other number.
813 static const uint16_t kInitialDebugStubPort = 4014;
815 net::SocketDescriptor NaClProcessHost::GetDebugStubSocketHandle() {
816 // We always try to allocate the default port first. If this fails, we then
817 // allocate any available port.
818 // On success, if the test system has register a handler
819 // (GdbDebugStubPortListener), we fire a notification.
820 uint16 port = kInitialDebugStubPort;
821 net::SocketDescriptor s =
822 net::CreatePlatformSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
823 if (s != net::kInvalidSocket) {
824 // Allow rapid reuse.
825 static const int kOn = 1;
826 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
828 sockaddr_in addr;
829 memset(&addr, 0, sizeof(addr));
830 addr.sin_family = AF_INET;
831 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
832 addr.sin_port = base::HostToNet16(port);
833 if (bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
834 // Try allocate any available port.
835 addr.sin_port = base::HostToNet16(0);
836 if (bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
837 close(s);
838 LOG(ERROR) << "Could not bind socket to port" << port;
839 s = net::kInvalidSocket;
840 } else {
841 sockaddr_in sock_addr;
842 socklen_t sock_addr_size = sizeof(sock_addr);
843 if (getsockname(s, reinterpret_cast<struct sockaddr*>(&sock_addr),
844 &sock_addr_size) != 0 ||
845 sock_addr_size != sizeof(sock_addr)) {
846 LOG(ERROR) << "Could not determine bound port, getsockname() failed";
847 close(s);
848 s = net::kInvalidSocket;
849 } else {
850 port = base::NetToHost16(sock_addr.sin_port);
856 if (s != net::kInvalidSocket) {
857 SetDebugStubPort(port);
859 if (s == net::kInvalidSocket) {
860 LOG(ERROR) << "failed to open socket for debug stub";
861 return net::kInvalidSocket;
862 } else {
863 LOG(WARNING) << "debug stub on port " << port;
865 if (listen(s, 1)) {
866 LOG(ERROR) << "listen() failed on debug stub socket";
867 if (IGNORE_EINTR(close(s)) < 0)
868 PLOG(ERROR) << "failed to close debug stub socket";
869 return net::kInvalidSocket;
871 return s;
873 #endif
875 #if defined(OS_WIN)
876 void NaClProcessHost::OnDebugStubPortSelected(uint16_t debug_stub_port) {
877 CHECK(!uses_nonsfi_mode_);
878 SetDebugStubPort(debug_stub_port);
880 #endif
882 bool NaClProcessHost::StartNaClExecution() {
883 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
885 NaClStartParams params;
887 // Enable PPAPI proxy channel creation only for renderer processes.
888 params.enable_ipc_proxy = enable_ppapi_proxy();
889 params.process_type = process_type_;
890 bool enable_nacl_debug = enable_debug_stub_ &&
891 NaClBrowser::GetDelegate()->URLMatchesDebugPatterns(manifest_url_);
892 if (uses_nonsfi_mode_) {
893 // Currently, non-SFI mode is supported only on Linux.
894 #if defined(OS_LINUX)
895 // In non-SFI mode, we do not use SRPC. Make sure that the socketpair is
896 // not created.
897 DCHECK(!socket_for_sel_ldr_.IsValid());
898 #endif
899 if (enable_nacl_debug) {
900 base::ProcessId pid = base::GetProcId(process_->GetData().handle);
901 LOG(WARNING) << "nonsfi nacl plugin running in " << pid;
903 } else {
904 params.validation_cache_enabled = nacl_browser->ValidationCacheIsEnabled();
905 params.validation_cache_key = nacl_browser->GetValidationCacheKey();
906 params.version = NaClBrowser::GetDelegate()->GetVersionString();
907 params.enable_debug_stub = enable_nacl_debug;
909 const ChildProcessData& data = process_->GetData();
910 params.imc_bootstrap_handle =
911 IPC::TakeFileHandleForProcess(socket_for_sel_ldr_.Pass(), data.handle);
912 if (params.imc_bootstrap_handle == IPC::InvalidPlatformFileForTransit()) {
913 return false;
916 const base::File& irt_file = nacl_browser->IrtFile();
917 CHECK(irt_file.IsValid());
918 // Send over the IRT file handle. We don't close our own copy!
919 params.irt_handle = IPC::GetFileHandleForProcess(
920 irt_file.GetPlatformFile(), data.handle, false);
921 if (params.irt_handle == IPC::InvalidPlatformFileForTransit()) {
922 return false;
925 #if defined(OS_MACOSX)
926 // For dynamic loading support, NaCl requires a file descriptor that
927 // was created in /tmp, since those created with shm_open() are not
928 // mappable with PROT_EXEC. Rather than requiring an extra IPC
929 // round trip out of the sandbox, we create an FD here.
930 base::SharedMemory memory_buffer;
931 base::SharedMemoryCreateOptions options;
932 options.size = 1;
933 options.executable = true;
934 if (!memory_buffer.Create(options)) {
935 DLOG(ERROR) << "Failed to allocate memory buffer";
936 return false;
938 base::SharedMemoryHandle duped_handle =
939 base::SharedMemory::DuplicateHandle(memory_buffer.handle());
940 base::ScopedFD memory_fd(
941 base::SharedMemory::GetFdFromSharedMemoryHandle(duped_handle));
942 if (!memory_fd.is_valid()) {
943 DLOG(ERROR) << "Failed to dup() a file descriptor";
944 return false;
946 params.mac_shm_fd = IPC::GetFileHandleForProcess(
947 memory_fd.release(), data.handle, true);
948 #endif
950 #if defined(OS_POSIX)
951 if (params.enable_debug_stub) {
952 net::SocketDescriptor server_bound_socket = GetDebugStubSocketHandle();
953 if (server_bound_socket != net::kInvalidSocket) {
954 params.debug_stub_server_bound_socket = IPC::GetFileHandleForProcess(
955 server_bound_socket, data.handle, true);
958 #endif
961 if (!crash_info_shmem_.ShareToProcess(process_->GetData().handle,
962 &params.crash_info_shmem_handle)) {
963 DLOG(ERROR) << "Failed to ShareToProcess() a shared memory buffer";
964 return false;
967 // Pass the pre-opened resource files to the loader. We do not have to reopen
968 // resource files here even for SFI mode because the descriptors are not from
969 // a renderer.
970 for (size_t i = 0; i < prefetched_resource_files_.size(); ++i) {
971 process_->Send(new NaClProcessMsg_AddPrefetchedResource(
972 NaClResourcePrefetchResult(
973 prefetched_resource_files_[i].file,
974 // For the same reason as the comment below, always use an empty
975 // base::FilePath for non-SFI mode.
976 (uses_nonsfi_mode_ ? base::FilePath() :
977 prefetched_resource_files_[i].file_path_metadata),
978 prefetched_resource_files_[i].file_key)));
980 prefetched_resource_files_.clear();
982 base::FilePath file_path;
983 if (uses_nonsfi_mode_) {
984 // Don't retrieve the file path when using nonsfi mode; there's no
985 // validation caching in that case, so it's unnecessary work, and would
986 // expose the file path to the plugin.
987 } else {
988 if (NaClBrowser::GetInstance()->GetFilePath(nexe_token_.lo,
989 nexe_token_.hi,
990 &file_path)) {
991 // We have to reopen the file in the browser process; we don't want a
992 // compromised renderer to pass an arbitrary fd that could get loaded
993 // into the plugin process.
994 if (base::PostTaskAndReplyWithResult(
995 content::BrowserThread::GetBlockingPool(),
996 FROM_HERE,
997 base::Bind(OpenNaClReadExecImpl,
998 file_path,
999 true /* is_executable */),
1000 base::Bind(&NaClProcessHost::StartNaClFileResolved,
1001 weak_factory_.GetWeakPtr(),
1002 params,
1003 file_path))) {
1004 return true;
1009 StartNaClFileResolved(params, base::FilePath(), base::File());
1010 return true;
1013 void NaClProcessHost::StartNaClFileResolved(
1014 NaClStartParams params,
1015 const base::FilePath& file_path,
1016 base::File checked_nexe_file) {
1017 if (checked_nexe_file.IsValid()) {
1018 // Release the file received from the renderer. This has to be done on a
1019 // thread where IO is permitted, though.
1020 content::BrowserThread::GetBlockingPool()->PostTask(
1021 FROM_HERE,
1022 base::Bind(&CloseFile, base::Passed(nexe_file_.Pass())));
1023 params.nexe_file_path_metadata = file_path;
1024 params.nexe_file = IPC::TakeFileHandleForProcess(
1025 checked_nexe_file.Pass(), process_->GetData().handle);
1026 } else {
1027 params.nexe_file = IPC::TakeFileHandleForProcess(
1028 nexe_file_.Pass(), process_->GetData().handle);
1031 #if defined(OS_LINUX)
1032 // In Non-SFI mode, create socket pairs for IPC channels here, unlike in
1033 // SFI-mode, in which those channels are created in nacl_listener.cc.
1034 // This is for security hardening. We can then prohibit the socketpair()
1035 // system call in nacl_helper and nacl_helper_nonsfi.
1036 if (uses_nonsfi_mode_) {
1037 // Note: here, because some FDs/handles for the NaCl loader process are
1038 // already opened, they are transferred to NaCl loader process even if
1039 // an error occurs first. It is because this is the simplest way to
1040 // ensure that these FDs/handles don't get leaked and that the NaCl loader
1041 // process will exit properly.
1042 bool has_error = false;
1044 // Note: this check is redundant. We check this earlier.
1045 DCHECK(params.enable_ipc_proxy);
1047 ScopedChannelHandle ppapi_browser_server_channel_handle;
1048 ScopedChannelHandle ppapi_browser_client_channel_handle;
1049 ScopedChannelHandle ppapi_renderer_server_channel_handle;
1050 ScopedChannelHandle ppapi_renderer_client_channel_handle;
1051 ScopedChannelHandle trusted_service_server_channel_handle;
1052 ScopedChannelHandle trusted_service_client_channel_handle;
1053 ScopedChannelHandle manifest_service_server_channel_handle;
1054 ScopedChannelHandle manifest_service_client_channel_handle;
1056 if (!CreateChannelHandlePair(&ppapi_browser_server_channel_handle,
1057 &ppapi_browser_client_channel_handle) ||
1058 !CreateChannelHandlePair(&ppapi_renderer_server_channel_handle,
1059 &ppapi_renderer_client_channel_handle) ||
1060 !CreateChannelHandlePair(&trusted_service_server_channel_handle,
1061 &trusted_service_client_channel_handle) ||
1062 !CreateChannelHandlePair(&manifest_service_server_channel_handle,
1063 &manifest_service_client_channel_handle)) {
1064 SendErrorToRenderer("Failed to create socket pairs.");
1065 has_error = true;
1068 if (!has_error &&
1069 !StartPPAPIProxy(ppapi_browser_client_channel_handle.Pass())) {
1070 SendErrorToRenderer("Failed to start browser PPAPI proxy.");
1071 has_error = true;
1074 if (!has_error) {
1075 // On success, send back a success message to the renderer process,
1076 // and transfer the channel handles for the NaCl loader process to
1077 // |params|.
1078 ReplyToRenderer(ppapi_renderer_client_channel_handle.Pass(),
1079 trusted_service_client_channel_handle.Pass(),
1080 manifest_service_client_channel_handle.Pass());
1081 params.ppapi_browser_channel_handle =
1082 ppapi_browser_server_channel_handle.release();
1083 params.ppapi_renderer_channel_handle =
1084 ppapi_renderer_server_channel_handle.release();
1085 params.trusted_service_channel_handle =
1086 trusted_service_server_channel_handle.release();
1087 params.manifest_service_channel_handle =
1088 manifest_service_server_channel_handle.release();
1091 #endif
1093 process_->Send(new NaClProcessMsg_Start(params));
1096 #if defined(OS_LINUX)
1097 // static
1098 bool NaClProcessHost::CreateChannelHandlePair(
1099 ScopedChannelHandle* channel_handle1,
1100 ScopedChannelHandle* channel_handle2) {
1101 DCHECK(channel_handle1);
1102 DCHECK(channel_handle2);
1104 int fd1 = -1;
1105 int fd2 = -1;
1106 if (!IPC::SocketPair(&fd1, &fd2)) {
1107 return false;
1110 IPC::ChannelHandle handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
1111 handle.socket = base::FileDescriptor(fd1, true);
1112 channel_handle1->reset(handle);
1113 handle.socket = base::FileDescriptor(fd2, true);
1114 channel_handle2->reset(handle);
1115 return true;
1117 #endif
1119 bool NaClProcessHost::StartPPAPIProxy(ScopedChannelHandle channel_handle) {
1120 if (ipc_proxy_channel_.get()) {
1121 // Attempt to open more than 1 browser channel is not supported.
1122 // Shut down the NaCl process.
1123 process_->GetHost()->ForceShutdown();
1124 return false;
1127 DCHECK_EQ(PROCESS_TYPE_NACL_LOADER, process_->GetData().process_type);
1129 ipc_proxy_channel_ = IPC::ChannelProxy::Create(
1130 channel_handle.release(), IPC::Channel::MODE_CLIENT, NULL,
1131 base::ThreadTaskRunnerHandle::Get().get());
1132 // Create the browser ppapi host and enable PPAPI message dispatching to the
1133 // browser process.
1134 ppapi_host_.reset(content::BrowserPpapiHost::CreateExternalPluginProcess(
1135 ipc_proxy_channel_.get(), // sender
1136 permissions_,
1137 process_->GetData().handle,
1138 ipc_proxy_channel_.get(),
1139 nacl_host_message_filter_->render_process_id(),
1140 render_view_id_,
1141 profile_directory_));
1142 ppapi_host_->SetOnKeepaliveCallback(
1143 NaClBrowser::GetDelegate()->GetOnKeepaliveCallback());
1145 ppapi::PpapiNaClPluginArgs args;
1146 args.off_the_record = nacl_host_message_filter_->off_the_record();
1147 args.permissions = permissions_;
1148 args.keepalive_throttle_interval_milliseconds =
1149 keepalive_throttle_interval_milliseconds_;
1150 base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
1151 DCHECK(cmdline);
1152 std::string flag_whitelist[] = {
1153 switches::kV,
1154 switches::kVModule,
1156 for (size_t i = 0; i < arraysize(flag_whitelist); ++i) {
1157 std::string value = cmdline->GetSwitchValueASCII(flag_whitelist[i]);
1158 if (!value.empty()) {
1159 args.switch_names.push_back(flag_whitelist[i]);
1160 args.switch_values.push_back(value);
1164 ppapi_host_->GetPpapiHost()->AddHostFactoryFilter(
1165 scoped_ptr<ppapi::host::HostFactory>(
1166 NaClBrowser::GetDelegate()->CreatePpapiHostFactory(
1167 ppapi_host_.get())));
1169 // Send a message to initialize the IPC dispatchers in the NaCl plugin.
1170 ipc_proxy_channel_->Send(new PpapiMsg_InitializeNaClDispatcher(args));
1171 return true;
1174 // This method is called when NaClProcessHostMsg_PpapiChannelCreated is
1175 // received.
1176 void NaClProcessHost::OnPpapiChannelsCreated(
1177 const IPC::ChannelHandle& raw_ppapi_browser_channel_handle,
1178 const IPC::ChannelHandle& raw_ppapi_renderer_channel_handle,
1179 const IPC::ChannelHandle& raw_trusted_renderer_channel_handle,
1180 const IPC::ChannelHandle& raw_manifest_service_channel_handle) {
1181 ScopedChannelHandle ppapi_browser_channel_handle(
1182 raw_ppapi_browser_channel_handle);
1183 ScopedChannelHandle ppapi_renderer_channel_handle(
1184 raw_ppapi_renderer_channel_handle);
1185 ScopedChannelHandle trusted_renderer_channel_handle(
1186 raw_trusted_renderer_channel_handle);
1187 ScopedChannelHandle manifest_service_channel_handle(
1188 raw_manifest_service_channel_handle);
1190 if (enable_ppapi_proxy()) {
1191 if (!StartPPAPIProxy(ppapi_browser_channel_handle.Pass())) {
1192 SendErrorToRenderer("Browser PPAPI proxy could not start.");
1193 return;
1195 } else {
1196 // If PPAPI proxy is disabled, channel handles should be invalid.
1197 DCHECK(ppapi_browser_channel_handle.get().name.empty());
1198 DCHECK(ppapi_renderer_channel_handle.get().name.empty());
1199 // Invalidate, just in case.
1200 ppapi_browser_channel_handle.reset();
1201 ppapi_renderer_channel_handle.reset();
1204 // Let the renderer know that the IPC channels are established.
1205 ReplyToRenderer(ppapi_renderer_channel_handle.Pass(),
1206 trusted_renderer_channel_handle.Pass(),
1207 manifest_service_channel_handle.Pass());
1210 bool NaClProcessHost::StartWithLaunchedProcess() {
1211 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
1213 if (nacl_browser->IsReady()) {
1214 return StartNaClExecution();
1215 } else if (nacl_browser->IsOk()) {
1216 nacl_browser->WaitForResources(
1217 base::Bind(&NaClProcessHost::OnResourcesReady,
1218 weak_factory_.GetWeakPtr()));
1219 return true;
1220 } else {
1221 SendErrorToRenderer("previously failed to acquire shared resources");
1222 return false;
1226 void NaClProcessHost::OnQueryKnownToValidate(const std::string& signature,
1227 bool* result) {
1228 CHECK(!uses_nonsfi_mode_);
1229 NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
1230 *result = nacl_browser->QueryKnownToValidate(signature, off_the_record_);
1233 void NaClProcessHost::OnSetKnownToValidate(const std::string& signature) {
1234 CHECK(!uses_nonsfi_mode_);
1235 NaClBrowser::GetInstance()->SetKnownToValidate(
1236 signature, off_the_record_);
1239 void NaClProcessHost::OnResolveFileToken(uint64 file_token_lo,
1240 uint64 file_token_hi) {
1241 // Was the file registered?
1243 // Note that the file path cache is of bounded size, and old entries can get
1244 // evicted. If a large number of NaCl modules are being launched at once,
1245 // resolving the file_token may fail because the path cache was thrashed
1246 // while the file_token was in flight. In this case the query fails, and we
1247 // need to fall back to the slower path.
1249 // However: each NaCl process will consume 2-3 entries as it starts up, this
1250 // means that eviction will not happen unless you start up 33+ NaCl processes
1251 // at the same time, and this still requires worst-case timing. As a
1252 // practical matter, no entries should be evicted prematurely.
1253 // The cache itself should take ~ (150 characters * 2 bytes/char + ~60 bytes
1254 // data structure overhead) * 100 = 35k when full, so making it bigger should
1255 // not be a problem, if needed.
1257 // Each NaCl process will consume 2-3 entries because the manifest and main
1258 // nexe are currently not resolved. Shared libraries will be resolved. They
1259 // will be loaded sequentially, so they will only consume a single entry
1260 // while the load is in flight.
1262 // TODO(ncbray): track behavior with UMA. If entries are getting evicted or
1263 // bogus keys are getting queried, this would be good to know.
1264 CHECK(!uses_nonsfi_mode_);
1265 base::FilePath file_path;
1266 if (!NaClBrowser::GetInstance()->GetFilePath(
1267 file_token_lo, file_token_hi, &file_path)) {
1268 Send(new NaClProcessMsg_ResolveFileTokenReply(
1269 file_token_lo,
1270 file_token_hi,
1271 IPC::PlatformFileForTransit(),
1272 base::FilePath()));
1273 return;
1276 // Open the file.
1277 if (!base::PostTaskAndReplyWithResult(
1278 content::BrowserThread::GetBlockingPool(),
1279 FROM_HERE,
1280 base::Bind(OpenNaClReadExecImpl, file_path, true /* is_executable */),
1281 base::Bind(&NaClProcessHost::FileResolved,
1282 weak_factory_.GetWeakPtr(),
1283 file_token_lo,
1284 file_token_hi,
1285 file_path))) {
1286 Send(new NaClProcessMsg_ResolveFileTokenReply(
1287 file_token_lo,
1288 file_token_hi,
1289 IPC::PlatformFileForTransit(),
1290 base::FilePath()));
1294 void NaClProcessHost::FileResolved(
1295 uint64_t file_token_lo,
1296 uint64_t file_token_hi,
1297 const base::FilePath& file_path,
1298 base::File file) {
1299 base::FilePath out_file_path;
1300 IPC::PlatformFileForTransit out_handle;
1301 if (file.IsValid()) {
1302 out_file_path = file_path;
1303 out_handle = IPC::TakeFileHandleForProcess(
1304 file.Pass(),
1305 process_->GetData().handle);
1306 } else {
1307 out_handle = IPC::InvalidPlatformFileForTransit();
1309 Send(new NaClProcessMsg_ResolveFileTokenReply(
1310 file_token_lo,
1311 file_token_hi,
1312 out_handle,
1313 out_file_path));
1316 #if defined(OS_WIN)
1317 void NaClProcessHost::OnAttachDebugExceptionHandler(const std::string& info,
1318 IPC::Message* reply_msg) {
1319 CHECK(!uses_nonsfi_mode_);
1320 if (!AttachDebugExceptionHandler(info, reply_msg)) {
1321 // Send failure message.
1322 NaClProcessMsg_AttachDebugExceptionHandler::WriteReplyParams(reply_msg,
1323 false);
1324 Send(reply_msg);
1328 bool NaClProcessHost::AttachDebugExceptionHandler(const std::string& info,
1329 IPC::Message* reply_msg) {
1330 bool enable_exception_handling = process_type_ == kNativeNaClProcessType;
1331 if (!enable_exception_handling && !enable_debug_stub_) {
1332 DLOG(ERROR) <<
1333 "Debug exception handler requested by NaCl process when not enabled";
1334 return false;
1336 if (debug_exception_handler_requested_) {
1337 // The NaCl process should not request this multiple times.
1338 DLOG(ERROR) << "Multiple AttachDebugExceptionHandler requests received";
1339 return false;
1341 debug_exception_handler_requested_ = true;
1343 base::ProcessId nacl_pid = base::GetProcId(process_->GetData().handle);
1344 // We cannot use process_->GetData().handle because it does not have
1345 // the necessary access rights. We open the new handle here rather
1346 // than in the NaCl broker process in case the NaCl loader process
1347 // dies before the NaCl broker process receives the message we send.
1348 // The debug exception handler uses DebugActiveProcess() to attach,
1349 // but this takes a PID. We need to prevent the NaCl loader's PID
1350 // from being reused before DebugActiveProcess() is called, and
1351 // holding a process handle open achieves this.
1352 base::Process process =
1353 base::Process::OpenWithAccess(nacl_pid,
1354 PROCESS_QUERY_INFORMATION |
1355 PROCESS_SUSPEND_RESUME |
1356 PROCESS_TERMINATE |
1357 PROCESS_VM_OPERATION |
1358 PROCESS_VM_READ |
1359 PROCESS_VM_WRITE |
1360 PROCESS_DUP_HANDLE |
1361 SYNCHRONIZE);
1362 if (!process.IsValid()) {
1363 LOG(ERROR) << "Failed to get process handle";
1364 return false;
1367 attach_debug_exception_handler_reply_msg_.reset(reply_msg);
1368 // If the NaCl loader is 64-bit, the process running its debug
1369 // exception handler must be 64-bit too, so we use the 64-bit NaCl
1370 // broker process for this. Otherwise, on a 32-bit system, we use
1371 // the 32-bit browser process to run the debug exception handler.
1372 if (RunningOnWOW64()) {
1373 return NaClBrokerService::GetInstance()->LaunchDebugExceptionHandler(
1374 weak_factory_.GetWeakPtr(), nacl_pid, process.Handle(),
1375 info);
1376 } else {
1377 NaClStartDebugExceptionHandlerThread(
1378 process.Pass(), info, base::ThreadTaskRunnerHandle::Get(),
1379 base::Bind(&NaClProcessHost::OnDebugExceptionHandlerLaunchedByBroker,
1380 weak_factory_.GetWeakPtr()));
1381 return true;
1384 #endif
1386 } // namespace nacl