1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/nacl/loader/nacl_ipc_adapter.h"
6
7#include <limits.h>
8#include <string.h>
9
10#include "base/basictypes.h"
11#include "base/bind.h"
12#include "base/location.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/memory/shared_memory.h"
15#include "build/build_config.h"
16#include "ipc/ipc_channel.h"
17#include "ipc/ipc_platform_file.h"
18#include "native_client/src/trusted/desc/nacl_desc_base.h"
19#include "native_client/src/trusted/desc/nacl_desc_custom.h"
20#include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
21#include "native_client/src/trusted/desc/nacl_desc_io.h"
22#include "native_client/src/trusted/desc/nacl_desc_sync_socket.h"
23#include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
24#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
25#include "ppapi/c/ppb_file_io.h"
26#include "ppapi/proxy/ppapi_messages.h"
27#include "ppapi/proxy/serialized_handle.h"
28
29namespace {
30
31enum BufferSizeStatus {
32  // The buffer contains a full message with no extra bytes.
33  MESSAGE_IS_COMPLETE,
34
35  // The message doesn't fit and the buffer contains only some of it.
36  MESSAGE_IS_TRUNCATED,
37
38  // The buffer contains a full message + extra data.
39  MESSAGE_HAS_EXTRA_DATA
40};
41
42BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
43  if (len < sizeof(NaClIPCAdapter::NaClMessageHeader))
44    return MESSAGE_IS_TRUNCATED;
45
46  const NaClIPCAdapter::NaClMessageHeader* header =
47      reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(data);
48  uint32 message_size =
49      sizeof(NaClIPCAdapter::NaClMessageHeader) + header->payload_size;
50
51  if (len == message_size)
52    return MESSAGE_IS_COMPLETE;
53  if (len > message_size)
54    return MESSAGE_HAS_EXTRA_DATA;
55  return MESSAGE_IS_TRUNCATED;
56}
57
58// This object allows the NaClDesc to hold a reference to a NaClIPCAdapter and
59// forward calls to it.
60struct DescThunker {
61  explicit DescThunker(NaClIPCAdapter* adapter_param)
62      : adapter(adapter_param) {
63  }
64  scoped_refptr<NaClIPCAdapter> adapter;
65};
66
67NaClIPCAdapter* ToAdapter(void* handle) {
68  return static_cast<DescThunker*>(handle)->adapter.get();
69}
70
71// NaClDescCustom implementation.
72void NaClDescCustomDestroy(void* handle) {
73  delete static_cast<DescThunker*>(handle);
74}
75
76ssize_t NaClDescCustomSendMsg(void* handle, const NaClImcTypedMsgHdr* msg,
77                              int /* flags */) {
78  return static_cast<ssize_t>(ToAdapter(handle)->Send(msg));
79}
80
81ssize_t NaClDescCustomRecvMsg(void* handle, NaClImcTypedMsgHdr* msg,
82                              int /* flags */) {
83  return static_cast<ssize_t>(ToAdapter(handle)->BlockingReceive(msg));
84}
85
86NaClDesc* MakeNaClDescCustom(NaClIPCAdapter* adapter) {
87  NaClDescCustomFuncs funcs = NACL_DESC_CUSTOM_FUNCS_INITIALIZER;
88  funcs.Destroy = NaClDescCustomDestroy;
89  funcs.SendMsg = NaClDescCustomSendMsg;
90  funcs.RecvMsg = NaClDescCustomRecvMsg;
91  // NaClDescMakeCustomDesc gives us a reference on the returned NaClDesc.
92  return NaClDescMakeCustomDesc(new DescThunker(adapter), &funcs);
93}
94
95void DeleteChannel(IPC::Channel* channel) {
96  delete channel;
97}
98
99// Translates Pepper's read/write open flags into the NaCl equivalents.
100// Since the host has already opened the file, flags such as O_CREAT, O_TRUNC,
101// and O_EXCL don't make sense, so we filter those out. If no read or write
102// flags are set, the function returns NACL_ABI_O_RDONLY as a safe fallback.
103int TranslatePepperFileReadWriteOpenFlags(int32_t pp_open_flags) {
104  bool read = (pp_open_flags & PP_FILEOPENFLAG_READ) != 0;
105  bool write = (pp_open_flags & PP_FILEOPENFLAG_WRITE) != 0;
106  bool append = (pp_open_flags & PP_FILEOPENFLAG_APPEND) != 0;
107
108  int nacl_open_flag = NACL_ABI_O_RDONLY;  // NACL_ABI_O_RDONLY == 0.
109  if (read && (write || append)) {
110    nacl_open_flag = NACL_ABI_O_RDWR;
111  } else if (write || append) {
112    nacl_open_flag = NACL_ABI_O_WRONLY;
113  } else if (!read) {
114    DLOG(WARNING) << "One of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, "
115                  << "or PP_FILEOPENFLAG_APPEND should be set.";
116  }
117  if (append)
118    nacl_open_flag |= NACL_ABI_O_APPEND;
119
120  return nacl_open_flag;
121}
122
123class NaClDescWrapper {
124 public:
125  explicit NaClDescWrapper(NaClDesc* desc): desc_(desc) {}
126  ~NaClDescWrapper() {
127    NaClDescUnref(desc_);
128  }
129
130  NaClDesc* desc() { return desc_; }
131
132 private:
133  NaClDesc* desc_;
134  DISALLOW_COPY_AND_ASSIGN(NaClDescWrapper);
135};
136
137}  // namespace
138
139class NaClIPCAdapter::RewrittenMessage
140    : public base::RefCounted<RewrittenMessage> {
141 public:
142  RewrittenMessage();
143
144  bool is_consumed() const { return data_read_cursor_ == data_len_; }
145
146  void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
147               const void* payload, size_t payload_length);
148
149  int Read(NaClImcTypedMsgHdr* msg);
150
151  void AddDescriptor(NaClDescWrapper* desc) { descs_.push_back(desc); }
152
153  size_t desc_count() const { return descs_.size(); }
154
155 private:
156  friend class base::RefCounted<RewrittenMessage>;
157  ~RewrittenMessage() {}
158
159  scoped_ptr<char[]> data_;
160  size_t data_len_;
161
162  // Offset into data where the next read will happen. This will be equal to
163  // data_len_ when all data has been consumed.
164  size_t data_read_cursor_;
165
166  // Wrapped descriptors for transfer to untrusted code.
167  ScopedVector<NaClDescWrapper> descs_;
168};
169
170NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
171    : data_len_(0),
172      data_read_cursor_(0) {
173}
174
175void NaClIPCAdapter::RewrittenMessage::SetData(
176    const NaClIPCAdapter::NaClMessageHeader& header,
177    const void* payload,
178    size_t payload_length) {
179  DCHECK(!data_.get() && data_len_ == 0);
180  size_t header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
181  data_len_ = header_len + payload_length;
182  data_.reset(new char[data_len_]);
183
184  memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
185  memcpy(&data_[header_len], payload, payload_length);
186}
187
188int NaClIPCAdapter::RewrittenMessage::Read(NaClImcTypedMsgHdr* msg) {
189  CHECK(data_len_ >= data_read_cursor_);
190  char* dest_buffer = static_cast<char*>(msg->iov[0].base);
191  size_t dest_buffer_size = msg->iov[0].length;
192  size_t bytes_to_write = std::min(dest_buffer_size,
193                                   data_len_ - data_read_cursor_);
194  if (bytes_to_write == 0)
195    return 0;
196
197  memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
198  data_read_cursor_ += bytes_to_write;
199
200  // Once all data has been consumed, transfer any file descriptors.
201  if (is_consumed()) {
202    nacl_abi_size_t desc_count = static_cast<nacl_abi_size_t>(descs_.size());
203    CHECK(desc_count <= msg->ndesc_length);
204    msg->ndesc_length = desc_count;
205    for (nacl_abi_size_t i = 0; i < desc_count; i++) {
206      // Copy the NaClDesc to the buffer and add a ref so it won't be freed
207      // when we clear our ScopedVector.
208      msg->ndescv[i] = descs_[i]->desc();
209      NaClDescRef(descs_[i]->desc());
210    }
211    descs_.clear();
212  } else {
213    msg->ndesc_length = 0;
214  }
215  return static_cast<int>(bytes_to_write);
216}
217
218NaClIPCAdapter::LockedData::LockedData()
219    : channel_closed_(false) {
220}
221
222NaClIPCAdapter::LockedData::~LockedData() {
223}
224
225NaClIPCAdapter::IOThreadData::IOThreadData() {
226}
227
228NaClIPCAdapter::IOThreadData::~IOThreadData() {
229}
230
231NaClIPCAdapter::NaClIPCAdapter(const IPC::ChannelHandle& handle,
232                               base::TaskRunner* runner)
233    : lock_(),
234      cond_var_(&lock_),
235      task_runner_(runner),
236      locked_data_() {
237  io_thread_data_.channel_.reset(
238      new IPC::Channel(handle, IPC::Channel::MODE_SERVER, this));
239  // Note, we can not PostTask for ConnectChannelOnIOThread here. If we did,
240  // and that task ran before this constructor completes, the reference count
241  // would go to 1 and then to 0 because of the Task, before we've been returned
242  // to the owning scoped_refptr, which is supposed to give us our first
243  // ref-count.
244}
245
246NaClIPCAdapter::NaClIPCAdapter(scoped_ptr<IPC::Channel> channel,
247                               base::TaskRunner* runner)
248    : lock_(),
249      cond_var_(&lock_),
250      task_runner_(runner),
251      locked_data_() {
252  io_thread_data_.channel_ = channel.Pass();
253}
254
255void NaClIPCAdapter::ConnectChannel() {
256  task_runner_->PostTask(FROM_HERE,
257      base::Bind(&NaClIPCAdapter::ConnectChannelOnIOThread, this));
258}
259
260// Note that this message is controlled by the untrusted code. So we should be
261// skeptical of anything it contains and quick to give up if anything is fishy.
262int NaClIPCAdapter::Send(const NaClImcTypedMsgHdr* msg) {
263  if (msg->iov_length != 1)
264    return -1;
265
266  base::AutoLock lock(lock_);
267
268  const char* input_data = static_cast<char*>(msg->iov[0].base);
269  size_t input_data_len = msg->iov[0].length;
270  if (input_data_len > IPC::Channel::kMaximumMessageSize) {
271    ClearToBeSent();
272    return -1;
273  }
274
275  // current_message[_len] refers to the total input data received so far.
276  const char* current_message;
277  size_t current_message_len;
278  bool did_append_input_data;
279  if (locked_data_.to_be_sent_.empty()) {
280    // No accumulated data, we can avoid a copy by referring to the input
281    // buffer (the entire message fitting in one call is the common case).
282    current_message = input_data;
283    current_message_len = input_data_len;
284    did_append_input_data = false;
285  } else {
286    // We've already accumulated some data, accumulate this new data and
287    // point to the beginning of the buffer.
288
289    // Make sure our accumulated message size doesn't overflow our max. Since
290    // we know that data_len < max size (checked above) and our current
291    // accumulated value is also < max size, we just need to make sure that
292    // 2x max size can never overflow.
293    COMPILE_ASSERT(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
294                   MaximumMessageSizeWillOverflow);
295    size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
296    if (new_size > IPC::Channel::kMaximumMessageSize) {
297      ClearToBeSent();
298      return -1;
299    }
300
301    locked_data_.to_be_sent_.append(input_data, input_data_len);
302    current_message = &locked_data_.to_be_sent_[0];
303    current_message_len = locked_data_.to_be_sent_.size();
304    did_append_input_data = true;
305  }
306
307  // Check the total data we've accumulated so far to see if it contains a full
308  // message.
309  switch (GetBufferStatus(current_message, current_message_len)) {
310    case MESSAGE_IS_COMPLETE: {
311      // Got a complete message, can send it out. This will be the common case.
312      bool success = SendCompleteMessage(current_message, current_message_len);
313      ClearToBeSent();
314      return success ? static_cast<int>(input_data_len) : -1;
315    }
316    case MESSAGE_IS_TRUNCATED:
317      // For truncated messages, just accumulate the new data (if we didn't
318      // already do so above) and go back to waiting for more.
319      if (!did_append_input_data)
320        locked_data_.to_be_sent_.append(input_data, input_data_len);
321      return static_cast<int>(input_data_len);
322    case MESSAGE_HAS_EXTRA_DATA:
323    default:
324      // When the plugin gives us too much data, it's an error.
325      ClearToBeSent();
326      return -1;
327  }
328}
329
330int NaClIPCAdapter::BlockingReceive(NaClImcTypedMsgHdr* msg) {
331  if (msg->iov_length != 1)
332    return -1;
333
334  int retval = 0;
335  {
336    base::AutoLock lock(lock_);
337    while (locked_data_.to_be_received_.empty() &&
338           !locked_data_.channel_closed_)
339      cond_var_.Wait();
340    if (locked_data_.channel_closed_) {
341      retval = -1;
342    } else {
343      retval = LockedReceive(msg);
344      DCHECK(retval > 0);
345    }
346  }
347  cond_var_.Signal();
348  return retval;
349}
350
351void NaClIPCAdapter::CloseChannel() {
352  {
353    base::AutoLock lock(lock_);
354    locked_data_.channel_closed_ = true;
355  }
356  cond_var_.Signal();
357
358  task_runner_->PostTask(FROM_HERE,
359      base::Bind(&NaClIPCAdapter::CloseChannelOnIOThread, this));
360}
361
362NaClDesc* NaClIPCAdapter::MakeNaClDesc() {
363  return MakeNaClDescCustom(this);
364}
365
366#if defined(OS_POSIX)
367int NaClIPCAdapter::TakeClientFileDescriptor() {
368  return io_thread_data_.channel_->TakeClientFileDescriptor();
369}
370#endif
371
372bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
373  {
374    base::AutoLock lock(lock_);
375
376    scoped_refptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
377
378    typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
379    Handles handles;
380    scoped_ptr<IPC::Message> new_msg;
381    if (!locked_data_.nacl_msg_scanner_.ScanMessage(msg, &handles, &new_msg))
382      return false;
383
384    // Now add any descriptors we found to rewritten_msg. |handles| is usually
385    // empty, unless we read a message containing a FD or handle.
386    for (Handles::const_iterator iter = handles.begin();
387         iter != handles.end();
388         ++iter) {
389      scoped_ptr<NaClDescWrapper> nacl_desc;
390      switch (iter->type()) {
391        case ppapi::proxy::SerializedHandle::SHARED_MEMORY: {
392          const base::SharedMemoryHandle& shm_handle = iter->shmem();
393          uint32_t size = iter->size();
394          nacl_desc.reset(new NaClDescWrapper(NaClDescImcShmMake(
395#if defined(OS_WIN)
396              shm_handle,
397#else
398              shm_handle.fd,
399#endif
400              static_cast<size_t>(size))));
401          break;
402        }
403        case ppapi::proxy::SerializedHandle::SOCKET: {
404          nacl_desc.reset(new NaClDescWrapper(NaClDescSyncSocketMake(
405#if defined(OS_WIN)
406              iter->descriptor()
407#else
408              iter->descriptor().fd
409#endif
410          )));
411          break;
412        }
413        case ppapi::proxy::SerializedHandle::CHANNEL_HANDLE: {
414          // Check that this came from a PpapiMsg_CreateNaClChannel message.
415          // This code here is only appropriate for that message.
416          DCHECK(msg.type() == PpapiMsg_CreateNaClChannel::ID);
417          IPC::ChannelHandle channel_handle =
418              IPC::Channel::GenerateVerifiedChannelID("nacl");
419          scoped_refptr<NaClIPCAdapter> ipc_adapter(
420              new NaClIPCAdapter(channel_handle, task_runner_.get()));
421          ipc_adapter->ConnectChannel();
422#if defined(OS_POSIX)
423          channel_handle.socket = base::FileDescriptor(
424              ipc_adapter->TakeClientFileDescriptor(), true);
425#endif
426          nacl_desc.reset(new NaClDescWrapper(ipc_adapter->MakeNaClDesc()));
427          // Send back a message that the channel was created.
428          scoped_ptr<IPC::Message> response(
429              new PpapiHostMsg_ChannelCreated(channel_handle));
430          task_runner_->PostTask(FROM_HERE,
431              base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
432                         base::Passed(&response)));
433          break;
434        }
435        case ppapi::proxy::SerializedHandle::FILE:
436          // IMPORTANT: The NaClDescIoDescFromHandleAllocCtor function creates
437          // a NaClDesc that checks file flags before reading and writing. This
438          // is essential since PPB_FileIO now sends a file descriptor to the
439          // plugin which may have write capabilities. We can't allow the plugin
440          // to write with it since it could bypass quota checks, which still
441          // happen in the host.
442          nacl_desc.reset(new NaClDescWrapper(NaClDescIoDescFromHandleAllocCtor(
443#if defined(OS_WIN)
444              iter->descriptor(),
445#else
446              iter->descriptor().fd,
447#endif
448              TranslatePepperFileReadWriteOpenFlags(iter->open_flag()))));
449          break;
450        case ppapi::proxy::SerializedHandle::INVALID: {
451          // Nothing to do. TODO(dmichael): Should we log this? Or is it
452          // sometimes okay to pass an INVALID handle?
453          break;
454        }
455        // No default, so the compiler will warn us if new types get added.
456      }
457      if (nacl_desc.get())
458        rewritten_msg->AddDescriptor(nacl_desc.release());
459    }
460    if (new_msg)
461      SaveMessage(*new_msg, rewritten_msg.get());
462    else
463      SaveMessage(msg, rewritten_msg.get());
464  }
465  cond_var_.Signal();
466  return true;
467}
468
469void NaClIPCAdapter::OnChannelConnected(int32 peer_pid) {
470}
471
472void NaClIPCAdapter::OnChannelError() {
473  CloseChannel();
474}
475
476NaClIPCAdapter::~NaClIPCAdapter() {
477  // Make sure the channel is deleted on the IO thread.
478  task_runner_->PostTask(FROM_HERE,
479      base::Bind(&DeleteChannel, io_thread_data_.channel_.release()));
480}
481
482int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr* msg) {
483  lock_.AssertAcquired();
484
485  if (locked_data_.to_be_received_.empty())
486    return 0;
487  scoped_refptr<RewrittenMessage> current =
488      locked_data_.to_be_received_.front();
489
490  int retval = current->Read(msg);
491
492  // When a message is entirely consumed, remove if from the waiting queue.
493  if (current->is_consumed())
494    locked_data_.to_be_received_.pop();
495
496  return retval;
497}
498
499bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
500                                         size_t buffer_len) {
501  lock_.AssertAcquired();
502  // The message will have already been validated, so we know it's large enough
503  // for our header.
504  const NaClMessageHeader* header =
505      reinterpret_cast<const NaClMessageHeader*>(buffer);
506
507  // Length of the message not including the body. The data passed to us by the
508  // plugin should match that in the message header. This should have already
509  // been validated by GetBufferStatus.
510  int body_len = static_cast<int>(buffer_len - sizeof(NaClMessageHeader));
511  DCHECK(body_len == static_cast<int>(header->payload_size));
512
513  // We actually discard the flags and only copy the ones we care about. This
514  // is just because message doesn't have a constructor that takes raw flags.
515  scoped_ptr<IPC::Message> msg(
516      new IPC::Message(header->routing, header->type,
517                       IPC::Message::PRIORITY_NORMAL));
518  if (header->flags & IPC::Message::SYNC_BIT)
519    msg->set_sync();
520  if (header->flags & IPC::Message::REPLY_BIT)
521    msg->set_reply();
522  if (header->flags & IPC::Message::REPLY_ERROR_BIT)
523    msg->set_reply_error();
524  if (header->flags & IPC::Message::UNBLOCK_BIT)
525    msg->set_unblock(true);
526
527  msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
528
529  // Technically we didn't have to do any of the previous work in the lock. But
530  // sometimes our buffer will point to the to_be_sent_ string which is
531  // protected by the lock, and it's messier to factor Send() such that it can
532  // unlock for us. Holding the lock for the message construction, which is
533  // just some memcpys, shouldn't be a big deal.
534  lock_.AssertAcquired();
535  if (locked_data_.channel_closed_) {
536    // If we ever pass handles from the plugin to the host, we should close them
537    // here before we drop the message.
538    return false;
539  }
540
541  if (msg->is_sync())
542    locked_data_.nacl_msg_scanner_.RegisterSyncMessageForReply(*msg);
543
544  // Actual send must be done on the I/O thread.
545  task_runner_->PostTask(FROM_HERE,
546      base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
547                 base::Passed(&msg)));
548  return true;
549}
550
551void NaClIPCAdapter::ClearToBeSent() {
552  lock_.AssertAcquired();
553
554  // Don't let the string keep its buffer behind our back.
555  std::string empty;
556  locked_data_.to_be_sent_.swap(empty);
557}
558
559void NaClIPCAdapter::ConnectChannelOnIOThread() {
560  if (!io_thread_data_.channel_->Connect())
561    NOTREACHED();
562}
563
564void NaClIPCAdapter::CloseChannelOnIOThread() {
565  io_thread_data_.channel_->Close();
566}
567
568void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
569  io_thread_data_.channel_->Send(message.release());
570}
571
572void NaClIPCAdapter::SaveMessage(const IPC::Message& msg,
573                                 RewrittenMessage* rewritten_msg) {
574  lock_.AssertAcquired();
575  // There is some padding in this structure (the "padding" member is 16
576  // bits but this then gets padded to 32 bits). We want to be sure not to
577  // leak data to the untrusted plugin, so zero everything out first.
578  NaClMessageHeader header;
579  memset(&header, 0, sizeof(NaClMessageHeader));
580
581  header.payload_size = static_cast<uint32>(msg.payload_size());
582  header.routing = msg.routing_id();
583  header.type = msg.type();
584  header.flags = msg.flags();
585  header.num_fds = static_cast<int>(rewritten_msg->desc_count());
586
587  rewritten_msg->SetData(header, msg.payload(), msg.payload_size());
588  locked_data_.to_be_received_.push(rewritten_msg);
589}
590
591int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags) {
592  return TranslatePepperFileReadWriteOpenFlags(pp_open_flags);
593}
594