Support for unpacked ARM packed relocations.
[chromium-blink-merge.git] / components / nacl / loader / nacl_listener.cc
blob427dd8c1584ad486c4084714cd69d8ba8faa7361
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
3 // found in the LICENSE file.
5 #include "components/nacl/loader/nacl_listener.h"
7 #include <errno.h>
8 #include <stdlib.h>
10 #if defined(OS_POSIX)
11 #include <unistd.h>
12 #endif
14 #include "base/command_line.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/rand_util.h"
19 #include "components/nacl/common/nacl_messages.h"
20 #include "components/nacl/loader/nacl_ipc_adapter.h"
21 #include "components/nacl/loader/nacl_validation_db.h"
22 #include "components/nacl/loader/nacl_validation_query.h"
23 #include "ipc/ipc_channel_handle.h"
24 #include "ipc/ipc_switches.h"
25 #include "ipc/ipc_sync_channel.h"
26 #include "ipc/ipc_sync_message_filter.h"
27 #include "native_client/src/public/chrome_main.h"
28 #include "native_client/src/public/nacl_app.h"
29 #include "native_client/src/public/nacl_file_info.h"
31 #if defined(OS_POSIX)
32 #include "base/file_descriptor_posix.h"
33 #endif
35 #if defined(OS_LINUX)
36 #include "components/nacl/loader/nonsfi/irt_random.h"
37 #include "components/nacl/loader/nonsfi/nonsfi_main.h"
38 #include "content/public/common/child_process_sandbox_support_linux.h"
39 #include "native_client/src/trusted/desc/nacl_desc_io.h"
40 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
41 #include "ppapi/nacl_irt/plugin_startup.h"
42 #endif
44 #if defined(OS_WIN)
45 #include <fcntl.h>
46 #include <io.h>
48 #include "content/public/common/sandbox_init.h"
49 #endif
51 namespace {
52 #if defined(OS_MACOSX)
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.
60 base::subtle::Atomic32 g_shm_fd = -1;
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;
73 if (write(result_fd, "", 1) != 1) {
74 LOG(ERROR) << "write() failed: " << errno;
75 return -1;
77 return result_fd;
80 // Fall back to NaCl's default implementation.
81 return -1;
84 #elif defined(OS_LINUX)
86 int CreateMemoryObject(size_t size, int executable) {
87 return content::MakeSharedMemorySegmentViaIPC(size, executable);
90 #elif defined(OS_WIN)
92 NaClListener* g_listener;
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);
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;
114 void DebugStubPortSelectedHandler(uint16_t port) {
115 g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port));
118 #endif
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
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());
142 } // namespace
144 class BrowserValidationDBProxy : public NaClValidationDB {
145 public:
146 explicit BrowserValidationDBProxy(NaClListener* listener)
147 : listener_(listener) {
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;
160 return result;
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.";
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;
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;
185 if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
186 return false;
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;
202 private:
203 // The listener never dies, otherwise this might be a dangling reference.
204 NaClListener* listener_;
208 NaClListener::NaClListener() : shutdown_event_(true, false),
209 io_thread_("NaCl_IOThread"),
210 uses_nonsfi_mode_(false),
211 #if defined(OS_LINUX)
212 prereserved_sandbox_size_(0),
213 #endif
214 #if defined(OS_POSIX)
215 number_of_cores_(-1), // unknown/error
216 #endif
217 main_loop_(NULL) {
218 io_thread_.StartWithOptions(
219 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
220 #if defined(OS_WIN)
221 DCHECK(g_listener == NULL);
222 g_listener = this;
223 #endif
226 NaClListener::~NaClListener() {
227 NOTREACHED();
228 shutdown_event_.Signal();
229 #if defined(OS_WIN)
230 g_listener = NULL;
231 #endif
234 bool NaClListener::Send(IPC::Message* msg) {
235 DCHECK(main_loop_ != NULL);
236 if (base::MessageLoop::current() == main_loop_) {
237 // This thread owns the channel.
238 return channel_->Send(msg);
239 } else {
240 // This thread does not own the channel.
241 return filter_->Send(msg);
245 void NaClListener::Listen() {
246 std::string channel_name =
247 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
248 switches::kProcessChannelID);
249 channel_ = IPC::SyncChannel::Create(
250 this, io_thread_.message_loop_proxy().get(), &shutdown_event_);
251 filter_ = new IPC::SyncMessageFilter(&shutdown_event_);
252 channel_->AddFilter(filter_.get());
253 channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true);
254 main_loop_ = base::MessageLoop::current();
255 main_loop_->Run();
258 bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
259 bool handled = true;
260 IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
261 IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
262 IPC_MESSAGE_UNHANDLED(handled = false)
263 IPC_END_MESSAGE_MAP()
264 return handled;
267 void NaClListener::OnStart(const nacl::NaClStartParams& params) {
268 if (uses_nonsfi_mode_) {
269 StartNonSfi(params);
270 return;
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;
279 NaClChromeMainSetUrandomFd(urandom_fd);
280 #endif
282 struct NaClApp* nap = NULL;
283 NaClChromeMainInit();
284 nap = NaClAppCreate();
285 if (nap == NULL) {
286 LOG(ERROR) << "NaClAppCreate() failed";
287 return;
290 IPC::ChannelHandle browser_handle;
291 IPC::ChannelHandle ppapi_renderer_handle;
293 if (params.enable_ipc_proxy) {
294 browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
295 ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
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);
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.";
313 std::vector<nacl::FileDescriptor> handles = params.handles;
314 struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
315 if (args == NULL) {
316 LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
317 return;
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
330 if (params.uses_irt) {
331 CHECK(handles.size() >= 1);
332 NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
333 handles.pop_back();
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;
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;
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);
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
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;
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
393 NaClChromeMainStartApp(nap, args);
394 NOTREACHED();
397 void NaClListener::StartNonSfi(const nacl::NaClStartParams& params) {
398 #if !defined(OS_LINUX)
399 NOTREACHED() << "Non-SFI NaCl is only supported on Linux";
400 #else
401 // Random number source initialization.
402 nacl::nonsfi::SetUrandomFd(base::GetUrandomFD());
404 IPC::ChannelHandle browser_handle;
405 IPC::ChannelHandle ppapi_renderer_handle;
406 IPC::ChannelHandle manifest_service_handle;
408 if (params.enable_ipc_proxy) {
409 browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
410 ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
411 manifest_service_handle =
412 IPC::Channel::GenerateVerifiedChannelID("nacl");
414 // In non-SFI mode, we neither intercept nor rewrite the message using
415 // NaClIPCAdapter, and the channels are connected between the plugin and
416 // the hosts directly. So, the IPC::Channel instances will be created in
417 // the plugin side, because the IPC::Listener needs to live on the
418 // plugin's main thread. However, on initialization (i.e. before loading
419 // the plugin binary), the FD needs to be passed to the hosts. So, here
420 // we create raw FD pairs, and pass the client side FDs to the hosts,
421 // and the server side FDs to the plugin.
422 int browser_server_ppapi_fd;
423 int browser_client_ppapi_fd;
424 int renderer_server_ppapi_fd;
425 int renderer_client_ppapi_fd;
426 int manifest_service_server_fd;
427 int manifest_service_client_fd;
428 if (!IPC::SocketPair(
429 &browser_server_ppapi_fd, &browser_client_ppapi_fd) ||
430 !IPC::SocketPair(
431 &renderer_server_ppapi_fd, &renderer_client_ppapi_fd) ||
432 !IPC::SocketPair(
433 &manifest_service_server_fd, &manifest_service_client_fd)) {
434 LOG(ERROR) << "Failed to create sockets for IPC.";
435 return;
438 // Set the plugin IPC channel FDs.
439 ppapi::SetIPCFileDescriptors(browser_server_ppapi_fd,
440 renderer_server_ppapi_fd,
441 manifest_service_server_fd);
442 ppapi::StartUpPlugin();
444 // Send back to the client side IPC channel FD to the host.
445 browser_handle.socket =
446 base::FileDescriptor(browser_client_ppapi_fd, true);
447 ppapi_renderer_handle.socket =
448 base::FileDescriptor(renderer_client_ppapi_fd, true);
449 manifest_service_handle.socket =
450 base::FileDescriptor(manifest_service_client_fd, true);
453 // TODO(teravest): Do we plan on using this renderer handle for nexe loading
454 // for non-SFI? Right now, passing an empty channel handle instead causes
455 // hangs, so we'll keep it.
456 IPC::ChannelHandle trusted_renderer_handle = CreateTrustedListener(
457 io_thread_.message_loop_proxy(), &shutdown_event_);
458 if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
459 browser_handle, ppapi_renderer_handle,
460 trusted_renderer_handle, manifest_service_handle)))
461 LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
463 // Ensure that the validation cache key (used as an extra input to the
464 // validation cache's hashing) isn't exposed accidentally.
465 CHECK(!params.validation_cache_enabled);
466 CHECK(params.validation_cache_key.size() == 0);
467 CHECK(params.version.size() == 0);
468 // Ensure that a debug stub FD isn't passed through accidentally.
469 CHECK(!params.enable_debug_stub);
470 CHECK(params.debug_stub_server_bound_socket.fd == -1);
472 CHECK(!params.uses_irt);
473 CHECK(params.handles.empty());
475 CHECK(params.nexe_file != IPC::InvalidPlatformFileForTransit());
476 nacl::nonsfi::MainStart(
477 NaClDescIoDescFromDescAllocCtor(
478 IPC::PlatformFileForTransitToPlatformFile(params.nexe_file),
479 NACL_ABI_O_RDONLY));
480 #endif // defined(OS_LINUX)
483 IPC::ChannelHandle NaClListener::CreateTrustedListener(
484 base::MessageLoopProxy* message_loop_proxy,
485 base::WaitableEvent* shutdown_event) {
486 // The argument passed to GenerateVerifiedChannelID() here MUST be "nacl".
487 // Using an alternate channel name prevents the pipe from being created on
488 // Windows when the sandbox is enabled.
489 IPC::ChannelHandle trusted_renderer_handle =
490 IPC::Channel::GenerateVerifiedChannelID("nacl");
491 trusted_listener_ = new NaClTrustedListener(
492 trusted_renderer_handle, io_thread_.message_loop_proxy().get());
493 #if defined(OS_POSIX)
494 trusted_renderer_handle.socket = base::FileDescriptor(
495 trusted_listener_->TakeClientFileDescriptor(), true);
496 #endif
497 return trusted_renderer_handle;