Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: components/nacl/loader/nacl_listener.cc

Issue 439713002: Refactoring: Split NaClListener into two delegated classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/nacl/loader/nacl_listener.h" 5 #include "components/nacl/loader/nacl_listener.h"
6 6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdlib.h>
10
11 #if defined(OS_POSIX)
12 #include <unistd.h>
13 #endif
14
15 #include "base/command_line.h" 7 #include "base/command_line.h"
16 #include "base/logging.h" 8 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h" 9 #include "base/memory/scoped_ptr.h"
18 #include "base/message_loop/message_loop.h" 10 #include "base/message_loop/message_loop.h"
19 #include "base/rand_util.h"
20 #include "components/nacl/common/nacl_messages.h" 11 #include "components/nacl/common/nacl_messages.h"
21 #include "components/nacl/loader/nacl_ipc_adapter.h" 12 #include "components/nacl/loader/listener_delegate.h"
22 #include "components/nacl/loader/nacl_validation_db.h"
23 #include "components/nacl/loader/nacl_validation_query.h"
24 #include "ipc/ipc_channel_handle.h" 13 #include "ipc/ipc_channel_handle.h"
25 #include "ipc/ipc_switches.h" 14 #include "ipc/ipc_switches.h"
26 #include "ipc/ipc_sync_channel.h" 15 #include "ipc/ipc_sync_channel.h"
27 #include "ipc/ipc_sync_message_filter.h" 16 #include "ipc/ipc_sync_message_filter.h"
28 #include "native_client/src/public/chrome_main.h"
29 #include "native_client/src/public/nacl_app.h"
30 #include "native_client/src/public/nacl_file_info.h"
31 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
32
33 #if defined(OS_POSIX)
34 #include "base/file_descriptor_posix.h"
35 #endif
36
37 #if defined(OS_LINUX)
38 #include "components/nacl/loader/nonsfi/irt_random.h"
39 #include "components/nacl/loader/nonsfi/nonsfi_main.h"
40 #include "content/public/common/child_process_sandbox_support_linux.h"
41 #include "ppapi/nacl_irt/plugin_startup.h"
42 #endif
43
44 #if defined(OS_WIN)
45 #include <fcntl.h>
46 #include <io.h>
47
48 #include "content/public/common/sandbox_init.h"
49 #endif
50
51 namespace {
52 #if defined(OS_MACOSX)
53
54 // On Mac OS X, shm_open() works in the sandbox but does not give us
55 // an FD that we can map as PROT_EXEC. Rather than doing an IPC to
56 // get an executable SHM region when CreateMemoryObject() is called,
57 // we preallocate one on startup, since NaCl's sel_ldr only needs one
58 // of them. This saves a round trip.
59
60 base::subtle::Atomic32 g_shm_fd = -1;
61
62 int CreateMemoryObject(size_t size, int executable) {
63 if (executable && size > 0) {
64 int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
65 if (result_fd != -1) {
66 // ftruncate() is disallowed by the Mac OS X sandbox and
67 // returns EPERM. Luckily, we can get the same effect with
68 // lseek() + write().
69 if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
70 LOG(ERROR) << "lseek() failed: " << errno;
71 return -1;
72 }
73 if (write(result_fd, "", 1) != 1) {
74 LOG(ERROR) << "write() failed: " << errno;
75 return -1;
76 }
77 return result_fd;
78 }
79 }
80 // Fall back to NaCl's default implementation.
81 return -1;
82 }
83
84 #elif defined(OS_LINUX)
85
86 int CreateMemoryObject(size_t size, int executable) {
87 return content::MakeSharedMemorySegmentViaIPC(size, executable);
88 }
89
90 #elif defined(OS_WIN)
91
92 NaClListener* g_listener;
93
94 // We wrap the function to convert the bool return value to an int.
95 int BrokerDuplicateHandle(NaClHandle source_handle,
96 uint32_t process_id,
97 NaClHandle* target_handle,
98 uint32_t desired_access,
99 uint32_t options) {
100 return content::BrokerDuplicateHandle(source_handle, process_id,
101 target_handle, desired_access,
102 options);
103 }
104
105 int AttachDebugExceptionHandler(const void* info, size_t info_size) {
106 std::string info_string(reinterpret_cast<const char*>(info), info_size);
107 bool result = false;
108 if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
109 info_string, &result)))
110 return false;
111 return result;
112 }
113
114 void DebugStubPortSelectedHandler(uint16_t port) {
115 g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port));
116 }
117
118 #endif
119
120 // Creates the PPAPI IPC channel between the NaCl IRT and the host
121 // (browser/renderer) process, and starts to listen it on the thread where
122 // the given message_loop_proxy runs.
123 // Also, creates and sets the corresponding NaClDesc to the given nap with
124 // the FD #.
125 void SetUpIPCAdapter(IPC::ChannelHandle* handle,
126 scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
127 struct NaClApp* nap,
128 int nacl_fd) {
129 scoped_refptr<NaClIPCAdapter> ipc_adapter(
130 new NaClIPCAdapter(*handle, message_loop_proxy.get()));
131 ipc_adapter->ConnectChannel();
132 #if defined(OS_POSIX)
133 handle->socket =
134 base::FileDescriptor(ipc_adapter->TakeClientFileDescriptor(), true);
135 #endif
136
137 // Pass a NaClDesc to the untrusted side. This will hold a ref to the
138 // NaClIPCAdapter.
139 NaClAppSetDesc(nap, nacl_fd, ipc_adapter->MakeNaClDesc());
140 }
141
142 } // namespace
143
144 class BrowserValidationDBProxy : public NaClValidationDB {
145 public:
146 explicit BrowserValidationDBProxy(NaClListener* listener)
147 : listener_(listener) {
148 }
149
150 virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
151 // Initialize to false so that if the Send fails to write to the return
152 // value we're safe. For example if the message is (for some reason)
153 // dispatched as an async message the return parameter will not be written.
154 bool result = false;
155 if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
156 &result))) {
157 LOG(ERROR) << "Failed to query NaCl validation cache.";
158 result = false;
159 }
160 return result;
161 }
162
163 virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
164 // Caching is optional: NaCl will still work correctly if the IPC fails.
165 if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
166 LOG(ERROR) << "Failed to update NaCl validation cache.";
167 }
168 }
169
170 virtual bool ResolveFileToken(struct NaClFileToken* file_token,
171 int32* fd, std::string* path) OVERRIDE {
172 *fd = -1;
173 *path = "";
174 if (!NaClFileTokenIsValid(file_token)) {
175 return false;
176 }
177 IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit();
178 base::FilePath ipc_path;
179 if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo,
180 file_token->hi,
181 &ipc_fd,
182 &ipc_path))) {
183 return false;
184 }
185 if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
186 return false;
187 }
188 base::PlatformFile handle =
189 IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
190 #if defined(OS_WIN)
191 // On Windows, valid handles are 32 bit unsigned integers so this is safe.
192 *fd = reinterpret_cast<uintptr_t>(handle);
193 #else
194 *fd = handle;
195 #endif
196 // It doesn't matter if the path is invalid UTF8 as long as it's consistent
197 // and unforgeable.
198 *path = ipc_path.AsUTF8Unsafe();
199 return true;
200 }
201
202 private:
203 // The listener never dies, otherwise this might be a dangling reference.
204 NaClListener* listener_;
205 };
206 17
207 18
208 NaClListener::NaClListener() : shutdown_event_(true, false), 19 NaClListener::NaClListener(scoped_ptr<nacl::ListenerDelegate> delegate)
209 io_thread_("NaCl_IOThread"), 20 : delegate_(delegate.Pass()),
210 uses_nonsfi_mode_(false), 21 shutdown_event_(true, false),
211 #if defined(OS_LINUX) 22 io_thread_("NaCl_IOThread"),
212 prereserved_sandbox_size_(0), 23 main_loop_(NULL) {
213 #endif
214 #if defined(OS_POSIX)
215 number_of_cores_(-1), // unknown/error
216 #endif
217 main_loop_(NULL) {
218 io_thread_.StartWithOptions( 24 io_thread_.StartWithOptions(
219 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 25 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
220 #if defined(OS_WIN)
221 DCHECK(g_listener == NULL);
222 g_listener = this;
223 #endif
224 } 26 }
225 27
226 NaClListener::~NaClListener() { 28 NaClListener::~NaClListener() {
227 NOTREACHED(); 29 NOTREACHED();
228 shutdown_event_.Signal(); 30 shutdown_event_.Signal();
229 #if defined(OS_WIN)
230 g_listener = NULL;
231 #endif
232 } 31 }
233 32
234 bool NaClListener::Send(IPC::Message* msg) { 33 bool NaClListener::Send(IPC::Message* msg) {
235 DCHECK(main_loop_ != NULL); 34 DCHECK(main_loop_ != NULL);
236 if (base::MessageLoop::current() == main_loop_) { 35 if (base::MessageLoop::current() == main_loop_) {
237 // This thread owns the channel. 36 // This thread owns the channel.
238 return channel_->Send(msg); 37 return channel_->Send(msg);
239 } else { 38 } else {
240 // This thread does not own the channel. 39 // This thread does not own the channel.
241 return filter_->Send(msg); 40 return filter_->Send(msg);
(...skipping 16 matching lines...) Expand all
258 bool NaClListener::OnMessageReceived(const IPC::Message& msg) { 57 bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
259 bool handled = true; 58 bool handled = true;
260 IPC_BEGIN_MESSAGE_MAP(NaClListener, msg) 59 IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
261 IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart) 60 IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
262 IPC_MESSAGE_UNHANDLED(handled = false) 61 IPC_MESSAGE_UNHANDLED(handled = false)
263 IPC_END_MESSAGE_MAP() 62 IPC_END_MESSAGE_MAP()
264 return handled; 63 return handled;
265 } 64 }
266 65
267 void NaClListener::OnStart(const nacl::NaClStartParams& params) { 66 void NaClListener::OnStart(const nacl::NaClStartParams& params) {
268 if (uses_nonsfi_mode_) { 67 delegate_->Start(
269 StartNonSfi(params); 68 params, io_thread_.message_loop_proxy(), CreateTrustedListener(), this);
270 return;
271 }
272
273 #if defined(OS_LINUX) || defined(OS_MACOSX)
274 int urandom_fd = dup(base::GetUrandomFD());
275 if (urandom_fd < 0) {
276 LOG(ERROR) << "Failed to dup() the urandom FD";
277 return;
278 }
279 NaClChromeMainSetUrandomFd(urandom_fd);
280 #endif
281
282 struct NaClApp* nap = NULL;
283 NaClChromeMainInit();
284 nap = NaClAppCreate();
285 if (nap == NULL) {
286 LOG(ERROR) << "NaClAppCreate() failed";
287 return;
288 }
289
290 IPC::ChannelHandle browser_handle;
291 IPC::ChannelHandle ppapi_renderer_handle;
292
293 if (params.enable_ipc_proxy) {
294 browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
295 ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
296
297 // Create the PPAPI IPC channels between the NaCl IRT and the host
298 // (browser/renderer) processes. The IRT uses these channels to
299 // communicate with the host and to initialize the IPC dispatchers.
300 SetUpIPCAdapter(&browser_handle, io_thread_.message_loop_proxy(),
301 nap, NACL_CHROME_DESC_BASE);
302 SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.message_loop_proxy(),
303 nap, NACL_CHROME_DESC_BASE + 1);
304 }
305
306 IPC::ChannelHandle trusted_renderer_handle = CreateTrustedListener(
307 io_thread_.message_loop_proxy(), &shutdown_event_);
308 if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
309 browser_handle, ppapi_renderer_handle,
310 trusted_renderer_handle, IPC::ChannelHandle())))
311 LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
312
313 std::vector<nacl::FileDescriptor> handles = params.handles;
314 struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
315 if (args == NULL) {
316 LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
317 return;
318 }
319
320 #if defined(OS_LINUX) || defined(OS_MACOSX)
321 args->number_of_cores = number_of_cores_;
322 args->create_memory_object_func = CreateMemoryObject;
323 # if defined(OS_MACOSX)
324 CHECK(handles.size() >= 1);
325 g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
326 handles.pop_back();
327 # endif
328 #endif
329
330 if (params.uses_irt) {
331 CHECK(handles.size() >= 1);
332 NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
333 handles.pop_back();
334
335 #if defined(OS_WIN)
336 args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
337 _O_RDONLY | _O_BINARY);
338 if (args->irt_fd < 0) {
339 LOG(ERROR) << "_open_osfhandle() failed";
340 return;
341 }
342 #else
343 args->irt_fd = irt_handle;
344 #endif
345 } else {
346 // Otherwise, the IRT handle is not even sent.
347 args->irt_fd = -1;
348 }
349
350 if (params.validation_cache_enabled) {
351 // SHA256 block size.
352 CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
353 // The cache structure is not freed and exists until the NaCl process exits.
354 args->validation_cache = CreateValidationCache(
355 new BrowserValidationDBProxy(this), params.validation_cache_key,
356 params.version);
357 }
358
359 CHECK(handles.size() == 1);
360 args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
361 args->enable_exception_handling = params.enable_exception_handling;
362 args->enable_debug_stub = params.enable_debug_stub;
363 args->enable_dyncode_syscalls = params.enable_dyncode_syscalls;
364 if (!params.enable_dyncode_syscalls) {
365 // Bound the initial nexe's code segment size under PNaCl to
366 // reduce the chance of a code spraying attack succeeding (see
367 // https://code.google.com/p/nativeclient/issues/detail?id=3572).
368 // We assume that !params.enable_dyncode_syscalls is synonymous
369 // with PNaCl. We can't apply this arbitrary limit outside of
370 // PNaCl because it might break existing NaCl apps, and this limit
371 // is only useful if the dyncode syscalls are disabled.
372 args->initial_nexe_max_code_bytes = 64 << 20; // 64 MB
373
374 // Indicate that this is a PNaCl module.
375 // TODO(jvoung): Plumb through something indicating that this is PNaCl
376 // instead of relying on enable_dyncode_syscalls.
377 args->pnacl_mode = 1;
378 }
379 #if defined(OS_LINUX) || defined(OS_MACOSX)
380 args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
381 params.debug_stub_server_bound_socket);
382 #endif
383 #if defined(OS_WIN)
384 args->broker_duplicate_handle_func = BrokerDuplicateHandle;
385 args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
386 args->debug_stub_server_port_selected_handler_func =
387 DebugStubPortSelectedHandler;
388 #endif
389 #if defined(OS_LINUX)
390 args->prereserved_sandbox_size = prereserved_sandbox_size_;
391 #endif
392
393 NaClFileInfo nexe_file_info;
394 base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile(
395 params.nexe_file);
396 #if defined(OS_WIN)
397 nexe_file_info.desc =
398 _open_osfhandle(reinterpret_cast<intptr_t>(nexe_file),
399 _O_RDONLY | _O_BINARY);
400 #elif defined(OS_POSIX)
401 nexe_file_info.desc = nexe_file;
402 #else
403 #error Unsupported target platform.
404 #endif
405 nexe_file_info.file_token.lo = params.nexe_token_lo;
406 nexe_file_info.file_token.hi = params.nexe_token_hi;
407 args->nexe_desc = NaClDescIoFromFileInfo(nexe_file_info, NACL_ABI_O_RDONLY);
408
409 NaClChromeMainStartApp(nap, args);
410 } 69 }
411 70
412 void NaClListener::StartNonSfi(const nacl::NaClStartParams& params) { 71 IPC::ChannelHandle NaClListener::CreateTrustedListener() {
413 #if !defined(OS_LINUX)
414 NOTREACHED() << "Non-SFI NaCl is only supported on Linux";
415 #else
416 // Random number source initialization.
417 nacl::nonsfi::SetUrandomFd(base::GetUrandomFD());
418
419 IPC::ChannelHandle browser_handle;
420 IPC::ChannelHandle ppapi_renderer_handle;
421 IPC::ChannelHandle manifest_service_handle;
422
423 if (params.enable_ipc_proxy) {
424 browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
425 ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
426 manifest_service_handle =
427 IPC::Channel::GenerateVerifiedChannelID("nacl");
428
429 // In non-SFI mode, we neither intercept nor rewrite the message using
430 // NaClIPCAdapter, and the channels are connected between the plugin and
431 // the hosts directly. So, the IPC::Channel instances will be created in
432 // the plugin side, because the IPC::Listener needs to live on the
433 // plugin's main thread. However, on initialization (i.e. before loading
434 // the plugin binary), the FD needs to be passed to the hosts. So, here
435 // we create raw FD pairs, and pass the client side FDs to the hosts,
436 // and the server side FDs to the plugin.
437 int browser_server_ppapi_fd;
438 int browser_client_ppapi_fd;
439 int renderer_server_ppapi_fd;
440 int renderer_client_ppapi_fd;
441 int manifest_service_server_fd;
442 int manifest_service_client_fd;
443 if (!IPC::SocketPair(
444 &browser_server_ppapi_fd, &browser_client_ppapi_fd) ||
445 !IPC::SocketPair(
446 &renderer_server_ppapi_fd, &renderer_client_ppapi_fd) ||
447 !IPC::SocketPair(
448 &manifest_service_server_fd, &manifest_service_client_fd)) {
449 LOG(ERROR) << "Failed to create sockets for IPC.";
450 return;
451 }
452
453 // Set the plugin IPC channel FDs.
454 ppapi::SetIPCFileDescriptors(browser_server_ppapi_fd,
455 renderer_server_ppapi_fd,
456 manifest_service_server_fd);
457 ppapi::StartUpPlugin();
458
459 // Send back to the client side IPC channel FD to the host.
460 browser_handle.socket =
461 base::FileDescriptor(browser_client_ppapi_fd, true);
462 ppapi_renderer_handle.socket =
463 base::FileDescriptor(renderer_client_ppapi_fd, true);
464 manifest_service_handle.socket =
465 base::FileDescriptor(manifest_service_client_fd, true);
466 }
467
468 // TODO(teravest): Do we plan on using this renderer handle for nexe loading
469 // for non-SFI? Right now, passing an empty channel handle instead causes
470 // hangs, so we'll keep it.
471 IPC::ChannelHandle trusted_renderer_handle = CreateTrustedListener(
472 io_thread_.message_loop_proxy(), &shutdown_event_);
473 if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
474 browser_handle, ppapi_renderer_handle,
475 trusted_renderer_handle, manifest_service_handle)))
476 LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
477
478 // Ensure that the validation cache key (used as an extra input to the
479 // validation cache's hashing) isn't exposed accidentally.
480 CHECK(!params.validation_cache_enabled);
481 CHECK(params.validation_cache_key.size() == 0);
482 CHECK(params.version.size() == 0);
483 // Ensure that a debug stub FD isn't passed through accidentally.
484 CHECK(!params.enable_debug_stub);
485 CHECK(params.debug_stub_server_bound_socket.fd == -1);
486
487 CHECK(!params.uses_irt);
488 CHECK(params.handles.empty());
489
490 CHECK(params.nexe_file != IPC::InvalidPlatformFileForTransit());
491 CHECK(params.nexe_token_lo == 0);
492 CHECK(params.nexe_token_hi == 0);
493 nacl::nonsfi::MainStart(
494 IPC::PlatformFileForTransitToPlatformFile(params.nexe_file));
495 #endif // defined(OS_LINUX)
496 }
497
498 IPC::ChannelHandle NaClListener::CreateTrustedListener(
499 base::MessageLoopProxy* message_loop_proxy,
500 base::WaitableEvent* shutdown_event) {
501 // The argument passed to GenerateVerifiedChannelID() here MUST be "nacl". 72 // The argument passed to GenerateVerifiedChannelID() here MUST be "nacl".
502 // Using an alternate channel name prevents the pipe from being created on 73 // Using an alternate channel name prevents the pipe from being created on
503 // Windows when the sandbox is enabled. 74 // Windows when the sandbox is enabled.
504 IPC::ChannelHandle trusted_renderer_handle = 75 IPC::ChannelHandle trusted_channel_handle =
505 IPC::Channel::GenerateVerifiedChannelID("nacl"); 76 IPC::Channel::GenerateVerifiedChannelID("nacl");
506 trusted_listener_ = new NaClTrustedListener( 77 trusted_listener_ = new NaClTrustedListener(
507 trusted_renderer_handle, io_thread_.message_loop_proxy().get()); 78 trusted_channel_handle, io_thread_.message_loop_proxy());
508 #if defined(OS_POSIX) 79 #if defined(OS_POSIX)
509 trusted_renderer_handle.socket = base::FileDescriptor( 80 trusted_channel_handle.socket = base::FileDescriptor(
510 trusted_listener_->TakeClientFileDescriptor(), true); 81 trusted_listener_->TakeClientFileDescriptor(), true);
511 #endif 82 #endif
512 return trusted_renderer_handle; 83 return trusted_channel_handle;
513 } 84 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698