Roll leveldb 3f7758:803d69 (v1.17 -> v1.18)
[chromium-blink-merge.git] / components / nacl / loader / nacl_listener.cc
blob0733bf8af638e18b60f1aa33d4876dbee92acd54
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 <fcntl.h>
9 #include <stdlib.h>
10 #include <string.h>
12 #if defined(OS_POSIX)
13 #include <unistd.h>
14 #endif
16 #include "base/command_line.h"
17 #include "base/logging.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/rand_util.h"
21 #include "components/nacl/common/nacl_messages.h"
22 #include "components/nacl/common/nacl_renderer_messages.h"
23 #include "components/nacl/loader/nacl_ipc_adapter.h"
24 #include "components/nacl/loader/nacl_validation_db.h"
25 #include "components/nacl/loader/nacl_validation_query.h"
26 #include "ipc/ipc_channel_handle.h"
27 #include "ipc/ipc_switches.h"
28 #include "ipc/ipc_sync_channel.h"
29 #include "ipc/ipc_sync_message_filter.h"
30 #include "native_client/src/public/chrome_main.h"
31 #include "native_client/src/public/nacl_app.h"
32 #include "native_client/src/public/nacl_file_info.h"
33 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
35 #if defined(OS_POSIX)
36 #include "base/file_descriptor_posix.h"
37 #endif
39 #if defined(OS_LINUX)
40 #include "content/public/common/child_process_sandbox_support_linux.h"
41 #endif
43 #if defined(OS_WIN)
44 #include <fcntl.h>
45 #include <io.h>
47 #include "content/public/common/sandbox_init.h"
48 #endif
50 namespace {
52 NaClListener* g_listener;
54 void FatalLogHandler(const char* data, size_t bytes) {
55 // We use uint32_t rather than size_t for the case when the browser and NaCl
56 // processes are a mix of 32-bit and 64-bit processes.
57 uint32_t copy_bytes = std::min<uint32_t>(static_cast<uint32_t>(bytes),
58 nacl::kNaClCrashInfoMaxLogSize);
60 // We copy the length of the crash data to the start of the shared memory
61 // segment so we know how much to copy.
62 memcpy(g_listener->crash_info_shmem_memory(), &copy_bytes, sizeof(uint32_t));
64 memcpy((char*)g_listener->crash_info_shmem_memory() + sizeof(uint32_t),
65 data,
66 copy_bytes);
69 #if defined(OS_MACOSX)
71 // On Mac OS X, shm_open() works in the sandbox but does not give us
72 // an FD that we can map as PROT_EXEC. Rather than doing an IPC to
73 // get an executable SHM region when CreateMemoryObject() is called,
74 // we preallocate one on startup, since NaCl's sel_ldr only needs one
75 // of them. This saves a round trip.
77 base::subtle::Atomic32 g_shm_fd = -1;
79 int CreateMemoryObject(size_t size, int executable) {
80 if (executable && size > 0) {
81 int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
82 if (result_fd != -1) {
83 // ftruncate() is disallowed by the Mac OS X sandbox and
84 // returns EPERM. Luckily, we can get the same effect with
85 // lseek() + write().
86 if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
87 LOG(ERROR) << "lseek() failed: " << errno;
88 return -1;
90 if (write(result_fd, "", 1) != 1) {
91 LOG(ERROR) << "write() failed: " << errno;
92 return -1;
94 return result_fd;
97 // Fall back to NaCl's default implementation.
98 return -1;
101 #elif defined(OS_LINUX)
103 int CreateMemoryObject(size_t size, int executable) {
104 return content::MakeSharedMemorySegmentViaIPC(size, executable);
107 #elif defined(OS_WIN)
108 // We wrap the function to convert the bool return value to an int.
109 int BrokerDuplicateHandle(NaClHandle source_handle,
110 uint32_t process_id,
111 NaClHandle* target_handle,
112 uint32_t desired_access,
113 uint32_t options) {
114 return content::BrokerDuplicateHandle(source_handle, process_id,
115 target_handle, desired_access,
116 options);
119 int AttachDebugExceptionHandler(const void* info, size_t info_size) {
120 std::string info_string(reinterpret_cast<const char*>(info), info_size);
121 bool result = false;
122 if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
123 info_string, &result)))
124 return false;
125 return result;
128 void DebugStubPortSelectedHandler(uint16_t port) {
129 g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port));
132 #endif
134 // Creates the PPAPI IPC channel between the NaCl IRT and the host
135 // (browser/renderer) process, and starts to listen it on the thread where
136 // the given message_loop_proxy runs.
137 // Also, creates and sets the corresponding NaClDesc to the given nap with
138 // the FD #.
139 scoped_refptr<NaClIPCAdapter> SetUpIPCAdapter(
140 IPC::ChannelHandle* handle,
141 scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
142 struct NaClApp* nap,
143 int nacl_fd) {
144 scoped_refptr<NaClIPCAdapter> ipc_adapter(
145 new NaClIPCAdapter(*handle, message_loop_proxy.get()));
146 ipc_adapter->ConnectChannel();
147 #if defined(OS_POSIX)
148 handle->socket =
149 base::FileDescriptor(ipc_adapter->TakeClientFileDescriptor());
150 #endif
152 // Pass a NaClDesc to the untrusted side. This will hold a ref to the
153 // NaClIPCAdapter.
154 NaClAppSetDesc(nap, nacl_fd, ipc_adapter->MakeNaClDesc());
155 return ipc_adapter;
158 } // namespace
160 class BrowserValidationDBProxy : public NaClValidationDB {
161 public:
162 explicit BrowserValidationDBProxy(NaClListener* listener)
163 : listener_(listener) {
166 bool QueryKnownToValidate(const std::string& signature) override {
167 // Initialize to false so that if the Send fails to write to the return
168 // value we're safe. For example if the message is (for some reason)
169 // dispatched as an async message the return parameter will not be written.
170 bool result = false;
171 if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
172 &result))) {
173 LOG(ERROR) << "Failed to query NaCl validation cache.";
174 result = false;
176 return result;
179 void SetKnownToValidate(const std::string& signature) override {
180 // Caching is optional: NaCl will still work correctly if the IPC fails.
181 if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
182 LOG(ERROR) << "Failed to update NaCl validation cache.";
186 // This is the "old" code path for resolving file tokens. It's only
187 // used for resolving the main nexe.
188 // TODO(teravest): Remove this.
189 bool ResolveFileToken(struct NaClFileToken* file_token,
190 int32* fd,
191 std::string* path) override {
192 *fd = -1;
193 *path = "";
194 if (!NaClFileTokenIsValid(file_token)) {
195 return false;
197 IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit();
198 base::FilePath ipc_path;
199 if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo,
200 file_token->hi,
201 &ipc_fd,
202 &ipc_path))) {
203 return false;
205 if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
206 return false;
208 base::PlatformFile handle =
209 IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
210 #if defined(OS_WIN)
211 // On Windows, valid handles are 32 bit unsigned integers so this is safe.
212 *fd = reinterpret_cast<int32>(handle);
213 #else
214 *fd = handle;
215 #endif
216 // It doesn't matter if the path is invalid UTF8 as long as it's consistent
217 // and unforgeable.
218 *path = ipc_path.AsUTF8Unsafe();
219 return true;
222 private:
223 // The listener never dies, otherwise this might be a dangling reference.
224 NaClListener* listener_;
228 NaClListener::NaClListener() : shutdown_event_(true, false),
229 io_thread_("NaCl_IOThread"),
230 #if defined(OS_LINUX)
231 prereserved_sandbox_size_(0),
232 #endif
233 #if defined(OS_POSIX)
234 number_of_cores_(-1), // unknown/error
235 #endif
236 main_loop_(NULL) {
237 io_thread_.StartWithOptions(
238 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
239 DCHECK(g_listener == NULL);
240 g_listener = this;
243 NaClListener::~NaClListener() {
244 NOTREACHED();
245 shutdown_event_.Signal();
246 g_listener = NULL;
249 bool NaClListener::Send(IPC::Message* msg) {
250 DCHECK(main_loop_ != NULL);
251 if (base::MessageLoop::current() == main_loop_) {
252 // This thread owns the channel.
253 return channel_->Send(msg);
254 } else {
255 // This thread does not own the channel.
256 return filter_->Send(msg);
260 // The NaClProcessMsg_ResolveFileTokenAsyncReply message must be
261 // processed in a MessageFilter so it can be handled on the IO thread.
262 // The main thread used by NaClListener is busy in
263 // NaClChromeMainAppStart(), so it can't be used for servicing messages.
264 class FileTokenMessageFilter : public IPC::MessageFilter {
265 public:
266 bool OnMessageReceived(const IPC::Message& msg) override {
267 bool handled = true;
268 IPC_BEGIN_MESSAGE_MAP(FileTokenMessageFilter, msg)
269 IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileTokenAsyncReply,
270 OnResolveFileTokenAsyncReply)
271 IPC_MESSAGE_UNHANDLED(handled = false)
272 IPC_END_MESSAGE_MAP()
273 return handled;
276 void OnResolveFileTokenAsyncReply(
277 uint64_t token_lo,
278 uint64_t token_hi,
279 IPC::PlatformFileForTransit ipc_fd,
280 base::FilePath file_path) {
281 CHECK(g_listener);
282 g_listener->OnFileTokenResolved(token_lo, token_hi, ipc_fd, file_path);
284 private:
285 ~FileTokenMessageFilter() override {}
288 void NaClListener::Listen() {
289 std::string channel_name =
290 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
291 switches::kProcessChannelID);
292 channel_ = IPC::SyncChannel::Create(
293 this, io_thread_.message_loop_proxy().get(), &shutdown_event_);
294 filter_ = new IPC::SyncMessageFilter(&shutdown_event_);
295 channel_->AddFilter(filter_.get());
296 channel_->AddFilter(new FileTokenMessageFilter());
297 channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true);
298 main_loop_ = base::MessageLoop::current();
299 main_loop_->Run();
302 bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
303 bool handled = true;
304 IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
305 IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
306 IPC_MESSAGE_UNHANDLED(handled = false)
307 IPC_END_MESSAGE_MAP()
308 return handled;
311 void NaClListener::OnStart(const nacl::NaClStartParams& params) {
312 #if defined(OS_LINUX) || defined(OS_MACOSX)
313 int urandom_fd = dup(base::GetUrandomFD());
314 if (urandom_fd < 0) {
315 LOG(ERROR) << "Failed to dup() the urandom FD";
316 return;
318 NaClChromeMainSetUrandomFd(urandom_fd);
319 #endif
320 struct NaClApp* nap = NULL;
321 NaClChromeMainInit();
323 crash_info_shmem_.reset(new base::SharedMemory(params.crash_info_shmem_handle,
324 false));
325 CHECK(crash_info_shmem_->Map(nacl::kNaClCrashInfoShmemSize));
326 NaClSetFatalErrorCallback(&FatalLogHandler);
328 nap = NaClAppCreate();
329 if (nap == NULL) {
330 LOG(ERROR) << "NaClAppCreate() failed";
331 return;
334 IPC::ChannelHandle browser_handle;
335 IPC::ChannelHandle ppapi_renderer_handle;
336 IPC::ChannelHandle manifest_service_handle;
338 if (params.enable_ipc_proxy) {
339 browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
340 ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
341 manifest_service_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
343 // Create the PPAPI IPC channels between the NaCl IRT and the host
344 // (browser/renderer) processes. The IRT uses these channels to
345 // communicate with the host and to initialize the IPC dispatchers.
346 SetUpIPCAdapter(&browser_handle, io_thread_.message_loop_proxy(),
347 nap, NACL_CHROME_DESC_BASE);
348 SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.message_loop_proxy(),
349 nap, NACL_CHROME_DESC_BASE + 1);
351 scoped_refptr<NaClIPCAdapter> manifest_ipc_adapter =
352 SetUpIPCAdapter(&manifest_service_handle,
353 io_thread_.message_loop_proxy(),
354 nap,
355 NACL_CHROME_DESC_BASE + 2);
356 manifest_ipc_adapter->set_resolve_file_token_callback(
357 base::Bind(&NaClListener::ResolveFileToken, base::Unretained(this)));
360 trusted_listener_ = new NaClTrustedListener(
361 IPC::Channel::GenerateVerifiedChannelID("nacl"),
362 io_thread_.message_loop_proxy().get(),
363 &shutdown_event_);
364 if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
365 browser_handle,
366 ppapi_renderer_handle,
367 trusted_listener_->TakeClientChannelHandle(),
368 manifest_service_handle)))
369 LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
371 std::vector<nacl::FileDescriptor> handles = params.handles;
372 struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
373 if (args == NULL) {
374 LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
375 return;
378 #if defined(OS_LINUX) || defined(OS_MACOSX)
379 args->number_of_cores = number_of_cores_;
380 args->create_memory_object_func = CreateMemoryObject;
381 # if defined(OS_MACOSX)
382 CHECK(handles.size() >= 1);
383 g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
384 handles.pop_back();
385 # endif
386 #endif
388 DCHECK(params.process_type != nacl::kUnknownNaClProcessType);
389 CHECK(handles.size() >= 1);
390 NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
391 handles.pop_back();
393 #if defined(OS_WIN)
394 args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
395 _O_RDONLY | _O_BINARY);
396 if (args->irt_fd < 0) {
397 LOG(ERROR) << "_open_osfhandle() failed";
398 return;
400 #else
401 args->irt_fd = irt_handle;
402 #endif
404 if (params.validation_cache_enabled) {
405 // SHA256 block size.
406 CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
407 // The cache structure is not freed and exists until the NaCl process exits.
408 args->validation_cache = CreateValidationCache(
409 new BrowserValidationDBProxy(this), params.validation_cache_key,
410 params.version);
413 CHECK(handles.size() == 1);
414 args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
415 args->enable_debug_stub = params.enable_debug_stub;
417 // Now configure parts that depend on process type.
418 // Start with stricter settings.
419 args->enable_exception_handling = 0;
420 args->enable_dyncode_syscalls = 0;
421 // pnacl_mode=1 mostly disables things (IRT interfaces and syscalls).
422 args->pnacl_mode = 1;
423 // Bound the initial nexe's code segment size under PNaCl to reduce the
424 // chance of a code spraying attack succeeding (see
425 // https://code.google.com/p/nativeclient/issues/detail?id=3572).
426 // We can't apply this arbitrary limit outside of PNaCl because it might
427 // break existing NaCl apps, and this limit is only useful if the dyncode
428 // syscalls are disabled.
429 args->initial_nexe_max_code_bytes = 64 << 20; // 64 MB.
431 if (params.process_type == nacl::kNativeNaClProcessType) {
432 args->enable_exception_handling = 1;
433 args->enable_dyncode_syscalls = 1;
434 args->pnacl_mode = 0;
435 args->initial_nexe_max_code_bytes = 0;
436 } else if (params.process_type == nacl::kPNaClTranslatorProcessType) {
437 // Transitioning the PNaCl translators to use the IRT again:
438 // https://code.google.com/p/nativeclient/issues/detail?id=3914.
439 // Once done, this can be removed.
440 args->irt_load_optional = 1;
443 #if defined(OS_LINUX) || defined(OS_MACOSX)
444 args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
445 params.debug_stub_server_bound_socket);
446 #endif
447 #if defined(OS_WIN)
448 args->broker_duplicate_handle_func = BrokerDuplicateHandle;
449 args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
450 args->debug_stub_server_port_selected_handler_func =
451 DebugStubPortSelectedHandler;
452 #endif
453 #if defined(OS_LINUX)
454 args->prereserved_sandbox_size = prereserved_sandbox_size_;
455 #endif
457 NaClFileInfo nexe_file_info;
458 base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile(
459 params.nexe_file);
460 #if defined(OS_WIN)
461 nexe_file_info.desc =
462 _open_osfhandle(reinterpret_cast<intptr_t>(nexe_file),
463 _O_RDONLY | _O_BINARY);
464 #elif defined(OS_POSIX)
465 nexe_file_info.desc = nexe_file;
466 #else
467 #error Unsupported target platform.
468 #endif
469 nexe_file_info.file_token.lo = params.nexe_token_lo;
470 nexe_file_info.file_token.hi = params.nexe_token_hi;
471 args->nexe_desc = NaClDescIoFromFileInfo(nexe_file_info, NACL_ABI_O_RDONLY);
473 int exit_status;
474 if (!NaClChromeMainStart(nap, args, &exit_status))
475 NaClExit(1);
477 // Report the plugin's exit status if the application started successfully.
478 trusted_listener_->Send(new NaClRendererMsg_ReportExitStatus(exit_status));
479 NaClExit(exit_status);
482 void NaClListener::ResolveFileToken(
483 uint64_t token_lo,
484 uint64_t token_hi,
485 base::Callback<void(IPC::PlatformFileForTransit, base::FilePath)> cb) {
486 if (!Send(new NaClProcessMsg_ResolveFileTokenAsync(token_lo, token_hi))) {
487 cb.Run(IPC::PlatformFileForTransit(), base::FilePath());
488 return;
490 resolved_cb_ = cb;
493 void NaClListener::OnFileTokenResolved(
494 uint64_t token_lo,
495 uint64_t token_hi,
496 IPC::PlatformFileForTransit ipc_fd,
497 base::FilePath file_path) {
498 resolved_cb_.Run(ipc_fd, file_path);
499 resolved_cb_.Reset();