1// Copyright (c) 2011 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 "build/build_config.h"
6
7#include "chrome/browser/nacl_host/nacl_process_host.h"
8
9#if defined(OS_POSIX)
10#include <fcntl.h>
11#endif
12
13#include "base/command_line.h"
14#include "base/metrics/nacl_histogram.h"
15#include "base/utf_string_conversions.h"
16#include "base/win/windows_version.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/common/logging_chrome.h"
19#include "chrome/common/nacl_cmd_line.h"
20#include "chrome/common/nacl_messages.h"
21#include "chrome/common/render_messages.h"
22#include "chrome/browser/renderer_host/chrome_render_message_filter.h"
23#include "ipc/ipc_switches.h"
24#include "native_client/src/shared/imc/nacl_imc.h"
25
26#if defined(OS_POSIX)
27#include "ipc/ipc_channel_posix.h"
28#elif defined(OS_WIN)
29#include "chrome/browser/nacl_host/nacl_broker_service_win.h"
30#endif
31
32namespace {
33
34#if !defined(DISABLE_NACL)
35void SetCloseOnExec(nacl::Handle fd) {
36#if defined(OS_POSIX)
37  int flags = fcntl(fd, F_GETFD);
38  CHECK(flags != -1);
39  int rc = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
40  CHECK(rc == 0);
41#endif
42}
43#endif
44
45}  // namespace
46
47struct NaClProcessHost::NaClInternal {
48  std::vector<nacl::Handle> sockets_for_renderer;
49  std::vector<nacl::Handle> sockets_for_sel_ldr;
50};
51
52NaClProcessHost::NaClProcessHost(const std::wstring& url)
53    : BrowserChildProcessHost(NACL_LOADER_PROCESS),
54      reply_msg_(NULL),
55      internal_(new NaClInternal()),
56      running_on_wow64_(false) {
57  set_name(url);
58#if defined(OS_WIN)
59  running_on_wow64_ = (base::win::OSInfo::GetInstance()->wow64_status() ==
60      base::win::OSInfo::WOW64_ENABLED);
61#endif
62}
63
64NaClProcessHost::~NaClProcessHost() {
65  if (!reply_msg_)
66    return;
67
68  // nacl::Close() is not available at link time if DISABLE_NACL is
69  // defined, but we still compile a bunch of other code from this
70  // file anyway.  TODO(mseaborn): Make this less messy.
71#ifndef DISABLE_NACL
72  for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) {
73    nacl::Close(internal_->sockets_for_renderer[i]);
74  }
75  for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) {
76    nacl::Close(internal_->sockets_for_sel_ldr[i]);
77  }
78#endif
79
80  // OnProcessLaunched didn't get called because the process couldn't launch.
81  // Don't keep the renderer hanging.
82  reply_msg_->set_reply_error();
83  chrome_render_message_filter_->Send(reply_msg_);
84}
85
86bool NaClProcessHost::Launch(
87    ChromeRenderMessageFilter* chrome_render_message_filter,
88    int socket_count,
89    IPC::Message* reply_msg) {
90#ifdef DISABLE_NACL
91  NOTIMPLEMENTED() << "Native Client disabled at build time";
92  return false;
93#else
94  // Place an arbitrary limit on the number of sockets to limit
95  // exposure in case the renderer is compromised.  We can increase
96  // this if necessary.
97  if (socket_count > 8) {
98    return false;
99  }
100
101  // Rather than creating a socket pair in the renderer, and passing
102  // one side through the browser to sel_ldr, socket pairs are created
103  // in the browser and then passed to the renderer and sel_ldr.
104  //
105  // This is mainly for the benefit of Windows, where sockets cannot
106  // be passed in messages, but are copied via DuplicateHandle().
107  // This means the sandboxed renderer cannot send handles to the
108  // browser process.
109
110  for (int i = 0; i < socket_count; i++) {
111    nacl::Handle pair[2];
112    // Create a connected socket
113    if (nacl::SocketPair(pair) == -1)
114      return false;
115    internal_->sockets_for_renderer.push_back(pair[0]);
116    internal_->sockets_for_sel_ldr.push_back(pair[1]);
117    SetCloseOnExec(pair[0]);
118    SetCloseOnExec(pair[1]);
119  }
120
121  // Launch the process
122  if (!LaunchSelLdr()) {
123    return false;
124  }
125  UmaNaclHistogramEnumeration(NACL_STARTED);
126  chrome_render_message_filter_ = chrome_render_message_filter;
127  reply_msg_ = reply_msg;
128
129  return true;
130#endif  // DISABLE_NACL
131}
132
133bool NaClProcessHost::LaunchSelLdr() {
134  if (!CreateChannel())
135    return false;
136
137  // Build command line for nacl.
138  FilePath exe_path = GetChildPath(true);
139  if (exe_path.empty())
140    return false;
141
142  CommandLine* cmd_line = new CommandLine(exe_path);
143  nacl::CopyNaClCommandLineArguments(cmd_line);
144
145  cmd_line->AppendSwitchASCII(switches::kProcessType,
146                              switches::kNaClLoaderProcess);
147
148  cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id());
149
150  SetCrashReporterCommandLine(cmd_line);
151
152  // On Windows we might need to start the broker process to launch a new loader
153#if defined(OS_WIN)
154  if (running_on_wow64_) {
155    return NaClBrokerService::GetInstance()->LaunchLoader(
156        this, ASCIIToWide(channel_id()));
157  } else {
158    BrowserChildProcessHost::Launch(FilePath(), cmd_line);
159  }
160#elif defined(OS_POSIX)
161  BrowserChildProcessHost::Launch(true,  // use_zygote
162                                  base::environment_vector(),
163                                  cmd_line);
164#endif
165
166  return true;
167}
168
169void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) {
170  set_handle(handle);
171  OnProcessLaunched();
172}
173
174base::TerminationStatus NaClProcessHost::GetChildTerminationStatus(
175    int* exit_code) {
176  if (running_on_wow64_)
177    return base::GetTerminationStatus(handle(), exit_code);
178  return BrowserChildProcessHost::GetChildTerminationStatus(exit_code);
179}
180
181void NaClProcessHost::OnChildDied() {
182#if defined(OS_WIN)
183  NaClBrokerService::GetInstance()->OnLoaderDied();
184#endif
185  BrowserChildProcessHost::OnChildDied();
186}
187
188void NaClProcessHost::OnProcessLaunched() {
189  std::vector<nacl::FileDescriptor> handles_for_renderer;
190  base::ProcessHandle nacl_process_handle;
191
192  for (size_t i = 0; i < internal_->sockets_for_renderer.size(); i++) {
193#if defined(OS_WIN)
194    // Copy the handle into the renderer process.
195    HANDLE handle_in_renderer;
196    DuplicateHandle(base::GetCurrentProcessHandle(),
197                    reinterpret_cast<HANDLE>(
198                        internal_->sockets_for_renderer[i]),
199                    chrome_render_message_filter_->peer_handle(),
200                    &handle_in_renderer,
201                    GENERIC_READ | GENERIC_WRITE,
202                    FALSE,
203                    DUPLICATE_CLOSE_SOURCE);
204    handles_for_renderer.push_back(
205        reinterpret_cast<nacl::FileDescriptor>(handle_in_renderer));
206#else
207    // No need to dup the imc_handle - we don't pass it anywhere else so
208    // it cannot be closed.
209    nacl::FileDescriptor imc_handle;
210    imc_handle.fd = internal_->sockets_for_renderer[i];
211    imc_handle.auto_close = true;
212    handles_for_renderer.push_back(imc_handle);
213#endif
214  }
215
216#if defined(OS_WIN)
217  // Copy the process handle into the renderer process.
218  DuplicateHandle(base::GetCurrentProcessHandle(),
219                  handle(),
220                  chrome_render_message_filter_->peer_handle(),
221                  &nacl_process_handle,
222                  PROCESS_DUP_HANDLE,
223                  FALSE,
224                  0);
225#else
226  // We use pid as process handle on Posix
227  nacl_process_handle = handle();
228#endif
229
230  // Get the pid of the NaCl process
231  base::ProcessId nacl_process_id = base::GetProcId(handle());
232
233  ViewHostMsg_LaunchNaCl::WriteReplyParams(
234      reply_msg_, handles_for_renderer, nacl_process_handle, nacl_process_id);
235  chrome_render_message_filter_->Send(reply_msg_);
236  chrome_render_message_filter_ = NULL;
237  reply_msg_ = NULL;
238  internal_->sockets_for_renderer.clear();
239
240  SendStartMessage();
241}
242
243void NaClProcessHost::SendStartMessage() {
244  std::vector<nacl::FileDescriptor> handles_for_sel_ldr;
245  for (size_t i = 0; i < internal_->sockets_for_sel_ldr.size(); i++) {
246#if defined(OS_WIN)
247    HANDLE channel;
248    if (!DuplicateHandle(GetCurrentProcess(),
249                         reinterpret_cast<HANDLE>(
250                             internal_->sockets_for_sel_ldr[i]),
251                         handle(),
252                         &channel,
253                         GENERIC_READ | GENERIC_WRITE,
254                         FALSE, DUPLICATE_CLOSE_SOURCE)) {
255      return;
256    }
257    handles_for_sel_ldr.push_back(
258        reinterpret_cast<nacl::FileDescriptor>(channel));
259#else
260    nacl::FileDescriptor channel;
261    channel.fd = dup(internal_->sockets_for_sel_ldr[i]);
262    if (channel.fd < 0) {
263      LOG(ERROR) << "Failed to dup() a file descriptor";
264      return;
265    }
266    channel.auto_close = true;
267    handles_for_sel_ldr.push_back(channel);
268#endif
269  }
270
271#if defined(OS_MACOSX)
272  // For dynamic loading support, NaCl requires a file descriptor that
273  // was created in /tmp, since those created with shm_open() are not
274  // mappable with PROT_EXEC.  Rather than requiring an extra IPC
275  // round trip out of the sandbox, we create an FD here.
276  base::SharedMemory memory_buffer;
277  if (!memory_buffer.CreateAnonymous(/* size= */ 1)) {
278    LOG(ERROR) << "Failed to allocate memory buffer";
279    return;
280  }
281  nacl::FileDescriptor memory_fd;
282  memory_fd.fd = dup(memory_buffer.handle().fd);
283  if (memory_fd.fd < 0) {
284    LOG(ERROR) << "Failed to dup() a file descriptor";
285    return;
286  }
287  memory_fd.auto_close = true;
288  handles_for_sel_ldr.push_back(memory_fd);
289#endif
290
291  Send(new NaClProcessMsg_Start(handles_for_sel_ldr));
292  internal_->sockets_for_sel_ldr.clear();
293}
294
295bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) {
296  NOTREACHED() << "Invalid message with type = " << msg.type();
297  return false;
298}
299
300bool NaClProcessHost::CanShutdown() {
301  return true;
302}
303