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
32 namespace {
33
34 #if !defined(DISABLE_NACL)
SetCloseOnExec(nacl::Handle fd)35 void 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
47 struct NaClProcessHost::NaClInternal {
48 std::vector<nacl::Handle> sockets_for_renderer;
49 std::vector<nacl::Handle> sockets_for_sel_ldr;
50 };
51
NaClProcessHost(const std::wstring & url)52 NaClProcessHost::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
~NaClProcessHost()64 NaClProcessHost::~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
Launch(ChromeRenderMessageFilter * chrome_render_message_filter,int socket_count,IPC::Message * reply_msg)86 bool 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
LaunchSelLdr()133 bool 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
OnProcessLaunchedByBroker(base::ProcessHandle handle)169 void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) {
170 set_handle(handle);
171 OnProcessLaunched();
172 }
173
GetChildTerminationStatus(int * exit_code)174 base::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
OnChildDied()181 void NaClProcessHost::OnChildDied() {
182 #if defined(OS_WIN)
183 NaClBrokerService::GetInstance()->OnLoaderDied();
184 #endif
185 BrowserChildProcessHost::OnChildDied();
186 }
187
OnProcessLaunched()188 void 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
SendStartMessage()243 void 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
OnMessageReceived(const IPC::Message & msg)295 bool NaClProcessHost::OnMessageReceived(const IPC::Message& msg) {
296 NOTREACHED() << "Invalid message with type = " << msg.type();
297 return false;
298 }
299
CanShutdown()300 bool NaClProcessHost::CanShutdown() {
301 return true;
302 }
303