Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / components / nacl / loader / nacl_listener.cc
blob7eab6036538413a59730637c21ff0fdeb9a36d0a
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_desc.h"
33 #include "native_client/src/public/nacl_file_info.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 <io.h>
46 #include "content/public/common/sandbox_init.h"
47 #endif
49 namespace {
51 NaClListener* g_listener;
53 void FatalLogHandler(const char* data, size_t bytes) {
54 // We use uint32_t rather than size_t for the case when the browser and NaCl
55 // processes are a mix of 32-bit and 64-bit processes.
56 uint32_t copy_bytes = std::min<uint32_t>(static_cast<uint32_t>(bytes),
57 nacl::kNaClCrashInfoMaxLogSize);
59 // We copy the length of the crash data to the start of the shared memory
60 // segment so we know how much to copy.
61 memcpy(g_listener->crash_info_shmem_memory(), &copy_bytes, sizeof(uint32_t));
63 memcpy((char*)g_listener->crash_info_shmem_memory() + sizeof(uint32_t),
64 data,
65 copy_bytes);
68 #if defined(OS_MACOSX)
70 // On Mac OS X, shm_open() works in the sandbox but does not give us
71 // an FD that we can map as PROT_EXEC. Rather than doing an IPC to
72 // get an executable SHM region when CreateMemoryObject() is called,
73 // we preallocate one on startup, since NaCl's sel_ldr only needs one
74 // of them. This saves a round trip.
76 base::subtle::Atomic32 g_shm_fd = -1;
78 int CreateMemoryObject(size_t size, int executable) {
79 if (executable && size > 0) {
80 int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
81 if (result_fd != -1) {
82 // ftruncate() is disallowed by the Mac OS X sandbox and
83 // returns EPERM. Luckily, we can get the same effect with
84 // lseek() + write().
85 if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
86 LOG(ERROR) << "lseek() failed: " << errno;
87 return -1;
89 if (write(result_fd, "", 1) != 1) {
90 LOG(ERROR) << "write() failed: " << errno;
91 return -1;
93 return result_fd;
96 // Fall back to NaCl's default implementation.
97 return -1;
100 #elif defined(OS_LINUX)
102 int CreateMemoryObject(size_t size, int executable) {
103 return content::MakeSharedMemorySegmentViaIPC(size, executable);
106 #elif defined(OS_WIN)
107 // We wrap the function to convert the bool return value to an int.
108 int BrokerDuplicateHandle(NaClHandle source_handle,
109 uint32_t process_id,
110 NaClHandle* target_handle,
111 uint32_t desired_access,
112 uint32_t options) {
113 return content::BrokerDuplicateHandle(source_handle, process_id,
114 target_handle, desired_access,
115 options);
118 int AttachDebugExceptionHandler(const void* info, size_t info_size) {
119 std::string info_string(reinterpret_cast<const char*>(info), info_size);
120 bool result = false;
121 if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
122 info_string, &result)))
123 return false;
124 return result;
127 void DebugStubPortSelectedHandler(uint16_t port) {
128 g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port));
131 #endif
133 // Creates the PPAPI IPC channel between the NaCl IRT and the host
134 // (browser/renderer) process, and starts to listen it on the thread where
135 // the given message_loop_proxy runs.
136 // Also, creates and sets the corresponding NaClDesc to the given nap with
137 // the FD #.
138 scoped_refptr<NaClIPCAdapter> SetUpIPCAdapter(
139 IPC::ChannelHandle* handle,
140 scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
141 struct NaClApp* nap,
142 int nacl_fd) {
143 scoped_refptr<NaClIPCAdapter> ipc_adapter(
144 new NaClIPCAdapter(*handle, message_loop_proxy.get()));
145 ipc_adapter->ConnectChannel();
146 #if defined(OS_POSIX)
147 handle->socket =
148 base::FileDescriptor(ipc_adapter->TakeClientFileDescriptor());
149 #endif
151 // Pass a NaClDesc to the untrusted side. This will hold a ref to the
152 // NaClIPCAdapter.
153 NaClAppSetDesc(nap, nacl_fd, ipc_adapter->MakeNaClDesc());
154 return ipc_adapter;
157 } // namespace
159 class BrowserValidationDBProxy : public NaClValidationDB {
160 public:
161 explicit BrowserValidationDBProxy(NaClListener* listener)
162 : listener_(listener) {
165 bool QueryKnownToValidate(const std::string& signature) override {
166 // Initialize to false so that if the Send fails to write to the return
167 // value we're safe. For example if the message is (for some reason)
168 // dispatched as an async message the return parameter will not be written.
169 bool result = false;
170 if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
171 &result))) {
172 LOG(ERROR) << "Failed to query NaCl validation cache.";
173 result = false;
175 return result;
178 void SetKnownToValidate(const std::string& signature) override {
179 // Caching is optional: NaCl will still work correctly if the IPC fails.
180 if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
181 LOG(ERROR) << "Failed to update NaCl validation cache.";
185 private:
186 // The listener never dies, otherwise this might be a dangling reference.
187 NaClListener* listener_;
191 NaClListener::NaClListener() : shutdown_event_(true, false),
192 io_thread_("NaCl_IOThread"),
193 #if defined(OS_LINUX)
194 prereserved_sandbox_size_(0),
195 #endif
196 #if defined(OS_POSIX)
197 number_of_cores_(-1), // unknown/error
198 #endif
199 main_loop_(NULL) {
200 io_thread_.StartWithOptions(
201 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
202 DCHECK(g_listener == NULL);
203 g_listener = this;
206 NaClListener::~NaClListener() {
207 NOTREACHED();
208 shutdown_event_.Signal();
209 g_listener = NULL;
212 bool NaClListener::Send(IPC::Message* msg) {
213 DCHECK(main_loop_ != NULL);
214 if (base::MessageLoop::current() == main_loop_) {
215 // This thread owns the channel.
216 return channel_->Send(msg);
217 } else {
218 // This thread does not own the channel.
219 return filter_->Send(msg);
223 // The NaClProcessMsg_ResolveFileTokenAsyncReply message must be
224 // processed in a MessageFilter so it can be handled on the IO thread.
225 // The main thread used by NaClListener is busy in
226 // NaClChromeMainAppStart(), so it can't be used for servicing messages.
227 class FileTokenMessageFilter : public IPC::MessageFilter {
228 public:
229 bool OnMessageReceived(const IPC::Message& msg) override {
230 bool handled = true;
231 IPC_BEGIN_MESSAGE_MAP(FileTokenMessageFilter, msg)
232 IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileTokenReply,
233 OnResolveFileTokenReply)
234 IPC_MESSAGE_UNHANDLED(handled = false)
235 IPC_END_MESSAGE_MAP()
236 return handled;
239 void OnResolveFileTokenReply(
240 uint64_t token_lo,
241 uint64_t token_hi,
242 IPC::PlatformFileForTransit ipc_fd,
243 base::FilePath file_path) {
244 CHECK(g_listener);
245 g_listener->OnFileTokenResolved(token_lo, token_hi, ipc_fd, file_path);
247 private:
248 ~FileTokenMessageFilter() override {}
251 void NaClListener::Listen() {
252 std::string channel_name =
253 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
254 switches::kProcessChannelID);
255 channel_ = IPC::SyncChannel::Create(
256 this, io_thread_.message_loop_proxy().get(), &shutdown_event_);
257 filter_ = new IPC::SyncMessageFilter(&shutdown_event_);
258 channel_->AddFilter(filter_.get());
259 channel_->AddFilter(new FileTokenMessageFilter());
260 channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true);
261 main_loop_ = base::MessageLoop::current();
262 main_loop_->Run();
265 bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
266 bool handled = true;
267 IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
268 IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
269 IPC_MESSAGE_UNHANDLED(handled = false)
270 IPC_END_MESSAGE_MAP()
271 return handled;
274 void NaClListener::OnStart(const nacl::NaClStartParams& params) {
275 #if defined(OS_LINUX) || defined(OS_MACOSX)
276 int urandom_fd = dup(base::GetUrandomFD());
277 if (urandom_fd < 0) {
278 LOG(ERROR) << "Failed to dup() the urandom FD";
279 return;
281 NaClChromeMainSetUrandomFd(urandom_fd);
282 #endif
283 struct NaClApp* nap = NULL;
284 NaClChromeMainInit();
286 crash_info_shmem_.reset(new base::SharedMemory(params.crash_info_shmem_handle,
287 false));
288 CHECK(crash_info_shmem_->Map(nacl::kNaClCrashInfoShmemSize));
289 NaClSetFatalErrorCallback(&FatalLogHandler);
291 nap = NaClAppCreate();
292 if (nap == NULL) {
293 LOG(ERROR) << "NaClAppCreate() failed";
294 return;
297 IPC::ChannelHandle browser_handle;
298 IPC::ChannelHandle ppapi_renderer_handle;
299 IPC::ChannelHandle manifest_service_handle;
301 if (params.enable_ipc_proxy) {
302 browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
303 ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
304 manifest_service_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
306 // Create the PPAPI IPC channels between the NaCl IRT and the host
307 // (browser/renderer) processes. The IRT uses these channels to
308 // communicate with the host and to initialize the IPC dispatchers.
309 SetUpIPCAdapter(&browser_handle, io_thread_.message_loop_proxy(),
310 nap, NACL_CHROME_DESC_BASE);
311 SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.message_loop_proxy(),
312 nap, NACL_CHROME_DESC_BASE + 1);
314 scoped_refptr<NaClIPCAdapter> manifest_ipc_adapter =
315 SetUpIPCAdapter(&manifest_service_handle,
316 io_thread_.message_loop_proxy(),
317 nap,
318 NACL_CHROME_DESC_BASE + 2);
319 manifest_ipc_adapter->set_resolve_file_token_callback(
320 base::Bind(&NaClListener::ResolveFileToken, base::Unretained(this)));
323 trusted_listener_ = new NaClTrustedListener(
324 IPC::Channel::GenerateVerifiedChannelID("nacl"),
325 io_thread_.message_loop_proxy().get(),
326 &shutdown_event_);
327 if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
328 browser_handle,
329 ppapi_renderer_handle,
330 trusted_listener_->TakeClientChannelHandle(),
331 manifest_service_handle)))
332 LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
334 std::vector<nacl::FileDescriptor> handles = params.handles;
335 struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
336 if (args == NULL) {
337 LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
338 return;
341 #if defined(OS_LINUX) || defined(OS_MACOSX)
342 args->number_of_cores = number_of_cores_;
343 args->create_memory_object_func = CreateMemoryObject;
344 # if defined(OS_MACOSX)
345 CHECK(handles.size() >= 1);
346 g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
347 handles.pop_back();
348 # endif
349 #endif
351 DCHECK(params.process_type != nacl::kUnknownNaClProcessType);
352 CHECK(handles.size() >= 1);
353 NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
354 handles.pop_back();
356 #if defined(OS_WIN)
357 args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
358 _O_RDONLY | _O_BINARY);
359 if (args->irt_fd < 0) {
360 LOG(ERROR) << "_open_osfhandle() failed";
361 return;
363 #else
364 args->irt_fd = irt_handle;
365 #endif
367 if (params.validation_cache_enabled) {
368 // SHA256 block size.
369 CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
370 // The cache structure is not freed and exists until the NaCl process exits.
371 args->validation_cache = CreateValidationCache(
372 new BrowserValidationDBProxy(this), params.validation_cache_key,
373 params.version);
376 CHECK(handles.size() == 1);
377 args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
378 args->enable_debug_stub = params.enable_debug_stub;
380 // Now configure parts that depend on process type.
381 // Start with stricter settings.
382 args->enable_exception_handling = 0;
383 args->enable_dyncode_syscalls = 0;
384 // pnacl_mode=1 mostly disables things (IRT interfaces and syscalls).
385 args->pnacl_mode = 1;
386 // Bound the initial nexe's code segment size under PNaCl to reduce the
387 // chance of a code spraying attack succeeding (see
388 // https://code.google.com/p/nativeclient/issues/detail?id=3572).
389 // We can't apply this arbitrary limit outside of PNaCl because it might
390 // break existing NaCl apps, and this limit is only useful if the dyncode
391 // syscalls are disabled.
392 args->initial_nexe_max_code_bytes = 64 << 20; // 64 MB.
394 if (params.process_type == nacl::kNativeNaClProcessType) {
395 args->enable_exception_handling = 1;
396 args->enable_dyncode_syscalls = 1;
397 args->pnacl_mode = 0;
398 args->initial_nexe_max_code_bytes = 0;
399 } else if (params.process_type == nacl::kPNaClTranslatorProcessType) {
400 // Transitioning the PNaCl translators to use the IRT again:
401 // https://code.google.com/p/nativeclient/issues/detail?id=3914.
402 // Once done, this can be removed.
403 args->irt_load_optional = 1;
406 #if defined(OS_LINUX) || defined(OS_MACOSX)
407 args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
408 params.debug_stub_server_bound_socket);
409 #endif
410 #if defined(OS_WIN)
411 args->broker_duplicate_handle_func = BrokerDuplicateHandle;
412 args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
413 args->debug_stub_server_port_selected_handler_func =
414 DebugStubPortSelectedHandler;
415 #endif
416 #if defined(OS_LINUX)
417 args->prereserved_sandbox_size = prereserved_sandbox_size_;
418 #endif
420 base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile(
421 params.nexe_file);
422 std::string file_path_str = params.nexe_file_path_metadata.AsUTF8Unsafe();
423 args->nexe_desc = NaClDescCreateWithFilePathMetadata(nexe_file,
424 file_path_str.c_str());
426 int exit_status;
427 if (!NaClChromeMainStart(nap, args, &exit_status))
428 NaClExit(1);
430 // Report the plugin's exit status if the application started successfully.
431 trusted_listener_->Send(new NaClRendererMsg_ReportExitStatus(exit_status));
432 NaClExit(exit_status);
435 void NaClListener::ResolveFileToken(
436 uint64_t token_lo,
437 uint64_t token_hi,
438 base::Callback<void(IPC::PlatformFileForTransit, base::FilePath)> cb) {
439 if (!Send(new NaClProcessMsg_ResolveFileToken(token_lo, token_hi))) {
440 cb.Run(IPC::PlatformFileForTransit(), base::FilePath());
441 return;
443 resolved_cb_ = cb;
446 void NaClListener::OnFileTokenResolved(
447 uint64_t token_lo,
448 uint64_t token_hi,
449 IPC::PlatformFileForTransit ipc_fd,
450 base::FilePath file_path) {
451 resolved_cb_.Run(ipc_fd, file_path);
452 resolved_cb_.Reset();