1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/nacl/loader/nacl_ipc_adapter.h"
6
7 #include <limits.h>
8 #include <string.h>
9
10 #include <memory>
11 #include <tuple>
12 #include <utility>
13 #include <vector>
14
15 #include "base/functional/bind.h"
16 #include "base/location.h"
17 #include "base/memory/platform_shared_memory_region.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/memory/raw_ptr_exclusion.h"
20 #include "base/task/single_thread_task_runner.h"
21 #include "base/tuple.h"
22 #include "build/build_config.h"
23 #include "ipc/ipc_channel.h"
24 #include "ipc/ipc_platform_file.h"
25 #include "native_client/src/public/nacl_desc.h"
26 #include "native_client/src/public/nacl_desc_custom.h"
27 #include "native_client/src/trusted/desc/nacl_desc_quota.h"
28 #include "native_client/src/trusted/desc/nacl_desc_quota_interface.h"
29 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
30 #include "ppapi/c/ppb_file_io.h"
31 #include "ppapi/proxy/ppapi_messages.h"
32 #include "ppapi/proxy/serialized_handle.h"
33
34 using ppapi::proxy::NaClMessageScanner;
35
36 namespace {
37
38 enum BufferSizeStatus {
39 // The buffer contains a full message with no extra bytes.
40 MESSAGE_IS_COMPLETE,
41
42 // The message doesn't fit and the buffer contains only some of it.
43 MESSAGE_IS_TRUNCATED,
44
45 // The buffer contains a full message + extra data.
46 MESSAGE_HAS_EXTRA_DATA
47 };
48
GetBufferStatus(const char * data,size_t len)49 BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
50 if (len < sizeof(NaClIPCAdapter::NaClMessageHeader))
51 return MESSAGE_IS_TRUNCATED;
52
53 const NaClIPCAdapter::NaClMessageHeader* header =
54 reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(data);
55 uint32_t message_size =
56 sizeof(NaClIPCAdapter::NaClMessageHeader) + header->payload_size;
57
58 if (len == message_size)
59 return MESSAGE_IS_COMPLETE;
60 if (len > message_size)
61 return MESSAGE_HAS_EXTRA_DATA;
62 return MESSAGE_IS_TRUNCATED;
63 }
64
65 //------------------------------------------------------------------------------
66 // This object allows the NaClDesc to hold a reference to a NaClIPCAdapter and
67 // forward calls to it.
68 struct DescThunker {
DescThunker__anon0efe8cd60111::DescThunker69 explicit DescThunker(NaClIPCAdapter* adapter_arg)
70 : adapter(adapter_arg) {
71 }
72
73 DescThunker(const DescThunker&) = delete;
74 DescThunker& operator=(const DescThunker&) = delete;
75
~DescThunker__anon0efe8cd60111::DescThunker76 ~DescThunker() { adapter->CloseChannel(); }
77
78 scoped_refptr<NaClIPCAdapter> adapter;
79 };
80
ToAdapter(void * handle)81 NaClIPCAdapter* ToAdapter(void* handle) {
82 return static_cast<DescThunker*>(handle)->adapter.get();
83 }
84
85 // NaClDescCustom implementation.
NaClDescCustomDestroy(void * handle)86 void NaClDescCustomDestroy(void* handle) {
87 delete static_cast<DescThunker*>(handle);
88 }
89
NaClDescCustomSendMsg(void * handle,const NaClImcTypedMsgHdr * msg,int)90 ssize_t NaClDescCustomSendMsg(void* handle, const NaClImcTypedMsgHdr* msg,
91 int /* flags */) {
92 return static_cast<ssize_t>(ToAdapter(handle)->Send(msg));
93 }
94
NaClDescCustomRecvMsg(void * handle,NaClImcTypedMsgHdr * msg,int)95 ssize_t NaClDescCustomRecvMsg(void* handle, NaClImcTypedMsgHdr* msg,
96 int /* flags */) {
97 return static_cast<ssize_t>(ToAdapter(handle)->BlockingReceive(msg));
98 }
99
MakeNaClDescCustom(NaClIPCAdapter * adapter)100 NaClDesc* MakeNaClDescCustom(NaClIPCAdapter* adapter) {
101 NaClDescCustomFuncs funcs = NACL_DESC_CUSTOM_FUNCS_INITIALIZER;
102 funcs.Destroy = NaClDescCustomDestroy;
103 funcs.SendMsg = NaClDescCustomSendMsg;
104 funcs.RecvMsg = NaClDescCustomRecvMsg;
105 // NaClDescMakeCustomDesc gives us a reference on the returned NaClDesc.
106 return NaClDescMakeCustomDesc(new DescThunker(adapter), &funcs);
107 }
108
109 //------------------------------------------------------------------------------
110 // This object is passed to a NaClDescQuota to intercept writes and forward them
111 // to the NaClIPCAdapter, which checks quota. This is a NaCl-style struct. Don't
112 // add non-trivial fields or virtual methods. Construction should use malloc,
113 // because this is owned by the NaClDesc, and the NaCl Dtor code will call free.
114 struct QuotaInterface {
115 // The "base" struct must be first. NaCl code expects a NaCl style ref-counted
116 // object, so the "vtable" and other base class fields must be first.
117 struct NaClDescQuotaInterface base NACL_IS_REFCOUNT_SUBCLASS;
118
119 // This field is not a raw_ptr<> because it was filtered by the rewriter for:
120 // #reinterpret-cast-trivial-type
121 RAW_PTR_EXCLUSION NaClMessageScanner::FileIO* file_io;
122 };
123
QuotaInterfaceDtor(NaClRefCount * nrcp)124 static void QuotaInterfaceDtor(NaClRefCount* nrcp) {
125 // Trivial class, just pass through to the "base" struct Dtor.
126 nrcp->vtbl = reinterpret_cast<NaClRefCountVtbl*>(
127 const_cast<NaClDescQuotaInterfaceVtbl*>(&kNaClDescQuotaInterfaceVtbl));
128 (*nrcp->vtbl->Dtor)(nrcp);
129 }
130
QuotaInterfaceWriteRequest(NaClDescQuotaInterface * ndqi,const uint8_t *,int64_t offset,int64_t length)131 static int64_t QuotaInterfaceWriteRequest(NaClDescQuotaInterface* ndqi,
132 const uint8_t* /* unused_id */,
133 int64_t offset,
134 int64_t length) {
135 if (offset < 0 || length < 0)
136 return 0;
137 if (std::numeric_limits<int64_t>::max() - length < offset)
138 return 0; // offset + length would overflow.
139 int64_t max_offset = offset + length;
140 if (max_offset < 0)
141 return 0;
142
143 QuotaInterface* quota_interface = reinterpret_cast<QuotaInterface*>(ndqi);
144 NaClMessageScanner::FileIO* file_io = quota_interface->file_io;
145 int64_t increase = max_offset - file_io->max_written_offset();
146 if (increase <= 0 || file_io->Grow(increase))
147 return length;
148
149 return 0;
150 }
151
QuotaInterfaceFtruncateRequest(NaClDescQuotaInterface * ndqi,const uint8_t *,int64_t length)152 static int64_t QuotaInterfaceFtruncateRequest(NaClDescQuotaInterface* ndqi,
153 const uint8_t* /* unused_id */,
154 int64_t length) {
155 // We can't implement SetLength on the plugin side due to sandbox limitations.
156 // See crbug.com/156077.
157 NOTREACHED();
158 return 0;
159 }
160
161 static const struct NaClDescQuotaInterfaceVtbl kQuotaInterfaceVtbl = {
162 {
163 QuotaInterfaceDtor
164 },
165 QuotaInterfaceWriteRequest,
166 QuotaInterfaceFtruncateRequest
167 };
168
MakeNaClDescQuota(NaClMessageScanner::FileIO * file_io,NaClDesc * wrapped_desc)169 NaClDesc* MakeNaClDescQuota(
170 NaClMessageScanner::FileIO* file_io,
171 NaClDesc* wrapped_desc) {
172 // Create the QuotaInterface.
173 QuotaInterface* quota_interface =
174 static_cast<QuotaInterface*>(malloc(sizeof *quota_interface));
175 if (quota_interface && NaClDescQuotaInterfaceCtor("a_interface->base)) {
176 quota_interface->base.base.vtbl =
177 (struct NaClRefCountVtbl *)(&kQuotaInterfaceVtbl);
178 // QuotaInterface is a trivial class, so skip the ctor.
179 quota_interface->file_io = file_io;
180 // Create the NaClDescQuota.
181 NaClDescQuota* desc = static_cast<NaClDescQuota*>(malloc(sizeof *desc));
182 uint8_t unused_id[NACL_DESC_QUOTA_FILE_ID_LEN] = {0};
183 if (desc && NaClDescQuotaCtor(desc,
184 wrapped_desc,
185 unused_id,
186 "a_interface->base)) {
187 return &desc->base;
188 }
189 if (desc)
190 NaClDescUnref(reinterpret_cast<NaClDesc*>(desc));
191 }
192
193 if (quota_interface)
194 NaClDescQuotaInterfaceUnref("a_interface->base);
195
196 return NULL;
197 }
198
199 //------------------------------------------------------------------------------
200
DeleteChannel(IPC::Channel * channel)201 void DeleteChannel(IPC::Channel* channel) {
202 delete channel;
203 }
204
205 // Translates Pepper's read/write open flags into the NaCl equivalents.
206 // Since the host has already opened the file, flags such as O_CREAT, O_TRUNC,
207 // and O_EXCL don't make sense, so we filter those out. If no read or write
208 // flags are set, the function returns NACL_ABI_O_RDONLY as a safe fallback.
TranslatePepperFileReadWriteOpenFlags(int32_t pp_open_flags)209 int TranslatePepperFileReadWriteOpenFlags(int32_t pp_open_flags) {
210 bool read = (pp_open_flags & PP_FILEOPENFLAG_READ) != 0;
211 bool write = (pp_open_flags & PP_FILEOPENFLAG_WRITE) != 0;
212 bool append = (pp_open_flags & PP_FILEOPENFLAG_APPEND) != 0;
213
214 int nacl_open_flag = NACL_ABI_O_RDONLY; // NACL_ABI_O_RDONLY == 0.
215 if (read && (write || append)) {
216 nacl_open_flag = NACL_ABI_O_RDWR;
217 } else if (write || append) {
218 nacl_open_flag = NACL_ABI_O_WRONLY;
219 } else if (!read) {
220 DLOG(WARNING) << "One of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, "
221 << "or PP_FILEOPENFLAG_APPEND should be set.";
222 }
223 if (append)
224 nacl_open_flag |= NACL_ABI_O_APPEND;
225
226 return nacl_open_flag;
227 }
228
229 class NaClDescWrapper {
230 public:
NaClDescWrapper(NaClDesc * desc)231 explicit NaClDescWrapper(NaClDesc* desc): desc_(desc) {}
232
233 NaClDescWrapper(const NaClDescWrapper&) = delete;
234 NaClDescWrapper& operator=(const NaClDescWrapper&) = delete;
235
~NaClDescWrapper()236 ~NaClDescWrapper() {
237 NaClDescUnref(desc_);
238 }
239
desc()240 NaClDesc* desc() { return desc_; }
241
242 private:
243 raw_ptr<NaClDesc> desc_;
244 };
245
MakeShmRegionNaClDesc(base::subtle::PlatformSharedMemoryRegion region)246 std::unique_ptr<NaClDescWrapper> MakeShmRegionNaClDesc(
247 base::subtle::PlatformSharedMemoryRegion region) {
248 // Writable regions are not supported in NaCl.
249 DCHECK_NE(region.GetMode(),
250 base::subtle::PlatformSharedMemoryRegion::Mode::kWritable);
251 size_t size = region.GetSize();
252 base::subtle::ScopedPlatformSharedMemoryHandle handle =
253 region.PassPlatformHandle();
254 return std::make_unique<NaClDescWrapper>(
255 NaClDescImcShmMake(handle.fd.release(),
256 size));
257 }
258
259 } // namespace
260
261 class NaClIPCAdapter::RewrittenMessage {
262 public:
263 RewrittenMessage();
~RewrittenMessage()264 ~RewrittenMessage() {}
265
is_consumed() const266 bool is_consumed() const { return data_read_cursor_ == data_len_; }
267
268 void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
269 const void* payload, size_t payload_length);
270
271 int Read(NaClImcTypedMsgHdr* msg);
272
AddDescriptor(std::unique_ptr<NaClDescWrapper> desc)273 void AddDescriptor(std::unique_ptr<NaClDescWrapper> desc) {
274 descs_.push_back(std::move(desc));
275 }
276
desc_count() const277 size_t desc_count() const { return descs_.size(); }
278
279 private:
280 std::unique_ptr<char[]> data_;
281 size_t data_len_;
282
283 // Offset into data where the next read will happen. This will be equal to
284 // data_len_ when all data has been consumed.
285 size_t data_read_cursor_;
286
287 // Wrapped descriptors for transfer to untrusted code.
288 std::vector<std::unique_ptr<NaClDescWrapper>> descs_;
289 };
290
RewrittenMessage()291 NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
292 : data_len_(0),
293 data_read_cursor_(0) {
294 }
295
SetData(const NaClIPCAdapter::NaClMessageHeader & header,const void * payload,size_t payload_length)296 void NaClIPCAdapter::RewrittenMessage::SetData(
297 const NaClIPCAdapter::NaClMessageHeader& header,
298 const void* payload,
299 size_t payload_length) {
300 DCHECK(!data_.get() && data_len_ == 0);
301 size_t header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
302 data_len_ = header_len + payload_length;
303 data_.reset(new char[data_len_]);
304
305 memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
306 memcpy(&data_[header_len], payload, payload_length);
307 }
308
Read(NaClImcTypedMsgHdr * msg)309 int NaClIPCAdapter::RewrittenMessage::Read(NaClImcTypedMsgHdr* msg) {
310 CHECK(data_len_ >= data_read_cursor_);
311 char* dest_buffer = static_cast<char*>(msg->iov[0].base);
312 size_t dest_buffer_size = msg->iov[0].length;
313 size_t bytes_to_write = std::min(dest_buffer_size,
314 data_len_ - data_read_cursor_);
315 if (bytes_to_write == 0)
316 return 0;
317
318 memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
319 data_read_cursor_ += bytes_to_write;
320
321 // Once all data has been consumed, transfer any file descriptors.
322 if (is_consumed()) {
323 nacl_abi_size_t desc_count = static_cast<nacl_abi_size_t>(descs_.size());
324 CHECK(desc_count <= msg->ndesc_length);
325 msg->ndesc_length = desc_count;
326 for (nacl_abi_size_t i = 0; i < desc_count; i++) {
327 // Copy the NaClDesc to the buffer and add a ref so it won't be freed
328 // when we clear our vector.
329 msg->ndescv[i] = descs_[i]->desc();
330 NaClDescRef(descs_[i]->desc());
331 }
332 descs_.clear();
333 } else {
334 msg->ndesc_length = 0;
335 }
336 return static_cast<int>(bytes_to_write);
337 }
338
LockedData()339 NaClIPCAdapter::LockedData::LockedData()
340 : channel_closed_(false) {
341 }
342
~LockedData()343 NaClIPCAdapter::LockedData::~LockedData() {
344 }
345
IOThreadData()346 NaClIPCAdapter::IOThreadData::IOThreadData() {
347 }
348
~IOThreadData()349 NaClIPCAdapter::IOThreadData::~IOThreadData() {
350 }
351
NaClIPCAdapter(const IPC::ChannelHandle & handle,const scoped_refptr<base::SingleThreadTaskRunner> & runner,ResolveFileTokenCallback resolve_file_token_cb,OpenResourceCallback open_resource_cb)352 NaClIPCAdapter::NaClIPCAdapter(
353 const IPC::ChannelHandle& handle,
354 const scoped_refptr<base::SingleThreadTaskRunner>& runner,
355 ResolveFileTokenCallback resolve_file_token_cb,
356 OpenResourceCallback open_resource_cb)
357 : lock_(),
358 cond_var_(&lock_),
359 task_runner_(runner),
360 resolve_file_token_cb_(std::move(resolve_file_token_cb)),
361 open_resource_cb_(std::move(open_resource_cb)),
362 locked_data_() {
363 io_thread_data_.channel_ = IPC::Channel::CreateServer(handle, this, runner);
364 // Note, we can not PostTask for ConnectChannelOnIOThread here. If we did,
365 // and that task ran before this constructor completes, the reference count
366 // would go to 1 and then to 0 because of the Task, before we've been returned
367 // to the owning scoped_refptr, which is supposed to give us our first
368 // ref-count.
369 }
370
NaClIPCAdapter(std::unique_ptr<IPC::Channel> channel,base::TaskRunner * runner)371 NaClIPCAdapter::NaClIPCAdapter(std::unique_ptr<IPC::Channel> channel,
372 base::TaskRunner* runner)
373 : lock_(), cond_var_(&lock_), task_runner_(runner), locked_data_() {
374 io_thread_data_.channel_ = std::move(channel);
375 }
376
ConnectChannel()377 void NaClIPCAdapter::ConnectChannel() {
378 task_runner_->PostTask(
379 FROM_HERE,
380 base::BindOnce(&NaClIPCAdapter::ConnectChannelOnIOThread, this));
381 }
382
383 // Note that this message is controlled by the untrusted code. So we should be
384 // skeptical of anything it contains and quick to give up if anything is fishy.
Send(const NaClImcTypedMsgHdr * msg)385 int NaClIPCAdapter::Send(const NaClImcTypedMsgHdr* msg) {
386 if (msg->iov_length != 1)
387 return -1;
388
389 base::AutoLock lock(lock_);
390
391 const char* input_data = static_cast<char*>(msg->iov[0].base);
392 size_t input_data_len = msg->iov[0].length;
393 if (input_data_len > IPC::Channel::kMaximumMessageSize) {
394 ClearToBeSent();
395 return -1;
396 }
397
398 // current_message[_len] refers to the total input data received so far.
399 const char* current_message;
400 size_t current_message_len;
401 bool did_append_input_data;
402 if (locked_data_.to_be_sent_.empty()) {
403 // No accumulated data, we can avoid a copy by referring to the input
404 // buffer (the entire message fitting in one call is the common case).
405 current_message = input_data;
406 current_message_len = input_data_len;
407 did_append_input_data = false;
408 } else {
409 // We've already accumulated some data, accumulate this new data and
410 // point to the beginning of the buffer.
411
412 // Make sure our accumulated message size doesn't overflow our max. Since
413 // we know that data_len < max size (checked above) and our current
414 // accumulated value is also < max size, we just need to make sure that
415 // 2x max size can never overflow.
416 static_assert(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
417 "kMaximumMessageSize is too large, and may overflow");
418 size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
419 if (new_size > IPC::Channel::kMaximumMessageSize) {
420 ClearToBeSent();
421 return -1;
422 }
423
424 locked_data_.to_be_sent_.append(input_data, input_data_len);
425 current_message = &locked_data_.to_be_sent_[0];
426 current_message_len = locked_data_.to_be_sent_.size();
427 did_append_input_data = true;
428 }
429
430 // Check the total data we've accumulated so far to see if it contains a full
431 // message.
432 switch (GetBufferStatus(current_message, current_message_len)) {
433 case MESSAGE_IS_COMPLETE: {
434 // Got a complete message, can send it out. This will be the common case.
435 bool success = SendCompleteMessage(current_message, current_message_len);
436 ClearToBeSent();
437 return success ? static_cast<int>(input_data_len) : -1;
438 }
439 case MESSAGE_IS_TRUNCATED:
440 // For truncated messages, just accumulate the new data (if we didn't
441 // already do so above) and go back to waiting for more.
442 if (!did_append_input_data)
443 locked_data_.to_be_sent_.append(input_data, input_data_len);
444 return static_cast<int>(input_data_len);
445 case MESSAGE_HAS_EXTRA_DATA:
446 default:
447 // When the plugin gives us too much data, it's an error.
448 ClearToBeSent();
449 return -1;
450 }
451 }
452
BlockingReceive(NaClImcTypedMsgHdr * msg)453 int NaClIPCAdapter::BlockingReceive(NaClImcTypedMsgHdr* msg) {
454 if (msg->iov_length != 1)
455 return -1;
456
457 int retval = 0;
458 {
459 base::AutoLock lock(lock_);
460 while (locked_data_.to_be_received_.empty() &&
461 !locked_data_.channel_closed_)
462 cond_var_.Wait();
463 if (locked_data_.channel_closed_) {
464 retval = -1;
465 } else {
466 retval = LockedReceive(msg);
467 DCHECK(retval > 0);
468 }
469 cond_var_.Signal();
470 }
471 return retval;
472 }
473
CloseChannel()474 void NaClIPCAdapter::CloseChannel() {
475 {
476 base::AutoLock lock(lock_);
477 locked_data_.channel_closed_ = true;
478 cond_var_.Signal();
479 }
480
481 task_runner_->PostTask(
482 FROM_HERE, base::BindOnce(&NaClIPCAdapter::CloseChannelOnIOThread, this));
483 }
484
MakeNaClDesc()485 NaClDesc* NaClIPCAdapter::MakeNaClDesc() {
486 return MakeNaClDescCustom(this);
487 }
488
OnMessageReceived(const IPC::Message & msg)489 bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
490 uint32_t type = msg.type();
491
492 if (type == IPC_REPLY_ID) {
493 int id = IPC::SyncMessage::GetMessageId(msg);
494 auto it = io_thread_data_.pending_sync_msgs_.find(id);
495 DCHECK(it != io_thread_data_.pending_sync_msgs_.end());
496 if (it != io_thread_data_.pending_sync_msgs_.end()) {
497 type = it->second;
498 io_thread_data_.pending_sync_msgs_.erase(it);
499 }
500 }
501 // Handle PpapiHostMsg_OpenResource outside the lock as it requires sending
502 // IPC to handle properly.
503 if (type == PpapiHostMsg_OpenResource::ID) {
504 base::PickleIterator iter = IPC::SyncMessage::GetDataIterator(&msg);
505 uint64_t token_lo;
506 uint64_t token_hi;
507 if (!IPC::ReadParam(&msg, &iter, &token_lo) ||
508 !IPC::ReadParam(&msg, &iter, &token_hi)) {
509 return false;
510 }
511
512 if (token_lo != 0 || token_hi != 0) {
513 // We've received a valid file token. Instead of using the file
514 // descriptor received, we send the file token to the browser in
515 // exchange for a new file descriptor and file path information.
516 // That file descriptor can be used to construct a NaClDesc with
517 // identity-based validation caching.
518 //
519 // We do not use file descriptors from the renderer with validation
520 // caching; a compromised renderer should not be able to run
521 // arbitrary code in a plugin process.
522 //
523 // We intentionally avoid deserializing the next parameter, which is an
524 // instance of SerializedHandle, since doing so takes ownership from the
525 // IPC stack. If we fail to get a resource from the file token, we will
526 // still need to read the original parameter in SaveOpenResourceMessage().
527 DCHECK(!resolve_file_token_cb_.is_null());
528
529 // resolve_file_token_cb_ must be invoked from the I/O thread.
530 resolve_file_token_cb_.Run(
531 token_lo, token_hi,
532 base::BindOnce(&NaClIPCAdapter::SaveOpenResourceMessage, this, msg));
533
534 // In this case, we don't release the message to NaCl untrusted code
535 // immediately. We defer it until we get an async message back from the
536 // browser process.
537 return true;
538 }
539 }
540 return RewriteMessage(msg, type);
541 }
542
RewriteMessage(const IPC::Message & msg,uint32_t type)543 bool NaClIPCAdapter::RewriteMessage(const IPC::Message& msg, uint32_t type) {
544 {
545 base::AutoLock lock(lock_);
546 std::unique_ptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
547
548 typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
549 Handles handles;
550 std::unique_ptr<IPC::Message> new_msg;
551
552 if (!locked_data_.nacl_msg_scanner_.ScanMessage(
553 msg, type, &handles, &new_msg))
554 return false;
555
556 // Now add any descriptors we found to rewritten_msg. |handles| is usually
557 // empty, unless we read a message containing a FD or handle.
558 for (ppapi::proxy::SerializedHandle& handle : handles) {
559 std::unique_ptr<NaClDescWrapper> nacl_desc;
560 switch (handle.type()) {
561 case ppapi::proxy::SerializedHandle::SHARED_MEMORY_REGION: {
562 nacl_desc = MakeShmRegionNaClDesc(handle.TakeSharedMemoryRegion());
563 break;
564 }
565 case ppapi::proxy::SerializedHandle::SOCKET: {
566 nacl_desc = std::make_unique<NaClDescWrapper>(NaClDescSyncSocketMake(
567 handle.descriptor().fd
568 ));
569 break;
570 }
571 case ppapi::proxy::SerializedHandle::FILE: {
572 // Create the NaClDesc for the file descriptor. If quota checking is
573 // required, wrap it in a NaClDescQuota.
574 NaClDesc* desc = NaClDescIoMakeFromHandle(
575 handle.descriptor().fd,
576 TranslatePepperFileReadWriteOpenFlags(handle.open_flags()));
577 if (desc && handle.file_io()) {
578 desc = MakeNaClDescQuota(
579 locked_data_.nacl_msg_scanner_.GetFile(handle.file_io()), desc);
580 }
581 if (desc)
582 nacl_desc = std::make_unique<NaClDescWrapper>(desc);
583 break;
584 }
585
586 case ppapi::proxy::SerializedHandle::INVALID: {
587 // Nothing to do.
588 break;
589 }
590 // No default, so the compiler will warn us if new types get added.
591 }
592 if (nacl_desc.get())
593 rewritten_msg->AddDescriptor(std::move(nacl_desc));
594 }
595 if (new_msg)
596 SaveMessage(*new_msg, std::move(rewritten_msg));
597 else
598 SaveMessage(msg, std::move(rewritten_msg));
599 cond_var_.Signal();
600 }
601 return true;
602 }
603
CreateOpenResourceReply(const IPC::Message & orig_msg,ppapi::proxy::SerializedHandle sh)604 std::unique_ptr<IPC::Message> CreateOpenResourceReply(
605 const IPC::Message& orig_msg,
606 ppapi::proxy::SerializedHandle sh) {
607 // The creation of new_msg must be kept in sync with
608 // SyncMessage::WriteSyncHeader.
609 std::unique_ptr<IPC::Message> new_msg(new IPC::Message(
610 orig_msg.routing_id(), orig_msg.type(), IPC::Message::PRIORITY_NORMAL));
611 new_msg->set_reply();
612 new_msg->WriteInt(IPC::SyncMessage::GetMessageId(orig_msg));
613
614 // Write empty file tokens.
615 new_msg->WriteUInt64(0); // token_lo
616 new_msg->WriteUInt64(0); // token_hi
617
618 ppapi::proxy::SerializedHandle::WriteHeader(sh.header(),
619 new_msg.get());
620 new_msg->WriteBool(true); // valid == true
621 // The file descriptor is at index 0. There's only ever one file
622 // descriptor provided for this message type, so this will be correct.
623 new_msg->WriteInt(0);
624
625 return new_msg;
626 }
627
SaveOpenResourceMessage(const IPC::Message & orig_msg,IPC::PlatformFileForTransit ipc_fd,base::FilePath file_path)628 void NaClIPCAdapter::SaveOpenResourceMessage(
629 const IPC::Message& orig_msg,
630 IPC::PlatformFileForTransit ipc_fd,
631 base::FilePath file_path) {
632 // The path where an invalid ipc_fd is returned isn't currently
633 // covered by any tests.
634 if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
635 base::PickleIterator iter = IPC::SyncMessage::GetDataIterator(&orig_msg);
636 uint64_t token_lo;
637 uint64_t token_hi;
638 ppapi::proxy::SerializedHandle orig_sh;
639
640 // These CHECKs could fail if the renderer sends this process a malformed
641 // message, but that's OK because in general the renderer can cause the NaCl
642 // loader process to exit.
643 CHECK(IPC::ReadParam(&orig_msg, &iter, &token_lo));
644 CHECK(IPC::ReadParam(&orig_msg, &iter, &token_hi));
645 CHECK(IPC::ReadParam(&orig_msg, &iter, &orig_sh));
646 CHECK(orig_sh.IsHandleValid());
647
648 std::unique_ptr<NaClDescWrapper> desc_wrapper(
649 new NaClDescWrapper(NaClDescIoMakeFromHandle(
650 orig_sh.descriptor().fd,
651 NACL_ABI_O_RDONLY)));
652
653 // The file token didn't resolve successfully, so we give the
654 // original FD to the client without making a validated NaClDesc.
655 // However, we must rewrite the message to clear the file tokens.
656 std::unique_ptr<IPC::Message> new_msg =
657 CreateOpenResourceReply(orig_msg, std::move(orig_sh));
658
659 std::unique_ptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
660 rewritten_msg->AddDescriptor(std::move(desc_wrapper));
661 {
662 base::AutoLock lock(lock_);
663 SaveMessage(*new_msg, std::move(rewritten_msg));
664 cond_var_.Signal();
665 }
666 return;
667 }
668
669 // The file token was successfully resolved.
670 std::string file_path_str = file_path.AsUTF8Unsafe();
671 base::PlatformFile handle =
672 IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
673
674 ppapi::proxy::SerializedHandle sh;
675 sh.set_file_handle(ipc_fd, PP_FILEOPENFLAG_READ, 0);
676 std::unique_ptr<IPC::Message> new_msg =
677 CreateOpenResourceReply(orig_msg, std::move(sh));
678 std::unique_ptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
679
680 struct NaClDesc* desc =
681 NaClDescCreateWithFilePathMetadata(handle, file_path_str.c_str());
682 rewritten_msg->AddDescriptor(std::make_unique<NaClDescWrapper>(desc));
683 {
684 base::AutoLock lock(lock_);
685 SaveMessage(*new_msg, std::move(rewritten_msg));
686 cond_var_.Signal();
687 }
688 }
689
OnChannelConnected(int32_t peer_pid)690 void NaClIPCAdapter::OnChannelConnected(int32_t peer_pid) {}
691
OnChannelError()692 void NaClIPCAdapter::OnChannelError() {
693 CloseChannel();
694 }
695
~NaClIPCAdapter()696 NaClIPCAdapter::~NaClIPCAdapter() {
697 // Make sure the channel is deleted on the IO thread.
698 task_runner_->PostTask(
699 FROM_HERE,
700 base::BindOnce(&DeleteChannel, io_thread_data_.channel_.release()));
701 }
702
LockedReceive(NaClImcTypedMsgHdr * msg)703 int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr* msg) {
704 lock_.AssertAcquired();
705
706 if (locked_data_.to_be_received_.empty())
707 return 0;
708 RewrittenMessage& current = *locked_data_.to_be_received_.front();
709
710 int retval = current.Read(msg);
711
712 // When a message is entirely consumed, remove it from the waiting queue.
713 if (current.is_consumed())
714 locked_data_.to_be_received_.pop();
715
716 return retval;
717 }
718
SendCompleteMessage(const char * buffer,size_t buffer_len)719 bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
720 size_t buffer_len) {
721 lock_.AssertAcquired();
722 // The message will have already been validated, so we know it's large enough
723 // for our header.
724 const NaClMessageHeader* header =
725 reinterpret_cast<const NaClMessageHeader*>(buffer);
726
727 // Length of the message not including the body. The data passed to us by the
728 // plugin should match that in the message header. This should have already
729 // been validated by GetBufferStatus.
730 size_t body_len = buffer_len - sizeof(NaClMessageHeader);
731 CHECK(body_len == header->payload_size);
732
733 // We actually discard the flags and only copy the ones we care about. This
734 // is just because message doesn't have a constructor that takes raw flags.
735 std::unique_ptr<IPC::Message> msg(new IPC::Message(
736 header->routing, header->type, IPC::Message::PRIORITY_NORMAL));
737 if (header->flags & IPC::Message::SYNC_BIT)
738 msg->set_sync();
739 if (header->flags & IPC::Message::REPLY_BIT)
740 msg->set_reply();
741 if (header->flags & IPC::Message::REPLY_ERROR_BIT)
742 msg->set_reply_error();
743 if (header->flags & IPC::Message::UNBLOCK_BIT)
744 msg->set_unblock(true);
745
746 msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
747
748 // Technically we didn't have to do any of the previous work in the lock. But
749 // sometimes our buffer will point to the to_be_sent_ string which is
750 // protected by the lock, and it's messier to factor Send() such that it can
751 // unlock for us. Holding the lock for the message construction, which is
752 // just some memcpys, shouldn't be a big deal.
753 lock_.AssertAcquired();
754 if (locked_data_.channel_closed_) {
755 // If we ever pass handles from the plugin to the host, we should close them
756 // here before we drop the message.
757 return false;
758 }
759
760 // Scan all untrusted messages.
761 std::unique_ptr<IPC::Message> new_msg;
762 locked_data_.nacl_msg_scanner_.ScanUntrustedMessage(*msg, &new_msg);
763 if (new_msg)
764 msg = std::move(new_msg);
765
766 // Actual send must be done on the I/O thread.
767 task_runner_->PostTask(
768 FROM_HERE, base::BindOnce(&NaClIPCAdapter::SendMessageOnIOThread, this,
769 std::move(msg)));
770 return true;
771 }
772
ClearToBeSent()773 void NaClIPCAdapter::ClearToBeSent() {
774 lock_.AssertAcquired();
775
776 // Don't let the string keep its buffer behind our back.
777 std::string empty;
778 locked_data_.to_be_sent_.swap(empty);
779 }
780
ConnectChannelOnIOThread()781 void NaClIPCAdapter::ConnectChannelOnIOThread() {
782 if (!io_thread_data_.channel_->Connect())
783 NOTREACHED();
784 }
785
CloseChannelOnIOThread()786 void NaClIPCAdapter::CloseChannelOnIOThread() {
787 io_thread_data_.channel_->Close();
788 }
789
SendMessageOnIOThread(std::unique_ptr<IPC::Message> message)790 void NaClIPCAdapter::SendMessageOnIOThread(
791 std::unique_ptr<IPC::Message> message) {
792 int id = IPC::SyncMessage::GetMessageId(*message.get());
793 DCHECK(io_thread_data_.pending_sync_msgs_.find(id) ==
794 io_thread_data_.pending_sync_msgs_.end());
795
796 // Handle PpapiHostMsg_OpenResource locally without sending an IPC to the
797 // renderer when possible.
798 PpapiHostMsg_OpenResource::Schema::SendParam send_params;
799 if (!open_resource_cb_.is_null() &&
800 message->type() == PpapiHostMsg_OpenResource::ID &&
801 PpapiHostMsg_OpenResource::ReadSendParam(message.get(), &send_params)) {
802 const std::string key = std::get<0>(send_params);
803 // Both open_resource_cb_ and SaveOpenResourceMessage must be invoked
804 // from the I/O thread.
805 if (open_resource_cb_.Run(
806 *message.get(), key,
807 base::BindOnce(&NaClIPCAdapter::SaveOpenResourceMessage, this))) {
808 // The callback sent a reply to the untrusted side.
809 return;
810 }
811 }
812
813 if (message->is_sync())
814 io_thread_data_.pending_sync_msgs_[id] = message->type();
815 io_thread_data_.channel_->Send(message.release());
816 }
817
SaveMessage(const IPC::Message & msg,std::unique_ptr<RewrittenMessage> rewritten_msg)818 void NaClIPCAdapter::SaveMessage(
819 const IPC::Message& msg,
820 std::unique_ptr<RewrittenMessage> rewritten_msg) {
821 lock_.AssertAcquired();
822 // There is some padding in this structure (the "padding" member is 16
823 // bits but this then gets padded to 32 bits). We want to be sure not to
824 // leak data to the untrusted plugin, so zero everything out first.
825 NaClMessageHeader header;
826 memset(&header, 0, sizeof(NaClMessageHeader));
827
828 header.payload_size = static_cast<uint32_t>(msg.payload_size());
829 header.routing = msg.routing_id();
830 header.type = msg.type();
831 header.flags = msg.flags();
832 header.num_fds = static_cast<uint16_t>(rewritten_msg->desc_count());
833
834 rewritten_msg->SetData(header, msg.payload_bytes().data(),
835 msg.payload_bytes().size());
836 locked_data_.to_be_received_.push(std::move(rewritten_msg));
837 }
838
TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags)839 int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags) {
840 return TranslatePepperFileReadWriteOpenFlags(pp_open_flags);
841 }
842