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