• 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 #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__anon19c46ce50111::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__anon19c46ce50111::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(&quota_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                                   &quota_interface->base)) {
187       return &desc->base;
188     }
189     if (desc)
190       NaClDescUnref(reinterpret_cast<NaClDesc*>(desc));
191   }
192 
193   if (quota_interface)
194     NaClDescQuotaInterfaceUnref(&quota_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 #if BUILDFLAG(IS_APPLE)
256       NaClDescImcShmMachMake(handle.release(),
257 #elif BUILDFLAG(IS_WIN)
258       NaClDescImcShmMake(handle.Take(),
259 #else
260       NaClDescImcShmMake(handle.fd.release(),
261 #endif
262                              size));
263 }
264 
265 }  // namespace
266 
267 class NaClIPCAdapter::RewrittenMessage {
268  public:
269   RewrittenMessage();
~RewrittenMessage()270   ~RewrittenMessage() {}
271 
is_consumed() const272   bool is_consumed() const { return data_read_cursor_ == data_len_; }
273 
274   void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
275                const void* payload, size_t payload_length);
276 
277   int Read(NaClImcTypedMsgHdr* msg);
278 
AddDescriptor(std::unique_ptr<NaClDescWrapper> desc)279   void AddDescriptor(std::unique_ptr<NaClDescWrapper> desc) {
280     descs_.push_back(std::move(desc));
281   }
282 
desc_count() const283   size_t desc_count() const { return descs_.size(); }
284 
285  private:
286   std::unique_ptr<char[]> data_;
287   size_t data_len_;
288 
289   // Offset into data where the next read will happen. This will be equal to
290   // data_len_ when all data has been consumed.
291   size_t data_read_cursor_;
292 
293   // Wrapped descriptors for transfer to untrusted code.
294   std::vector<std::unique_ptr<NaClDescWrapper>> descs_;
295 };
296 
RewrittenMessage()297 NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
298     : data_len_(0),
299       data_read_cursor_(0) {
300 }
301 
SetData(const NaClIPCAdapter::NaClMessageHeader & header,const void * payload,size_t payload_length)302 void NaClIPCAdapter::RewrittenMessage::SetData(
303     const NaClIPCAdapter::NaClMessageHeader& header,
304     const void* payload,
305     size_t payload_length) {
306   DCHECK(!data_.get() && data_len_ == 0);
307   size_t header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
308   data_len_ = header_len + payload_length;
309   data_.reset(new char[data_len_]);
310 
311   memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
312   memcpy(&data_[header_len], payload, payload_length);
313 }
314 
Read(NaClImcTypedMsgHdr * msg)315 int NaClIPCAdapter::RewrittenMessage::Read(NaClImcTypedMsgHdr* msg) {
316   CHECK(data_len_ >= data_read_cursor_);
317   char* dest_buffer = static_cast<char*>(msg->iov[0].base);
318   size_t dest_buffer_size = msg->iov[0].length;
319   size_t bytes_to_write = std::min(dest_buffer_size,
320                                    data_len_ - data_read_cursor_);
321   if (bytes_to_write == 0)
322     return 0;
323 
324   memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
325   data_read_cursor_ += bytes_to_write;
326 
327   // Once all data has been consumed, transfer any file descriptors.
328   if (is_consumed()) {
329     nacl_abi_size_t desc_count = static_cast<nacl_abi_size_t>(descs_.size());
330     CHECK(desc_count <= msg->ndesc_length);
331     msg->ndesc_length = desc_count;
332     for (nacl_abi_size_t i = 0; i < desc_count; i++) {
333       // Copy the NaClDesc to the buffer and add a ref so it won't be freed
334       // when we clear our vector.
335       msg->ndescv[i] = descs_[i]->desc();
336       NaClDescRef(descs_[i]->desc());
337     }
338     descs_.clear();
339   } else {
340     msg->ndesc_length = 0;
341   }
342   return static_cast<int>(bytes_to_write);
343 }
344 
LockedData()345 NaClIPCAdapter::LockedData::LockedData()
346     : channel_closed_(false) {
347 }
348 
~LockedData()349 NaClIPCAdapter::LockedData::~LockedData() {
350 }
351 
IOThreadData()352 NaClIPCAdapter::IOThreadData::IOThreadData() {
353 }
354 
~IOThreadData()355 NaClIPCAdapter::IOThreadData::~IOThreadData() {
356 }
357 
NaClIPCAdapter(const IPC::ChannelHandle & handle,const scoped_refptr<base::SingleThreadTaskRunner> & runner,ResolveFileTokenCallback resolve_file_token_cb,OpenResourceCallback open_resource_cb)358 NaClIPCAdapter::NaClIPCAdapter(
359     const IPC::ChannelHandle& handle,
360     const scoped_refptr<base::SingleThreadTaskRunner>& runner,
361     ResolveFileTokenCallback resolve_file_token_cb,
362     OpenResourceCallback open_resource_cb)
363     : lock_(),
364       cond_var_(&lock_),
365       task_runner_(runner),
366       resolve_file_token_cb_(std::move(resolve_file_token_cb)),
367       open_resource_cb_(std::move(open_resource_cb)),
368       locked_data_() {
369   io_thread_data_.channel_ = IPC::Channel::CreateServer(handle, this, runner);
370   // Note, we can not PostTask for ConnectChannelOnIOThread here. If we did,
371   // and that task ran before this constructor completes, the reference count
372   // would go to 1 and then to 0 because of the Task, before we've been returned
373   // to the owning scoped_refptr, which is supposed to give us our first
374   // ref-count.
375 }
376 
NaClIPCAdapter(std::unique_ptr<IPC::Channel> channel,base::TaskRunner * runner)377 NaClIPCAdapter::NaClIPCAdapter(std::unique_ptr<IPC::Channel> channel,
378                                base::TaskRunner* runner)
379     : lock_(), cond_var_(&lock_), task_runner_(runner), locked_data_() {
380   io_thread_data_.channel_ = std::move(channel);
381 }
382 
ConnectChannel()383 void NaClIPCAdapter::ConnectChannel() {
384   task_runner_->PostTask(
385       FROM_HERE,
386       base::BindOnce(&NaClIPCAdapter::ConnectChannelOnIOThread, this));
387 }
388 
389 // Note that this message is controlled by the untrusted code. So we should be
390 // skeptical of anything it contains and quick to give up if anything is fishy.
Send(const NaClImcTypedMsgHdr * msg)391 int NaClIPCAdapter::Send(const NaClImcTypedMsgHdr* msg) {
392   if (msg->iov_length != 1)
393     return -1;
394 
395   base::AutoLock lock(lock_);
396 
397   const char* input_data = static_cast<char*>(msg->iov[0].base);
398   size_t input_data_len = msg->iov[0].length;
399   if (input_data_len > IPC::Channel::kMaximumMessageSize) {
400     ClearToBeSent();
401     return -1;
402   }
403 
404   // current_message[_len] refers to the total input data received so far.
405   const char* current_message;
406   size_t current_message_len;
407   bool did_append_input_data;
408   if (locked_data_.to_be_sent_.empty()) {
409     // No accumulated data, we can avoid a copy by referring to the input
410     // buffer (the entire message fitting in one call is the common case).
411     current_message = input_data;
412     current_message_len = input_data_len;
413     did_append_input_data = false;
414   } else {
415     // We've already accumulated some data, accumulate this new data and
416     // point to the beginning of the buffer.
417 
418     // Make sure our accumulated message size doesn't overflow our max. Since
419     // we know that data_len < max size (checked above) and our current
420     // accumulated value is also < max size, we just need to make sure that
421     // 2x max size can never overflow.
422     static_assert(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
423                   "kMaximumMessageSize is too large, and may overflow");
424     size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
425     if (new_size > IPC::Channel::kMaximumMessageSize) {
426       ClearToBeSent();
427       return -1;
428     }
429 
430     locked_data_.to_be_sent_.append(input_data, input_data_len);
431     current_message = &locked_data_.to_be_sent_[0];
432     current_message_len = locked_data_.to_be_sent_.size();
433     did_append_input_data = true;
434   }
435 
436   // Check the total data we've accumulated so far to see if it contains a full
437   // message.
438   switch (GetBufferStatus(current_message, current_message_len)) {
439     case MESSAGE_IS_COMPLETE: {
440       // Got a complete message, can send it out. This will be the common case.
441       bool success = SendCompleteMessage(current_message, current_message_len);
442       ClearToBeSent();
443       return success ? static_cast<int>(input_data_len) : -1;
444     }
445     case MESSAGE_IS_TRUNCATED:
446       // For truncated messages, just accumulate the new data (if we didn't
447       // already do so above) and go back to waiting for more.
448       if (!did_append_input_data)
449         locked_data_.to_be_sent_.append(input_data, input_data_len);
450       return static_cast<int>(input_data_len);
451     case MESSAGE_HAS_EXTRA_DATA:
452     default:
453       // When the plugin gives us too much data, it's an error.
454       ClearToBeSent();
455       return -1;
456   }
457 }
458 
BlockingReceive(NaClImcTypedMsgHdr * msg)459 int NaClIPCAdapter::BlockingReceive(NaClImcTypedMsgHdr* msg) {
460   if (msg->iov_length != 1)
461     return -1;
462 
463   int retval = 0;
464   {
465     base::AutoLock lock(lock_);
466     while (locked_data_.to_be_received_.empty() &&
467            !locked_data_.channel_closed_)
468       cond_var_.Wait();
469     if (locked_data_.channel_closed_) {
470       retval = -1;
471     } else {
472       retval = LockedReceive(msg);
473       DCHECK(retval > 0);
474     }
475     cond_var_.Signal();
476   }
477   return retval;
478 }
479 
CloseChannel()480 void NaClIPCAdapter::CloseChannel() {
481   {
482     base::AutoLock lock(lock_);
483     locked_data_.channel_closed_ = true;
484     cond_var_.Signal();
485   }
486 
487   task_runner_->PostTask(
488       FROM_HERE, base::BindOnce(&NaClIPCAdapter::CloseChannelOnIOThread, this));
489 }
490 
MakeNaClDesc()491 NaClDesc* NaClIPCAdapter::MakeNaClDesc() {
492   return MakeNaClDescCustom(this);
493 }
494 
OnMessageReceived(const IPC::Message & msg)495 bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
496   uint32_t type = msg.type();
497 
498   if (type == IPC_REPLY_ID) {
499     int id = IPC::SyncMessage::GetMessageId(msg);
500     auto it = io_thread_data_.pending_sync_msgs_.find(id);
501     DCHECK(it != io_thread_data_.pending_sync_msgs_.end());
502     if (it != io_thread_data_.pending_sync_msgs_.end()) {
503       type = it->second;
504       io_thread_data_.pending_sync_msgs_.erase(it);
505     }
506   }
507   // Handle PpapiHostMsg_OpenResource outside the lock as it requires sending
508   // IPC to handle properly.
509   if (type == PpapiHostMsg_OpenResource::ID) {
510     base::PickleIterator iter = IPC::SyncMessage::GetDataIterator(&msg);
511     uint64_t token_lo;
512     uint64_t token_hi;
513     if (!IPC::ReadParam(&msg, &iter, &token_lo) ||
514         !IPC::ReadParam(&msg, &iter, &token_hi)) {
515       return false;
516     }
517 
518     if (token_lo != 0 || token_hi != 0) {
519       // We've received a valid file token. Instead of using the file
520       // descriptor received, we send the file token to the browser in
521       // exchange for a new file descriptor and file path information.
522       // That file descriptor can be used to construct a NaClDesc with
523       // identity-based validation caching.
524       //
525       // We do not use file descriptors from the renderer with validation
526       // caching; a compromised renderer should not be able to run
527       // arbitrary code in a plugin process.
528       //
529       // We intentionally avoid deserializing the next parameter, which is an
530       // instance of SerializedHandle, since doing so takes ownership from the
531       // IPC stack. If we fail to get a resource from the file token, we will
532       // still need to read the original parameter in SaveOpenResourceMessage().
533       DCHECK(!resolve_file_token_cb_.is_null());
534 
535       // resolve_file_token_cb_ must be invoked from the I/O thread.
536       resolve_file_token_cb_.Run(
537           token_lo, token_hi,
538           base::BindOnce(&NaClIPCAdapter::SaveOpenResourceMessage, this, msg));
539 
540       // In this case, we don't release the message to NaCl untrusted code
541       // immediately. We defer it until we get an async message back from the
542       // browser process.
543       return true;
544     }
545   }
546   return RewriteMessage(msg, type);
547 }
548 
RewriteMessage(const IPC::Message & msg,uint32_t type)549 bool NaClIPCAdapter::RewriteMessage(const IPC::Message& msg, uint32_t type) {
550   {
551     base::AutoLock lock(lock_);
552     std::unique_ptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
553 
554     typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
555     Handles handles;
556     std::unique_ptr<IPC::Message> new_msg;
557 
558     if (!locked_data_.nacl_msg_scanner_.ScanMessage(
559             msg, type, &handles, &new_msg))
560       return false;
561 
562     // Now add any descriptors we found to rewritten_msg. |handles| is usually
563     // empty, unless we read a message containing a FD or handle.
564     for (ppapi::proxy::SerializedHandle& handle : handles) {
565       std::unique_ptr<NaClDescWrapper> nacl_desc;
566       switch (handle.type()) {
567         case ppapi::proxy::SerializedHandle::SHARED_MEMORY_REGION: {
568           nacl_desc = MakeShmRegionNaClDesc(handle.TakeSharedMemoryRegion());
569           break;
570         }
571         case ppapi::proxy::SerializedHandle::SOCKET: {
572           nacl_desc = std::make_unique<NaClDescWrapper>(NaClDescSyncSocketMake(
573 #if BUILDFLAG(IS_WIN)
574               handle.descriptor().GetHandle()
575 #else
576               handle.descriptor().fd
577 #endif
578                   ));
579           break;
580         }
581         case ppapi::proxy::SerializedHandle::FILE: {
582           // Create the NaClDesc for the file descriptor. If quota checking is
583           // required, wrap it in a NaClDescQuota.
584           NaClDesc* desc = NaClDescIoMakeFromHandle(
585 #if BUILDFLAG(IS_WIN)
586               handle.descriptor().GetHandle(),
587 #else
588               handle.descriptor().fd,
589 #endif
590               TranslatePepperFileReadWriteOpenFlags(handle.open_flags()));
591           if (desc && handle.file_io()) {
592             desc = MakeNaClDescQuota(
593                 locked_data_.nacl_msg_scanner_.GetFile(handle.file_io()), desc);
594           }
595           if (desc)
596             nacl_desc = std::make_unique<NaClDescWrapper>(desc);
597           break;
598         }
599 
600         case ppapi::proxy::SerializedHandle::INVALID: {
601           // Nothing to do.
602           break;
603         }
604         // No default, so the compiler will warn us if new types get added.
605       }
606       if (nacl_desc.get())
607         rewritten_msg->AddDescriptor(std::move(nacl_desc));
608     }
609     if (new_msg)
610       SaveMessage(*new_msg, std::move(rewritten_msg));
611     else
612       SaveMessage(msg, std::move(rewritten_msg));
613     cond_var_.Signal();
614   }
615   return true;
616 }
617 
CreateOpenResourceReply(const IPC::Message & orig_msg,ppapi::proxy::SerializedHandle sh)618 std::unique_ptr<IPC::Message> CreateOpenResourceReply(
619     const IPC::Message& orig_msg,
620     ppapi::proxy::SerializedHandle sh) {
621   // The creation of new_msg must be kept in sync with
622   // SyncMessage::WriteSyncHeader.
623   std::unique_ptr<IPC::Message> new_msg(new IPC::Message(
624       orig_msg.routing_id(), orig_msg.type(), IPC::Message::PRIORITY_NORMAL));
625   new_msg->set_reply();
626   new_msg->WriteInt(IPC::SyncMessage::GetMessageId(orig_msg));
627 
628   // Write empty file tokens.
629   new_msg->WriteUInt64(0);  // token_lo
630   new_msg->WriteUInt64(0);  // token_hi
631 
632   ppapi::proxy::SerializedHandle::WriteHeader(sh.header(),
633                                               new_msg.get());
634   new_msg->WriteBool(true);  // valid == true
635   // The file descriptor is at index 0. There's only ever one file
636   // descriptor provided for this message type, so this will be correct.
637   new_msg->WriteInt(0);
638 
639   return new_msg;
640 }
641 
SaveOpenResourceMessage(const IPC::Message & orig_msg,IPC::PlatformFileForTransit ipc_fd,base::FilePath file_path)642 void NaClIPCAdapter::SaveOpenResourceMessage(
643     const IPC::Message& orig_msg,
644     IPC::PlatformFileForTransit ipc_fd,
645     base::FilePath file_path) {
646   // The path where an invalid ipc_fd is returned isn't currently
647   // covered by any tests.
648   if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
649     base::PickleIterator iter = IPC::SyncMessage::GetDataIterator(&orig_msg);
650     uint64_t token_lo;
651     uint64_t token_hi;
652     ppapi::proxy::SerializedHandle orig_sh;
653 
654     // These CHECKs could fail if the renderer sends this process a malformed
655     // message, but that's OK because in general the renderer can cause the NaCl
656     // loader process to exit.
657     CHECK(IPC::ReadParam(&orig_msg, &iter, &token_lo));
658     CHECK(IPC::ReadParam(&orig_msg, &iter, &token_hi));
659     CHECK(IPC::ReadParam(&orig_msg, &iter, &orig_sh));
660     CHECK(orig_sh.IsHandleValid());
661 
662     std::unique_ptr<NaClDescWrapper> desc_wrapper(
663         new NaClDescWrapper(NaClDescIoMakeFromHandle(
664 #if BUILDFLAG(IS_WIN)
665             orig_sh.descriptor().GetHandle(),
666 #else
667             orig_sh.descriptor().fd,
668 #endif
669             NACL_ABI_O_RDONLY)));
670 
671     // The file token didn't resolve successfully, so we give the
672     // original FD to the client without making a validated NaClDesc.
673     // However, we must rewrite the message to clear the file tokens.
674     std::unique_ptr<IPC::Message> new_msg =
675         CreateOpenResourceReply(orig_msg, std::move(orig_sh));
676 
677     std::unique_ptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
678     rewritten_msg->AddDescriptor(std::move(desc_wrapper));
679     {
680       base::AutoLock lock(lock_);
681       SaveMessage(*new_msg, std::move(rewritten_msg));
682       cond_var_.Signal();
683     }
684     return;
685   }
686 
687   // The file token was successfully resolved.
688   std::string file_path_str = file_path.AsUTF8Unsafe();
689   base::PlatformFile handle =
690       IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
691 
692   ppapi::proxy::SerializedHandle sh;
693   sh.set_file_handle(ipc_fd, PP_FILEOPENFLAG_READ, 0);
694   std::unique_ptr<IPC::Message> new_msg =
695       CreateOpenResourceReply(orig_msg, std::move(sh));
696   std::unique_ptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
697 
698   struct NaClDesc* desc =
699       NaClDescCreateWithFilePathMetadata(handle, file_path_str.c_str());
700   rewritten_msg->AddDescriptor(std::make_unique<NaClDescWrapper>(desc));
701   {
702     base::AutoLock lock(lock_);
703     SaveMessage(*new_msg, std::move(rewritten_msg));
704     cond_var_.Signal();
705   }
706 }
707 
OnChannelConnected(int32_t peer_pid)708 void NaClIPCAdapter::OnChannelConnected(int32_t peer_pid) {}
709 
OnChannelError()710 void NaClIPCAdapter::OnChannelError() {
711   CloseChannel();
712 }
713 
~NaClIPCAdapter()714 NaClIPCAdapter::~NaClIPCAdapter() {
715   // Make sure the channel is deleted on the IO thread.
716   task_runner_->PostTask(
717       FROM_HERE,
718       base::BindOnce(&DeleteChannel, io_thread_data_.channel_.release()));
719 }
720 
LockedReceive(NaClImcTypedMsgHdr * msg)721 int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr* msg) {
722   lock_.AssertAcquired();
723 
724   if (locked_data_.to_be_received_.empty())
725     return 0;
726   RewrittenMessage& current = *locked_data_.to_be_received_.front();
727 
728   int retval = current.Read(msg);
729 
730   // When a message is entirely consumed, remove it from the waiting queue.
731   if (current.is_consumed())
732     locked_data_.to_be_received_.pop();
733 
734   return retval;
735 }
736 
SendCompleteMessage(const char * buffer,size_t buffer_len)737 bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
738                                          size_t buffer_len) {
739   lock_.AssertAcquired();
740   // The message will have already been validated, so we know it's large enough
741   // for our header.
742   const NaClMessageHeader* header =
743       reinterpret_cast<const NaClMessageHeader*>(buffer);
744 
745   // Length of the message not including the body. The data passed to us by the
746   // plugin should match that in the message header. This should have already
747   // been validated by GetBufferStatus.
748   size_t body_len = buffer_len - sizeof(NaClMessageHeader);
749   CHECK(body_len == header->payload_size);
750 
751   // We actually discard the flags and only copy the ones we care about. This
752   // is just because message doesn't have a constructor that takes raw flags.
753   std::unique_ptr<IPC::Message> msg(new IPC::Message(
754       header->routing, header->type, IPC::Message::PRIORITY_NORMAL));
755   if (header->flags & IPC::Message::SYNC_BIT)
756     msg->set_sync();
757   if (header->flags & IPC::Message::REPLY_BIT)
758     msg->set_reply();
759   if (header->flags & IPC::Message::REPLY_ERROR_BIT)
760     msg->set_reply_error();
761   if (header->flags & IPC::Message::UNBLOCK_BIT)
762     msg->set_unblock(true);
763 
764   msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
765 
766   // Technically we didn't have to do any of the previous work in the lock. But
767   // sometimes our buffer will point to the to_be_sent_ string which is
768   // protected by the lock, and it's messier to factor Send() such that it can
769   // unlock for us. Holding the lock for the message construction, which is
770   // just some memcpys, shouldn't be a big deal.
771   lock_.AssertAcquired();
772   if (locked_data_.channel_closed_) {
773     // If we ever pass handles from the plugin to the host, we should close them
774     // here before we drop the message.
775     return false;
776   }
777 
778   // Scan all untrusted messages.
779   std::unique_ptr<IPC::Message> new_msg;
780   locked_data_.nacl_msg_scanner_.ScanUntrustedMessage(*msg, &new_msg);
781   if (new_msg)
782     msg = std::move(new_msg);
783 
784   // Actual send must be done on the I/O thread.
785   task_runner_->PostTask(
786       FROM_HERE, base::BindOnce(&NaClIPCAdapter::SendMessageOnIOThread, this,
787                                 std::move(msg)));
788   return true;
789 }
790 
ClearToBeSent()791 void NaClIPCAdapter::ClearToBeSent() {
792   lock_.AssertAcquired();
793 
794   // Don't let the string keep its buffer behind our back.
795   std::string empty;
796   locked_data_.to_be_sent_.swap(empty);
797 }
798 
ConnectChannelOnIOThread()799 void NaClIPCAdapter::ConnectChannelOnIOThread() {
800   if (!io_thread_data_.channel_->Connect())
801     NOTREACHED();
802 }
803 
CloseChannelOnIOThread()804 void NaClIPCAdapter::CloseChannelOnIOThread() {
805   io_thread_data_.channel_->Close();
806 }
807 
SendMessageOnIOThread(std::unique_ptr<IPC::Message> message)808 void NaClIPCAdapter::SendMessageOnIOThread(
809     std::unique_ptr<IPC::Message> message) {
810   int id = IPC::SyncMessage::GetMessageId(*message.get());
811   DCHECK(io_thread_data_.pending_sync_msgs_.find(id) ==
812          io_thread_data_.pending_sync_msgs_.end());
813 
814   // Handle PpapiHostMsg_OpenResource locally without sending an IPC to the
815   // renderer when possible.
816   PpapiHostMsg_OpenResource::Schema::SendParam send_params;
817   if (!open_resource_cb_.is_null() &&
818       message->type() == PpapiHostMsg_OpenResource::ID &&
819       PpapiHostMsg_OpenResource::ReadSendParam(message.get(), &send_params)) {
820     const std::string key = std::get<0>(send_params);
821     // Both open_resource_cb_ and SaveOpenResourceMessage must be invoked
822     // from the I/O thread.
823     if (open_resource_cb_.Run(
824             *message.get(), key,
825             base::BindOnce(&NaClIPCAdapter::SaveOpenResourceMessage, this))) {
826       // The callback sent a reply to the untrusted side.
827       return;
828     }
829   }
830 
831   if (message->is_sync())
832     io_thread_data_.pending_sync_msgs_[id] = message->type();
833   io_thread_data_.channel_->Send(message.release());
834 }
835 
SaveMessage(const IPC::Message & msg,std::unique_ptr<RewrittenMessage> rewritten_msg)836 void NaClIPCAdapter::SaveMessage(
837     const IPC::Message& msg,
838     std::unique_ptr<RewrittenMessage> rewritten_msg) {
839   lock_.AssertAcquired();
840   // There is some padding in this structure (the "padding" member is 16
841   // bits but this then gets padded to 32 bits). We want to be sure not to
842   // leak data to the untrusted plugin, so zero everything out first.
843   NaClMessageHeader header;
844   memset(&header, 0, sizeof(NaClMessageHeader));
845 
846   header.payload_size = static_cast<uint32_t>(msg.payload_size());
847   header.routing = msg.routing_id();
848   header.type = msg.type();
849   header.flags = msg.flags();
850   header.num_fds = static_cast<uint16_t>(rewritten_msg->desc_count());
851 
852   rewritten_msg->SetData(header, msg.payload(), msg.payload_size());
853   locked_data_.to_be_received_.push(std::move(rewritten_msg));
854 }
855 
TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags)856 int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags) {
857   return TranslatePepperFileReadWriteOpenFlags(pp_open_flags);
858 }
859