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"
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"
32 #include "base/file_descriptor_posix.h"
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"
48 #include "content/public/common/sandbox_init.h"
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
69 if (lseek(result_fd
, size
- 1, SEEK_SET
) == -1) {
70 LOG(ERROR
) << "lseek() failed: " << errno
;
73 if (write(result_fd
, "", 1) != 1) {
74 LOG(ERROR
) << "write() failed: " << errno
;
80 // Fall back to NaCl's default implementation.
84 #elif defined(OS_LINUX)
86 int CreateMemoryObject(size_t size
, int executable
) {
87 return content::MakeSharedMemorySegmentViaIPC(size
, executable
);
92 NaClListener
* g_listener
;
94 // We wrap the function to convert the bool return value to an int.
95 int BrokerDuplicateHandle(NaClHandle source_handle
,
97 NaClHandle
* target_handle
,
98 uint32_t desired_access
,
100 return content::BrokerDuplicateHandle(source_handle
, process_id
,
101 target_handle
, desired_access
,
105 int AttachDebugExceptionHandler(const void* info
, size_t info_size
) {
106 std::string
info_string(reinterpret_cast<const char*>(info
), info_size
);
108 if (!g_listener
->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
109 info_string
, &result
)))
114 void DebugStubPortSelectedHandler(uint16_t port
) {
115 g_listener
->Send(new NaClProcessHostMsg_DebugStubPortSelected(port
));
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
125 void SetUpIPCAdapter(IPC::ChannelHandle
* handle
,
126 scoped_refptr
<base::MessageLoopProxy
> message_loop_proxy
,
129 scoped_refptr
<NaClIPCAdapter
> ipc_adapter(
130 new NaClIPCAdapter(*handle
, message_loop_proxy
.get()));
131 ipc_adapter
->ConnectChannel();
132 #if defined(OS_POSIX)
134 base::FileDescriptor(ipc_adapter
->TakeClientFileDescriptor(), true);
137 // Pass a NaClDesc to the untrusted side. This will hold a ref to the
139 NaClAppSetDesc(nap
, nacl_fd
, ipc_adapter
->MakeNaClDesc());
144 class BrowserValidationDBProxy
: public NaClValidationDB
{
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.
155 if (!listener_
->Send(new NaClProcessMsg_QueryKnownToValidate(signature
,
157 LOG(ERROR
) << "Failed to query NaCl validation cache.";
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
{
174 if (!NaClFileTokenIsValid(file_token
)) {
177 IPC::PlatformFileForTransit ipc_fd
= IPC::InvalidPlatformFileForTransit();
178 base::FilePath ipc_path
;
179 if (!listener_
->Send(new NaClProcessMsg_ResolveFileToken(file_token
->lo
,
185 if (ipc_fd
== IPC::InvalidPlatformFileForTransit()) {
188 base::PlatformFile handle
=
189 IPC::PlatformFileForTransitToPlatformFile(ipc_fd
);
191 // On Windows, valid handles are 32 bit unsigned integers so this is safe.
192 *fd
= reinterpret_cast<uintptr_t>(handle
);
196 // It doesn't matter if the path is invalid UTF8 as long as it's consistent
198 *path
= ipc_path
.AsUTF8Unsafe();
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),
214 #if defined(OS_POSIX)
215 number_of_cores_(-1), // unknown/error
218 io_thread_
.StartWithOptions(
219 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0));
221 DCHECK(g_listener
== NULL
);
226 NaClListener::~NaClListener() {
228 shutdown_event_
.Signal();
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
);
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();
258 bool NaClListener::OnMessageReceived(const IPC::Message
& msg
) {
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()
267 void NaClListener::OnStart(const nacl::NaClStartParams
& params
) {
268 if (uses_nonsfi_mode_
) {
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";
279 NaClChromeMainSetUrandomFd(urandom_fd
);
282 struct NaClApp
* nap
= NULL
;
283 NaClChromeMainInit();
284 nap
= NaClAppCreate();
286 LOG(ERROR
) << "NaClAppCreate() failed";
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();
316 LOG(ERROR
) << "NaClChromeMainArgsCreate() failed";
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]);
330 if (params
.uses_irt
) {
331 CHECK(handles
.size() >= 1);
332 NaClHandle irt_handle
= nacl::ToNativeHandle(handles
[handles
.size() - 1]);
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";
343 args
->irt_fd
= irt_handle
;
346 // Otherwise, the IRT handle is not even sent.
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
,
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
);
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
;
389 #if defined(OS_LINUX)
390 args
->prereserved_sandbox_size
= prereserved_sandbox_size_
;
393 NaClChromeMainStartApp(nap
, args
);
397 void NaClListener::StartNonSfi(const nacl::NaClStartParams
& params
) {
398 #if !defined(OS_LINUX)
399 NOTREACHED() << "Non-SFI NaCl is only supported on Linux";
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
) ||
431 &renderer_server_ppapi_fd
, &renderer_client_ppapi_fd
) ||
433 &manifest_service_server_fd
, &manifest_service_client_fd
)) {
434 LOG(ERROR
) << "Failed to create sockets for IPC.";
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
),
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);
497 return trusted_renderer_handle
;