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("a_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 "a_interface->base)) {
191 return &desc->base;
192 }
193 if (desc)
194 NaClDescUnref(reinterpret_cast<NaClDesc*>(desc));
195 }
196
197 if (quota_interface)
198 NaClDescQuotaInterfaceUnref("a_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