Home | History | Annotate | Download | only in loader
      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 
     29 namespace {
     30 
     31 enum 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 
     42 BufferSizeStatus 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.
     60 struct DescThunker {
     61   explicit DescThunker(NaClIPCAdapter* adapter_param)
     62       : adapter(adapter_param) {
     63   }
     64   scoped_refptr<NaClIPCAdapter> adapter;
     65 };
     66 
     67 NaClIPCAdapter* ToAdapter(void* handle) {
     68   return static_cast<DescThunker*>(handle)->adapter.get();
     69 }
     70 
     71 // NaClDescCustom implementation.
     72 void NaClDescCustomDestroy(void* handle) {
     73   delete static_cast<DescThunker*>(handle);
     74 }
     75 
     76 ssize_t NaClDescCustomSendMsg(void* handle, const NaClImcTypedMsgHdr* msg,
     77                               int /* flags */) {
     78   return static_cast<ssize_t>(ToAdapter(handle)->Send(msg));
     79 }
     80 
     81 ssize_t NaClDescCustomRecvMsg(void* handle, NaClImcTypedMsgHdr* msg,
     82                               int /* flags */) {
     83   return static_cast<ssize_t>(ToAdapter(handle)->BlockingReceive(msg));
     84 }
     85 
     86 NaClDesc* 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 
     95 void 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.
    103 int 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 
    123 class 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 
    139 class 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 
    170 NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
    171     : data_len_(0),
    172       data_read_cursor_(0) {
    173 }
    174 
    175 void 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 
    188 int 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 
    218 NaClIPCAdapter::LockedData::LockedData()
    219     : channel_closed_(false) {
    220 }
    221 
    222 NaClIPCAdapter::LockedData::~LockedData() {
    223 }
    224 
    225 NaClIPCAdapter::IOThreadData::IOThreadData() {
    226 }
    227 
    228 NaClIPCAdapter::IOThreadData::~IOThreadData() {
    229 }
    230 
    231 NaClIPCAdapter::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 
    246 NaClIPCAdapter::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 
    255 void 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.
    262 int 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 
    330 int 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 
    351 void 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 
    362 NaClDesc* NaClIPCAdapter::MakeNaClDesc() {
    363   return MakeNaClDescCustom(this);
    364 }
    365 
    366 #if defined(OS_POSIX)
    367 int NaClIPCAdapter::TakeClientFileDescriptor() {
    368   return io_thread_data_.channel_->TakeClientFileDescriptor();
    369 }
    370 #endif
    371 
    372 bool 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 
    469 void NaClIPCAdapter::OnChannelConnected(int32 peer_pid) {
    470 }
    471 
    472 void NaClIPCAdapter::OnChannelError() {
    473   CloseChannel();
    474 }
    475 
    476 NaClIPCAdapter::~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 
    482 int 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 
    499 bool 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 
    551 void 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 
    559 void NaClIPCAdapter::ConnectChannelOnIOThread() {
    560   if (!io_thread_data_.channel_->Connect())
    561     NOTREACHED();
    562 }
    563 
    564 void NaClIPCAdapter::CloseChannelOnIOThread() {
    565   io_thread_data_.channel_->Close();
    566 }
    567 
    568 void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
    569   io_thread_data_.channel_->Send(message.release());
    570 }
    571 
    572 void 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 
    591 int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags) {
    592   return TranslatePepperFileReadWriteOpenFlags(pp_open_flags);
    593 }
    594