// 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;
}