// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "build/build_config.h" #include "chrome/browser/nacl_host/nacl_process_host.h" #if defined(OS_POSIX) #include <fcntl.h> #endif #include "base/command_line.h" #include "base/metrics/nacl_histogram.h" #include "base/utf_string_conversions.h" #include "base/win/windows_version.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/logging_chrome.h" #include "chrome/common/nacl_cmd_line.h" #include "chrome/common/nacl_messages.h" #include "chrome/common/render_messages.h" #include "chrome/browser/renderer_host/chrome_render_message_filter.h" #include "ipc/ipc_switches.h" #include "native_client/src/shared/imc/nacl_imc.h" #if defined(OS_POSIX) #include "ipc/ipc_channel_posix.h" #elif defined(OS_WIN) #include "chrome/browser/nacl_host/nacl_broker_service_win.h" #endif namespace { #if !defined(DISABLE_NACL) void SetCloseOnExec(nacl::Handle fd) { #if defined(OS_POSIX) int flags = fcntl(fd, F_GETFD); CHECK(flags != -1); int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC); CHECK(rc == 0); #endif } #endif } // namespace struct NaClProcessHost::NaClInternal { std::vector<nacl::Handle> sockets_for_renderer; std::vector<nacl::Handle> sockets_for_sel_ldr; }; NaClProcessHost::NaClProcessHost(const std::wstring& url) : BrowserChildProcessHost(NACL_LOADER_PROCESS), reply_msg_(NULL), internal_(new NaClInternal()), running_on_wow64_(false) { set_name(url); #if defined(OS_WIN) running_on_wow64_ = (base::win::OSInfo::GetInstance()->wow64_status() == base::win::OSInfo::WOW64_ENABLED); #endif } NaClProcessHost::~NaClProcessHost() { if (!reply_msg_) return; // nacl::Close() is not available at link time if DISABLE_NACL is // defined, but we still compile a bunch of other code from this // file anyway. TODO(mseaborn): Make this less messy. #ifndef DISABLE_NACL for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { nacl::Close(internal_->sockets_for_renderer[i]); } for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) { nacl::Close(internal_->sockets_for_sel_ldr[i]); } #endif // OnProcessLaunched didn't get called because the process couldn't launch. // Don't keep the renderer hanging. reply_msg_->set_reply_error(); chrome_render_message_filter_->Send(reply_msg_); } bool NaClProcessHost::Launch( ChromeRenderMessageFilter* chrome_render_message_filter, int socket_count, IPC::Message* reply_msg) { #ifdef DISABLE_NACL NOTIMPLEMENTED() << "Native Client disabled at build time"; return false; #else // Place an arbitrary limit on the number of sockets to limit // exposure in case the renderer is compromised. We can increase // this if necessary. if (socket_count > 8) { return false; } // Rather than creating a socket pair in the renderer, and passing // one side through the browser to sel_ldr, socket pairs are created // in the browser and then passed to the renderer and sel_ldr. // // This is mainly for the benefit of Windows, where sockets cannot // be passed in messages, but are copied via DuplicateHandle(). // This means the sandboxed renderer cannot send handles to the // browser process. for (int i = 0; i < socket_count; i++) { nacl::Handle pair[2]; // Create a connected socket if (nacl::SocketPair(pair) == -1) return false; internal_->sockets_for_renderer.push_back(pair[0]); internal_->sockets_for_sel_ldr.push_back(pair[1]); SetCloseOnExec(pair[0]); SetCloseOnExec(pair[1]); } // Launch the process if (!LaunchSelLdr()) { return false; } UmaNaclHistogramEnumeration(NACL_STARTED); chrome_render_message_filter_ = chrome_render_message_filter; reply_msg_ = reply_msg; return true; #endif // DISABLE_NACL } bool NaClProcessHost::LaunchSelLdr() { if (!CreateChannel()) return false; // Build command line for nacl. FilePath exe_path = GetChildPath(true); if (exe_path.empty()) return false; CommandLine* cmd_line = new CommandLine(exe_path); nacl::CopyNaClCommandLineArguments(cmd_line); cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kNaClLoaderProcess); cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); SetCrashReporterCommandLine(cmd_line); // On Windows we might need to start the broker process to launch a new loader #if defined(OS_WIN) if (running_on_wow64_) { return NaClBrokerService::GetInstance()->LaunchLoader( this, ASCIIToWide(channel_id())); } else { BrowserChildProcessHost::Launch(FilePath(), cmd_line); } #elif defined(OS_POSIX) BrowserChildProcessHost::Launch(true, // use_zygote base::environment_vector(), cmd_line); #endif return true; } void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) { set_handle(handle); OnProcessLaunched(); } base::TerminationStatus NaClProcessHost::GetChildTerminationStatus( int* exit_code) { if (running_on_wow64_) return base::GetTerminationStatus(handle(), exit_code); return BrowserChildProcessHost::GetChildTerminationStatus(exit_code); } void NaClProcessHost::OnChildDied() { #if defined(OS_WIN) NaClBrokerService::GetInstance()->OnLoaderDied(); #endif BrowserChildProcessHost::OnChildDied(); } void NaClProcessHost::OnProcessLaunched() { std::vector<nacl::FileDescriptor> handles_for_renderer; base::ProcessHandle nacl_process_handle; for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) { #if defined(OS_WIN) // Copy the handle into the renderer process. HANDLE handle_in_renderer; DuplicateHandle(base::GetCurrentProcessHandle(), reinterpret_cast<HANDLE>( internal_->sockets_for_renderer[i]), chrome_render_message_filter_->peer_handle(), &handle_in_renderer, GENERIC_READ | GENERIC_WRITE, FALSE, DUPLICATE_CLOSE_SOURCE); handles_for_renderer.push_back( reinterpret_cast<nacl::FileDescriptor>(handle_in_renderer)); #else // No need to dup the imc_handle - we don't pass it anywhere else so // it cannot be closed. nacl::FileDescriptor imc_handle; imc_handle.fd = internal_->sockets_for_renderer[i]; imc_handle.auto_close = true; handles_for_renderer.push_back(imc_handle); #endif } #if defined(OS_WIN) // Copy the process handle into the renderer process. DuplicateHandle(base::GetCurrentProcessHandle(), handle(), chrome_render_message_filter_->peer_handle(), &nacl_process_handle, PROCESS_DUP_HANDLE, FALSE, 0); #else // We use pid as process handle on Posix nacl_process_handle = handle(); #endif // Get the pid of the NaCl process base::ProcessId nacl_process_id = base::GetProcId(handle()); ViewHostMsg_LaunchNaCl::WriteReplyParams( reply_msg_, handles_for_renderer, nacl_process_handle, nacl_process_id); chrome_render_message_filter_->Send(reply_msg_); chrome_render_message_filter_ = NULL; reply_msg_ = NULL; internal_->sockets_for_renderer.clear(); SendStartMessage(); } void NaClProcessHost::SendStartMessage() { std::vector<nacl::FileDescriptor> handles_for_sel_ldr; for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) { #if defined(OS_WIN) HANDLE channel; if (!DuplicateHandle(GetCurrentProcess(), reinterpret_cast<HANDLE>( internal_->sockets_for_sel_ldr[i]), handle(), &channel, GENERIC_READ | GENERIC_WRITE, FALSE, DUPLICATE_CLOSE_SOURCE)) { return; } handles_for_sel_ldr.push_back( reinterpret_cast<nacl::FileDescriptor>(channel)); #else nacl::FileDescriptor channel; channel.fd = dup(internal_->sockets_for_sel_ldr[i]); if (channel.fd < 0) { LOG(ERROR) << "Failed to dup() a file descriptor"; return; } channel.auto_close = true; handles_for_sel_ldr.push_back(channel); #endif } #if defined(OS_MACOSX) // For dynamic loading support, NaCl requires a file descriptor that // was created in /tmp, since those created with shm_open() are not // mappable with PROT_EXEC. Rather than requiring an extra IPC // round trip out of the sandbox, we create an FD here. base::SharedMemory memory_buffer; if (!memory_buffer.CreateAnonymous(/* size= */ 1)) { LOG(ERROR) << "Failed to allocate memory buffer"; return; } nacl::FileDescriptor memory_fd; memory_fd.fd = dup(memory_buffer.handle().fd); if (memory_fd.fd < 0) { LOG(ERROR) << "Failed to dup() a file descriptor"; return; } memory_fd.auto_close = true; handles_for_sel_ldr.push_back(memory_fd); #endif Send(new NaClProcessMsg_Start(handles_for_sel_ldr)); internal_->sockets_for_sel_ldr.clear(); } bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) { NOTREACHED() << "Invalid message with type = " << msg.type(); return false; } bool NaClProcessHost::CanShutdown() { return true; }