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