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