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"
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"
36 #include "base/file_descriptor_posix.h"
40 #include "content/public/common/child_process_sandbox_support_linux.h"
47 #include "content/public/common/sandbox_init.h"
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(), ©_bytes
, sizeof(uint32_t));
64 memcpy((char*)g_listener
->crash_info_shmem_memory() + sizeof(uint32_t),
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
86 if (lseek(result_fd
, size
- 1, SEEK_SET
) == -1) {
87 LOG(ERROR
) << "lseek() failed: " << errno
;
90 if (write(result_fd
, "", 1) != 1) {
91 LOG(ERROR
) << "write() failed: " << errno
;
97 // Fall back to NaCl's default implementation.
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
,
111 NaClHandle
* target_handle
,
112 uint32_t desired_access
,
114 return content::BrokerDuplicateHandle(source_handle
, process_id
,
115 target_handle
, desired_access
,
119 int AttachDebugExceptionHandler(const void* info
, size_t info_size
) {
120 std::string
info_string(reinterpret_cast<const char*>(info
), info_size
);
122 if (!g_listener
->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
123 info_string
, &result
)))
128 void DebugStubPortSelectedHandler(uint16_t port
) {
129 g_listener
->Send(new NaClProcessHostMsg_DebugStubPortSelected(port
));
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
139 scoped_refptr
<NaClIPCAdapter
> SetUpIPCAdapter(
140 IPC::ChannelHandle
* handle
,
141 scoped_refptr
<base::MessageLoopProxy
> message_loop_proxy
,
144 scoped_refptr
<NaClIPCAdapter
> ipc_adapter(
145 new NaClIPCAdapter(*handle
, message_loop_proxy
.get()));
146 ipc_adapter
->ConnectChannel();
147 #if defined(OS_POSIX)
149 base::FileDescriptor(ipc_adapter
->TakeClientFileDescriptor());
152 // Pass a NaClDesc to the untrusted side. This will hold a ref to the
154 NaClAppSetDesc(nap
, nacl_fd
, ipc_adapter
->MakeNaClDesc());
160 class BrowserValidationDBProxy
: public NaClValidationDB
{
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.
171 if (!listener_
->Send(new NaClProcessMsg_QueryKnownToValidate(signature
,
173 LOG(ERROR
) << "Failed to query NaCl validation cache.";
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
,
191 std::string
* path
) override
{
194 if (!NaClFileTokenIsValid(file_token
)) {
197 IPC::PlatformFileForTransit ipc_fd
= IPC::InvalidPlatformFileForTransit();
198 base::FilePath ipc_path
;
199 if (!listener_
->Send(new NaClProcessMsg_ResolveFileToken(file_token
->lo
,
205 if (ipc_fd
== IPC::InvalidPlatformFileForTransit()) {
208 base::PlatformFile handle
=
209 IPC::PlatformFileForTransitToPlatformFile(ipc_fd
);
211 // On Windows, valid handles are 32 bit unsigned integers so this is safe.
212 *fd
= reinterpret_cast<int32
>(handle
);
216 // It doesn't matter if the path is invalid UTF8 as long as it's consistent
218 *path
= ipc_path
.AsUTF8Unsafe();
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),
233 #if defined(OS_POSIX)
234 number_of_cores_(-1), // unknown/error
237 io_thread_
.StartWithOptions(
238 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0));
239 DCHECK(g_listener
== NULL
);
243 NaClListener::~NaClListener() {
245 shutdown_event_
.Signal();
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
);
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
{
266 bool OnMessageReceived(const IPC::Message
& msg
) override
{
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()
276 void OnResolveFileTokenAsyncReply(
279 IPC::PlatformFileForTransit ipc_fd
,
280 base::FilePath file_path
) {
282 g_listener
->OnFileTokenResolved(token_lo
, token_hi
, ipc_fd
, file_path
);
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();
302 bool NaClListener::OnMessageReceived(const IPC::Message
& msg
) {
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()
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";
318 NaClChromeMainSetUrandomFd(urandom_fd
);
320 struct NaClApp
* nap
= NULL
;
321 NaClChromeMainInit();
323 crash_info_shmem_
.reset(new base::SharedMemory(params
.crash_info_shmem_handle
,
325 CHECK(crash_info_shmem_
->Map(nacl::kNaClCrashInfoShmemSize
));
326 NaClSetFatalErrorCallback(&FatalLogHandler
);
328 nap
= NaClAppCreate();
330 LOG(ERROR
) << "NaClAppCreate() failed";
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(),
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(),
364 if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
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();
374 LOG(ERROR
) << "NaClChromeMainArgsCreate() failed";
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]);
388 DCHECK(params
.process_type
!= nacl::kUnknownNaClProcessType
);
389 CHECK(handles
.size() >= 1);
390 NaClHandle irt_handle
= nacl::ToNativeHandle(handles
[handles
.size() - 1]);
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";
401 args
->irt_fd
= irt_handle
;
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
,
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
);
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
;
453 #if defined(OS_LINUX)
454 args
->prereserved_sandbox_size
= prereserved_sandbox_size_
;
457 NaClFileInfo nexe_file_info
;
458 base::PlatformFile nexe_file
= IPC::PlatformFileForTransitToPlatformFile(
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
;
467 #error Unsupported target platform.
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
);
474 if (!NaClChromeMainStart(nap
, args
, &exit_status
))
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(
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());
493 void NaClListener::OnFileTokenResolved(
496 IPC::PlatformFileForTransit ipc_fd
,
497 base::FilePath file_path
) {
498 resolved_cb_
.Run(ipc_fd
, file_path
);
499 resolved_cb_
.Reset();