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_ipc_adapter.h"
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/shared_memory.h"
15 #include "base/task_runner_util.h"
16 #include "build/build_config.h"
17 #include "ipc/ipc_channel.h"
18 #include "ipc/ipc_platform_file.h"
19 #include "native_client/src/trusted/desc/nacl_desc_base.h"
20 #include "native_client/src/trusted/desc/nacl_desc_custom.h"
21 #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
22 #include "native_client/src/trusted/desc/nacl_desc_io.h"
23 #include "native_client/src/trusted/desc/nacl_desc_quota.h"
24 #include "native_client/src/trusted/desc/nacl_desc_quota_interface.h"
25 #include "native_client/src/trusted/desc/nacl_desc_sync_socket.h"
26 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
27 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
28 #include "native_client/src/trusted/validator/rich_file_info.h"
29 #include "ppapi/c/ppb_file_io.h"
30 #include "ppapi/proxy/ppapi_messages.h"
31 #include "ppapi/proxy/serialized_handle.h"
33 using ppapi::proxy::NaClMessageScanner
;
37 enum BufferSizeStatus
{
38 // The buffer contains a full message with no extra bytes.
41 // The message doesn't fit and the buffer contains only some of it.
44 // The buffer contains a full message + extra data.
45 MESSAGE_HAS_EXTRA_DATA
48 BufferSizeStatus
GetBufferStatus(const char* data
, size_t len
) {
49 if (len
< sizeof(NaClIPCAdapter::NaClMessageHeader
))
50 return MESSAGE_IS_TRUNCATED
;
52 const NaClIPCAdapter::NaClMessageHeader
* header
=
53 reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader
*>(data
);
55 sizeof(NaClIPCAdapter::NaClMessageHeader
) + header
->payload_size
;
57 if (len
== message_size
)
58 return MESSAGE_IS_COMPLETE
;
59 if (len
> message_size
)
60 return MESSAGE_HAS_EXTRA_DATA
;
61 return MESSAGE_IS_TRUNCATED
;
64 //------------------------------------------------------------------------------
65 // This object allows the NaClDesc to hold a reference to a NaClIPCAdapter and
66 // forward calls to it.
68 explicit DescThunker(NaClIPCAdapter
* adapter_arg
)
69 : adapter(adapter_arg
) {
71 scoped_refptr
<NaClIPCAdapter
> adapter
;
74 NaClIPCAdapter
* ToAdapter(void* handle
) {
75 return static_cast<DescThunker
*>(handle
)->adapter
.get();
78 // NaClDescCustom implementation.
79 void NaClDescCustomDestroy(void* handle
) {
80 delete static_cast<DescThunker
*>(handle
);
83 ssize_t
NaClDescCustomSendMsg(void* handle
, const NaClImcTypedMsgHdr
* msg
,
85 return static_cast<ssize_t
>(ToAdapter(handle
)->Send(msg
));
88 ssize_t
NaClDescCustomRecvMsg(void* handle
, NaClImcTypedMsgHdr
* msg
,
90 return static_cast<ssize_t
>(ToAdapter(handle
)->BlockingReceive(msg
));
93 NaClDesc
* MakeNaClDescCustom(NaClIPCAdapter
* adapter
) {
94 NaClDescCustomFuncs funcs
= NACL_DESC_CUSTOM_FUNCS_INITIALIZER
;
95 funcs
.Destroy
= NaClDescCustomDestroy
;
96 funcs
.SendMsg
= NaClDescCustomSendMsg
;
97 funcs
.RecvMsg
= NaClDescCustomRecvMsg
;
98 // NaClDescMakeCustomDesc gives us a reference on the returned NaClDesc.
99 return NaClDescMakeCustomDesc(new DescThunker(adapter
), &funcs
);
102 //------------------------------------------------------------------------------
103 // This object is passed to a NaClDescQuota to intercept writes and forward them
104 // to the NaClIPCAdapter, which checks quota. This is a NaCl-style struct. Don't
105 // add non-trivial fields or virtual methods. Construction should use malloc,
106 // because this is owned by the NaClDesc, and the NaCl Dtor code will call free.
107 struct QuotaInterface
{
108 // The "base" struct must be first. NaCl code expects a NaCl style ref-counted
109 // object, so the "vtable" and other base class fields must be first.
110 struct NaClDescQuotaInterface base NACL_IS_REFCOUNT_SUBCLASS
;
112 NaClMessageScanner::FileIO
* file_io
;
115 static void QuotaInterfaceDtor(NaClRefCount
* nrcp
) {
116 // Trivial class, just pass through to the "base" struct Dtor.
117 nrcp
->vtbl
= reinterpret_cast<NaClRefCountVtbl
*>(
118 const_cast<NaClDescQuotaInterfaceVtbl
*>(&kNaClDescQuotaInterfaceVtbl
));
119 (*nrcp
->vtbl
->Dtor
)(nrcp
);
122 static int64_t QuotaInterfaceWriteRequest(NaClDescQuotaInterface
* ndqi
,
123 const uint8_t* /* unused_id */,
126 if (offset
< 0 || length
< 0)
128 if (std::numeric_limits
<int64_t>::max() - length
< offset
)
129 return 0; // offset + length would overflow.
130 int64_t max_offset
= offset
+ length
;
134 QuotaInterface
* quota_interface
= reinterpret_cast<QuotaInterface
*>(ndqi
);
135 NaClMessageScanner::FileIO
* file_io
= quota_interface
->file_io
;
136 int64_t increase
= max_offset
- file_io
->max_written_offset();
137 if (increase
<= 0 || file_io
->Grow(increase
))
143 static int64_t QuotaInterfaceFtruncateRequest(NaClDescQuotaInterface
* ndqi
,
144 const uint8_t* /* unused_id */,
146 // We can't implement SetLength on the plugin side due to sandbox limitations.
147 // See crbug.com/156077.
152 static const struct NaClDescQuotaInterfaceVtbl kQuotaInterfaceVtbl
= {
156 QuotaInterfaceWriteRequest
,
157 QuotaInterfaceFtruncateRequest
160 NaClDesc
* MakeNaClDescQuota(
161 NaClMessageScanner::FileIO
* file_io
,
162 NaClDesc
* wrapped_desc
) {
163 // Create the QuotaInterface.
164 QuotaInterface
* quota_interface
=
165 static_cast<QuotaInterface
*>(malloc(sizeof *quota_interface
));
166 if (quota_interface
&& NaClDescQuotaInterfaceCtor("a_interface
->base
)) {
167 quota_interface
->base
.base
.vtbl
=
168 (struct NaClRefCountVtbl
*)(&kQuotaInterfaceVtbl
);
169 // QuotaInterface is a trivial class, so skip the ctor.
170 quota_interface
->file_io
= file_io
;
171 // Create the NaClDescQuota.
172 NaClDescQuota
* desc
= static_cast<NaClDescQuota
*>(malloc(sizeof *desc
));
173 uint8_t unused_id
[NACL_DESC_QUOTA_FILE_ID_LEN
] = {0};
174 if (desc
&& NaClDescQuotaCtor(desc
,
177 "a_interface
->base
)) {
181 NaClDescUnref(reinterpret_cast<NaClDesc
*>(desc
));
185 NaClDescQuotaInterfaceUnref("a_interface
->base
);
190 //------------------------------------------------------------------------------
192 void DeleteChannel(IPC::Channel
* channel
) {
196 // Translates Pepper's read/write open flags into the NaCl equivalents.
197 // Since the host has already opened the file, flags such as O_CREAT, O_TRUNC,
198 // and O_EXCL don't make sense, so we filter those out. If no read or write
199 // flags are set, the function returns NACL_ABI_O_RDONLY as a safe fallback.
200 int TranslatePepperFileReadWriteOpenFlags(int32_t pp_open_flags
) {
201 bool read
= (pp_open_flags
& PP_FILEOPENFLAG_READ
) != 0;
202 bool write
= (pp_open_flags
& PP_FILEOPENFLAG_WRITE
) != 0;
203 bool append
= (pp_open_flags
& PP_FILEOPENFLAG_APPEND
) != 0;
205 int nacl_open_flag
= NACL_ABI_O_RDONLY
; // NACL_ABI_O_RDONLY == 0.
206 if (read
&& (write
|| append
)) {
207 nacl_open_flag
= NACL_ABI_O_RDWR
;
208 } else if (write
|| append
) {
209 nacl_open_flag
= NACL_ABI_O_WRONLY
;
211 DLOG(WARNING
) << "One of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, "
212 << "or PP_FILEOPENFLAG_APPEND should be set.";
215 nacl_open_flag
|= NACL_ABI_O_APPEND
;
217 return nacl_open_flag
;
220 class NaClDescWrapper
{
222 explicit NaClDescWrapper(NaClDesc
* desc
): desc_(desc
) {}
224 NaClDescUnref(desc_
);
227 NaClDesc
* desc() { return desc_
; }
231 DISALLOW_COPY_AND_ASSIGN(NaClDescWrapper
);
236 class NaClIPCAdapter::RewrittenMessage
237 : public base::RefCounted
<RewrittenMessage
> {
241 bool is_consumed() const { return data_read_cursor_
== data_len_
; }
243 void SetData(const NaClIPCAdapter::NaClMessageHeader
& header
,
244 const void* payload
, size_t payload_length
);
246 int Read(NaClImcTypedMsgHdr
* msg
);
248 void AddDescriptor(NaClDescWrapper
* desc
) { descs_
.push_back(desc
); }
250 size_t desc_count() const { return descs_
.size(); }
253 friend class base::RefCounted
<RewrittenMessage
>;
254 ~RewrittenMessage() {}
256 scoped_ptr
<char[]> data_
;
259 // Offset into data where the next read will happen. This will be equal to
260 // data_len_ when all data has been consumed.
261 size_t data_read_cursor_
;
263 // Wrapped descriptors for transfer to untrusted code.
264 ScopedVector
<NaClDescWrapper
> descs_
;
267 NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
269 data_read_cursor_(0) {
272 void NaClIPCAdapter::RewrittenMessage::SetData(
273 const NaClIPCAdapter::NaClMessageHeader
& header
,
275 size_t payload_length
) {
276 DCHECK(!data_
.get() && data_len_
== 0);
277 size_t header_len
= sizeof(NaClIPCAdapter::NaClMessageHeader
);
278 data_len_
= header_len
+ payload_length
;
279 data_
.reset(new char[data_len_
]);
281 memcpy(data_
.get(), &header
, sizeof(NaClIPCAdapter::NaClMessageHeader
));
282 memcpy(&data_
[header_len
], payload
, payload_length
);
285 int NaClIPCAdapter::RewrittenMessage::Read(NaClImcTypedMsgHdr
* msg
) {
286 CHECK(data_len_
>= data_read_cursor_
);
287 char* dest_buffer
= static_cast<char*>(msg
->iov
[0].base
);
288 size_t dest_buffer_size
= msg
->iov
[0].length
;
289 size_t bytes_to_write
= std::min(dest_buffer_size
,
290 data_len_
- data_read_cursor_
);
291 if (bytes_to_write
== 0)
294 memcpy(dest_buffer
, &data_
[data_read_cursor_
], bytes_to_write
);
295 data_read_cursor_
+= bytes_to_write
;
297 // Once all data has been consumed, transfer any file descriptors.
299 nacl_abi_size_t desc_count
= static_cast<nacl_abi_size_t
>(descs_
.size());
300 CHECK(desc_count
<= msg
->ndesc_length
);
301 msg
->ndesc_length
= desc_count
;
302 for (nacl_abi_size_t i
= 0; i
< desc_count
; i
++) {
303 // Copy the NaClDesc to the buffer and add a ref so it won't be freed
304 // when we clear our ScopedVector.
305 msg
->ndescv
[i
] = descs_
[i
]->desc();
306 NaClDescRef(descs_
[i
]->desc());
310 msg
->ndesc_length
= 0;
312 return static_cast<int>(bytes_to_write
);
315 NaClIPCAdapter::LockedData::LockedData()
316 : channel_closed_(false) {
319 NaClIPCAdapter::LockedData::~LockedData() {
322 NaClIPCAdapter::IOThreadData::IOThreadData() {
325 NaClIPCAdapter::IOThreadData::~IOThreadData() {
328 NaClIPCAdapter::NaClIPCAdapter(const IPC::ChannelHandle
& handle
,
329 base::TaskRunner
* runner
)
332 task_runner_(runner
),
334 io_thread_data_
.channel_
= IPC::Channel::CreateServer(handle
, this);
335 // Note, we can not PostTask for ConnectChannelOnIOThread here. If we did,
336 // and that task ran before this constructor completes, the reference count
337 // would go to 1 and then to 0 because of the Task, before we've been returned
338 // to the owning scoped_refptr, which is supposed to give us our first
342 NaClIPCAdapter::NaClIPCAdapter(scoped_ptr
<IPC::Channel
> channel
,
343 base::TaskRunner
* runner
)
346 task_runner_(runner
),
348 io_thread_data_
.channel_
= channel
.Pass();
351 void NaClIPCAdapter::ConnectChannel() {
352 task_runner_
->PostTask(FROM_HERE
,
353 base::Bind(&NaClIPCAdapter::ConnectChannelOnIOThread
, this));
356 // Note that this message is controlled by the untrusted code. So we should be
357 // skeptical of anything it contains and quick to give up if anything is fishy.
358 int NaClIPCAdapter::Send(const NaClImcTypedMsgHdr
* msg
) {
359 if (msg
->iov_length
!= 1)
362 base::AutoLock
lock(lock_
);
364 const char* input_data
= static_cast<char*>(msg
->iov
[0].base
);
365 size_t input_data_len
= msg
->iov
[0].length
;
366 if (input_data_len
> IPC::Channel::kMaximumMessageSize
) {
371 // current_message[_len] refers to the total input data received so far.
372 const char* current_message
;
373 size_t current_message_len
;
374 bool did_append_input_data
;
375 if (locked_data_
.to_be_sent_
.empty()) {
376 // No accumulated data, we can avoid a copy by referring to the input
377 // buffer (the entire message fitting in one call is the common case).
378 current_message
= input_data
;
379 current_message_len
= input_data_len
;
380 did_append_input_data
= false;
382 // We've already accumulated some data, accumulate this new data and
383 // point to the beginning of the buffer.
385 // Make sure our accumulated message size doesn't overflow our max. Since
386 // we know that data_len < max size (checked above) and our current
387 // accumulated value is also < max size, we just need to make sure that
388 // 2x max size can never overflow.
389 COMPILE_ASSERT(IPC::Channel::kMaximumMessageSize
< (UINT_MAX
/ 2),
390 MaximumMessageSizeWillOverflow
);
391 size_t new_size
= locked_data_
.to_be_sent_
.size() + input_data_len
;
392 if (new_size
> IPC::Channel::kMaximumMessageSize
) {
397 locked_data_
.to_be_sent_
.append(input_data
, input_data_len
);
398 current_message
= &locked_data_
.to_be_sent_
[0];
399 current_message_len
= locked_data_
.to_be_sent_
.size();
400 did_append_input_data
= true;
403 // Check the total data we've accumulated so far to see if it contains a full
405 switch (GetBufferStatus(current_message
, current_message_len
)) {
406 case MESSAGE_IS_COMPLETE
: {
407 // Got a complete message, can send it out. This will be the common case.
408 bool success
= SendCompleteMessage(current_message
, current_message_len
);
410 return success
? static_cast<int>(input_data_len
) : -1;
412 case MESSAGE_IS_TRUNCATED
:
413 // For truncated messages, just accumulate the new data (if we didn't
414 // already do so above) and go back to waiting for more.
415 if (!did_append_input_data
)
416 locked_data_
.to_be_sent_
.append(input_data
, input_data_len
);
417 return static_cast<int>(input_data_len
);
418 case MESSAGE_HAS_EXTRA_DATA
:
420 // When the plugin gives us too much data, it's an error.
426 int NaClIPCAdapter::BlockingReceive(NaClImcTypedMsgHdr
* msg
) {
427 if (msg
->iov_length
!= 1)
432 base::AutoLock
lock(lock_
);
433 while (locked_data_
.to_be_received_
.empty() &&
434 !locked_data_
.channel_closed_
)
436 if (locked_data_
.channel_closed_
) {
439 retval
= LockedReceive(msg
);
447 void NaClIPCAdapter::CloseChannel() {
449 base::AutoLock
lock(lock_
);
450 locked_data_
.channel_closed_
= true;
454 task_runner_
->PostTask(FROM_HERE
,
455 base::Bind(&NaClIPCAdapter::CloseChannelOnIOThread
, this));
458 NaClDesc
* NaClIPCAdapter::MakeNaClDesc() {
459 return MakeNaClDescCustom(this);
462 #if defined(OS_POSIX)
463 base::ScopedFD
NaClIPCAdapter::TakeClientFileDescriptor() {
464 return io_thread_data_
.channel_
->TakeClientFileDescriptor();
468 bool NaClIPCAdapter::OnMessageReceived(const IPC::Message
& msg
) {
469 uint32_t type
= msg
.type();
471 if (type
== IPC_REPLY_ID
) {
472 int id
= IPC::SyncMessage::GetMessageId(msg
);
473 IOThreadData::PendingSyncMsgMap::iterator it
=
474 io_thread_data_
.pending_sync_msgs_
.find(id
);
475 DCHECK(it
!= io_thread_data_
.pending_sync_msgs_
.end());
476 if (it
!= io_thread_data_
.pending_sync_msgs_
.end()) {
478 io_thread_data_
.pending_sync_msgs_
.erase(it
);
481 // Handle PpapiHostMsg_OpenResource outside the lock as it requires sending
482 // IPC to handle properly.
483 if (type
== PpapiHostMsg_OpenResource::ID
) {
484 PickleIterator iter
= IPC::SyncMessage::GetDataIterator(&msg
);
485 ppapi::proxy::SerializedHandle sh
;
488 if (!IPC::ReadParam(&msg
, &iter
, &sh
) ||
489 !IPC::ReadParam(&msg
, &iter
, &token_lo
) ||
490 !IPC::ReadParam(&msg
, &iter
, &token_hi
)) {
494 if (sh
.IsHandleValid() && (token_lo
!= 0 || token_hi
!= 0)) {
495 // We've received a valid file token. Instead of using the file
496 // descriptor received, we send the file token to the browser in
497 // exchange for a new file descriptor and file path information.
498 // That file descriptor can be used to construct a NaClDesc with
499 // identity-based validation caching.
501 // We do not use file descriptors from the renderer with validation
502 // caching; a compromised renderer should not be able to run
503 // arbitrary code in a plugin process.
504 DCHECK(!resolve_file_token_cb_
.is_null());
506 // resolve_file_token_cb_ must be invoked from the main thread.
507 resolve_file_token_cb_
.Run(
510 base::Bind(&NaClIPCAdapter::OnFileTokenResolved
,
514 // In this case, we don't release the message to NaCl untrusted code
515 // immediately. We defer it until we get an async message back from the
520 return RewriteMessage(msg
, type
);
523 bool NaClIPCAdapter::RewriteMessage(const IPC::Message
& msg
, uint32_t type
) {
525 base::AutoLock
lock(lock_
);
526 scoped_refptr
<RewrittenMessage
> rewritten_msg(new RewrittenMessage
);
528 typedef std::vector
<ppapi::proxy::SerializedHandle
> Handles
;
530 scoped_ptr
<IPC::Message
> new_msg
;
532 if (!locked_data_
.nacl_msg_scanner_
.ScanMessage(
533 msg
, type
, &handles
, &new_msg
))
536 // Now add any descriptors we found to rewritten_msg. |handles| is usually
537 // empty, unless we read a message containing a FD or handle.
538 for (Handles::const_iterator iter
= handles
.begin();
539 iter
!= handles
.end();
541 scoped_ptr
<NaClDescWrapper
> nacl_desc
;
542 switch (iter
->type()) {
543 case ppapi::proxy::SerializedHandle::SHARED_MEMORY
: {
544 const base::SharedMemoryHandle
& shm_handle
= iter
->shmem();
545 uint32_t size
= iter
->size();
546 nacl_desc
.reset(new NaClDescWrapper(NaClDescImcShmMake(
552 static_cast<size_t>(size
))));
555 case ppapi::proxy::SerializedHandle::SOCKET
: {
556 nacl_desc
.reset(new NaClDescWrapper(NaClDescSyncSocketMake(
560 iter
->descriptor().fd
565 case ppapi::proxy::SerializedHandle::FILE: {
566 // Create the NaClDesc for the file descriptor. If quota checking is
567 // required, wrap it in a NaClDescQuota.
568 NaClDesc
* desc
= NaClDescIoDescFromHandleAllocCtor(
572 iter
->descriptor().fd
,
574 TranslatePepperFileReadWriteOpenFlags(iter
->open_flags()));
575 if (desc
&& iter
->file_io()) {
576 desc
= MakeNaClDescQuota(
577 locked_data_
.nacl_msg_scanner_
.GetFile(iter
->file_io()),
581 nacl_desc
.reset(new NaClDescWrapper(desc
));
585 case ppapi::proxy::SerializedHandle::INVALID
: {
589 // No default, so the compiler will warn us if new types get added.
592 rewritten_msg
->AddDescriptor(nacl_desc
.release());
595 SaveMessage(*new_msg
, rewritten_msg
.get());
597 SaveMessage(msg
, rewritten_msg
.get());
603 scoped_ptr
<IPC::Message
> CreateOpenResourceReply(
604 const IPC::Message
& orig_msg
,
605 ppapi::proxy::SerializedHandle sh
) {
606 // The creation of new_msg must be kept in sync with
607 // SyncMessage::WriteSyncHeader.
608 scoped_ptr
<IPC::Message
> new_msg(new IPC::Message(
609 orig_msg
.routing_id(),
611 IPC::Message::PRIORITY_NORMAL
));
612 new_msg
->set_reply();
613 new_msg
->WriteInt(IPC::SyncMessage::GetMessageId(orig_msg
));
615 ppapi::proxy::SerializedHandle::WriteHeader(sh
.header(),
617 new_msg
->WriteBool(true); // valid == true
618 // The file descriptor is at index 0. There's only ever one file
619 // descriptor provided for this message type, so this will be correct.
620 new_msg
->WriteInt(0);
622 // Write empty file tokens.
623 new_msg
->WriteUInt64(0); // token_lo
624 new_msg
->WriteUInt64(0); // token_hi
625 return new_msg
.Pass();
628 void NaClIPCAdapter::OnFileTokenResolved(const IPC::Message
& orig_msg
,
629 IPC::PlatformFileForTransit ipc_fd
,
630 base::FilePath file_path
) {
631 // The path where an invalid ipc_fd is returned isn't currently
632 // covered by any tests.
633 if (ipc_fd
== IPC::InvalidPlatformFileForTransit()) {
634 // The file token didn't resolve successfully, so we give the
635 // original FD to the client without making a validated NaClDesc.
636 // However, we must rewrite the message to clear the file tokens.
637 PickleIterator iter
= IPC::SyncMessage::GetDataIterator(&orig_msg
);
638 ppapi::proxy::SerializedHandle sh
;
640 // We know that this can be read safely; see the original read in
641 // OnMessageReceived().
642 CHECK(IPC::ReadParam(&orig_msg
, &iter
, &sh
));
643 scoped_ptr
<IPC::Message
> new_msg
= CreateOpenResourceReply(orig_msg
, sh
);
645 scoped_ptr
<NaClDescWrapper
> desc_wrapper(new NaClDescWrapper(
646 NaClDescIoDescFromHandleAllocCtor(
652 NACL_ABI_O_RDONLY
)));
654 scoped_refptr
<RewrittenMessage
> rewritten_msg(new RewrittenMessage
);
655 rewritten_msg
->AddDescriptor(desc_wrapper
.release());
657 base::AutoLock
lock(lock_
);
658 SaveMessage(*new_msg
, rewritten_msg
.get());
664 // The file token was sucessfully resolved.
665 std::string file_path_str
= file_path
.AsUTF8Unsafe();
666 base::PlatformFile handle
=
667 IPC::PlatformFileForTransitToPlatformFile(ipc_fd
);
668 // The file token was resolved successfully, so we populate the new
669 // NaClDesc with that information.
670 char* alloc_file_path
= static_cast<char*>(
671 malloc(file_path_str
.length() + 1));
672 strcpy(alloc_file_path
, file_path_str
.c_str());
673 scoped_ptr
<NaClDescWrapper
> desc_wrapper(new NaClDescWrapper(
674 NaClDescIoDescFromHandleAllocCtor(handle
, NACL_ABI_O_RDONLY
)));
676 // Mark the desc as OK for mapping as executable memory.
677 NaClDescMarkSafeForMmap(desc_wrapper
->desc());
679 // Provide metadata for validation.
680 struct NaClRichFileInfo info
;
681 NaClRichFileInfoCtor(&info
);
683 info
.file_path
= alloc_file_path
; // Takes ownership.
684 info
.file_path_length
=
685 static_cast<uint32_t>(file_path_str
.length());
686 NaClSetFileOriginInfo(desc_wrapper
->desc(), &info
);
687 NaClRichFileInfoDtor(&info
);
689 ppapi::proxy::SerializedHandle sh
;
690 sh
.set_file_handle(ipc_fd
, PP_FILEOPENFLAG_READ
, 0);
691 scoped_ptr
<IPC::Message
> new_msg
= CreateOpenResourceReply(orig_msg
, sh
);
692 scoped_refptr
<RewrittenMessage
> rewritten_msg(new RewrittenMessage
);
694 rewritten_msg
->AddDescriptor(desc_wrapper
.release());
696 base::AutoLock
lock(lock_
);
697 SaveMessage(*new_msg
, rewritten_msg
.get());
702 void NaClIPCAdapter::OnChannelConnected(int32 peer_pid
) {
705 void NaClIPCAdapter::OnChannelError() {
709 NaClIPCAdapter::~NaClIPCAdapter() {
710 // Make sure the channel is deleted on the IO thread.
711 task_runner_
->PostTask(FROM_HERE
,
712 base::Bind(&DeleteChannel
, io_thread_data_
.channel_
.release()));
715 int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr
* msg
) {
716 lock_
.AssertAcquired();
718 if (locked_data_
.to_be_received_
.empty())
720 scoped_refptr
<RewrittenMessage
> current
=
721 locked_data_
.to_be_received_
.front();
723 int retval
= current
->Read(msg
);
725 // When a message is entirely consumed, remove if from the waiting queue.
726 if (current
->is_consumed())
727 locked_data_
.to_be_received_
.pop();
732 bool NaClIPCAdapter::SendCompleteMessage(const char* buffer
,
734 lock_
.AssertAcquired();
735 // The message will have already been validated, so we know it's large enough
737 const NaClMessageHeader
* header
=
738 reinterpret_cast<const NaClMessageHeader
*>(buffer
);
740 // Length of the message not including the body. The data passed to us by the
741 // plugin should match that in the message header. This should have already
742 // been validated by GetBufferStatus.
743 int body_len
= static_cast<int>(buffer_len
- sizeof(NaClMessageHeader
));
744 DCHECK(body_len
== static_cast<int>(header
->payload_size
));
746 // We actually discard the flags and only copy the ones we care about. This
747 // is just because message doesn't have a constructor that takes raw flags.
748 scoped_ptr
<IPC::Message
> msg(
749 new IPC::Message(header
->routing
, header
->type
,
750 IPC::Message::PRIORITY_NORMAL
));
751 if (header
->flags
& IPC::Message::SYNC_BIT
)
753 if (header
->flags
& IPC::Message::REPLY_BIT
)
755 if (header
->flags
& IPC::Message::REPLY_ERROR_BIT
)
756 msg
->set_reply_error();
757 if (header
->flags
& IPC::Message::UNBLOCK_BIT
)
758 msg
->set_unblock(true);
760 msg
->WriteBytes(&buffer
[sizeof(NaClMessageHeader
)], body_len
);
762 // Technically we didn't have to do any of the previous work in the lock. But
763 // sometimes our buffer will point to the to_be_sent_ string which is
764 // protected by the lock, and it's messier to factor Send() such that it can
765 // unlock for us. Holding the lock for the message construction, which is
766 // just some memcpys, shouldn't be a big deal.
767 lock_
.AssertAcquired();
768 if (locked_data_
.channel_closed_
) {
769 // If we ever pass handles from the plugin to the host, we should close them
770 // here before we drop the message.
774 // Scan all untrusted messages.
775 scoped_ptr
<IPC::Message
> new_msg
;
776 locked_data_
.nacl_msg_scanner_
.ScanUntrustedMessage(*msg
, &new_msg
);
778 msg
.reset(new_msg
.release());
780 // Actual send must be done on the I/O thread.
781 task_runner_
->PostTask(FROM_HERE
,
782 base::Bind(&NaClIPCAdapter::SendMessageOnIOThread
, this,
783 base::Passed(&msg
)));
787 void NaClIPCAdapter::ClearToBeSent() {
788 lock_
.AssertAcquired();
790 // Don't let the string keep its buffer behind our back.
792 locked_data_
.to_be_sent_
.swap(empty
);
795 void NaClIPCAdapter::ConnectChannelOnIOThread() {
796 if (!io_thread_data_
.channel_
->Connect())
800 void NaClIPCAdapter::CloseChannelOnIOThread() {
801 io_thread_data_
.channel_
->Close();
804 void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr
<IPC::Message
> message
) {
805 int id
= IPC::SyncMessage::GetMessageId(*message
.get());
806 DCHECK(io_thread_data_
.pending_sync_msgs_
.find(id
) ==
807 io_thread_data_
.pending_sync_msgs_
.end());
809 if (message
->is_sync())
810 io_thread_data_
.pending_sync_msgs_
[id
] = message
->type();
811 io_thread_data_
.channel_
->Send(message
.release());
814 void NaClIPCAdapter::SaveMessage(const IPC::Message
& msg
,
815 RewrittenMessage
* rewritten_msg
) {
816 lock_
.AssertAcquired();
817 // There is some padding in this structure (the "padding" member is 16
818 // bits but this then gets padded to 32 bits). We want to be sure not to
819 // leak data to the untrusted plugin, so zero everything out first.
820 NaClMessageHeader header
;
821 memset(&header
, 0, sizeof(NaClMessageHeader
));
823 header
.payload_size
= static_cast<uint32
>(msg
.payload_size());
824 header
.routing
= msg
.routing_id();
825 header
.type
= msg
.type();
826 header
.flags
= msg
.flags();
827 header
.num_fds
= static_cast<int>(rewritten_msg
->desc_count());
829 rewritten_msg
->SetData(header
, msg
.payload(), msg
.payload_size());
830 locked_data_
.to_be_received_
.push(rewritten_msg
);
833 int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags
) {
834 return TranslatePepperFileReadWriteOpenFlags(pp_open_flags
);