Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3104)

Unified Diff: chrome/nacl/nacl_ipc_adapter.cc

Issue 9863005: Initial implementation of an IPC adapter to expose Chrome IPC to Native Client. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/nacl/nacl_ipc_adapter.h ('k') | chrome/nacl/nacl_ipc_adapter_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/nacl/nacl_ipc_adapter.cc
diff --git a/chrome/nacl/nacl_ipc_adapter.cc b/chrome/nacl/nacl_ipc_adapter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f5720815d8a588d7cc23671372fae30b407a3a36
--- /dev/null
+++ b/chrome/nacl/nacl_ipc_adapter.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/nacl/nacl_ipc_adapter.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+
+namespace {
+
+// The NaCl message header type is always like Posix. So when we're compiling
+// on Posix platforms, we can check that our header is correct.
+#if defined(OS_POSIX)
+COMPILE_ASSERT(sizeof(NaClIPCAdapter::NaClMessageHeader) ==
+ sizeof(IPC::Message::Header),
+ PosixHeaderSizesDontMatch);
+#endif
+
+enum BufferSizeStatus {
+ // The buffer contains a full message with no extra bytes.
+ MESSAGE_IS_COMPLETE,
+
+ // The message doesn't fit and the buffer contains only some of it.
+ MESSAGE_IS_TRUNCATED,
+
+ // The buffer contains a full message + extra data.
+ BUFFER_UNDERFLOW
+};
+
+BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
+ if (len < sizeof(NaClIPCAdapter::NaClMessageHeader))
+ return MESSAGE_IS_TRUNCATED;
+
+ const NaClIPCAdapter::NaClMessageHeader* header =
+ reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(data);
+ uint32 message_size =
+ sizeof(NaClIPCAdapter::NaClMessageHeader) + header->payload_size;
+
+ if (len == message_size)
+ return MESSAGE_IS_COMPLETE;
+ if (len > message_size)
+ return BUFFER_UNDERFLOW;
+ return MESSAGE_IS_TRUNCATED;
+}
+
+} // namespace
+
+class NaClIPCAdapter::RewrittenMessage
+ : public base::RefCounted<RewrittenMessage> {
+ public:
+ RewrittenMessage();
+
+ bool is_consumed() const { return data_read_cursor_ == data_len_; }
+
+ void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
+ const void* payload, size_t payload_length);
+
+ int Read(char* dest_buffer, int dest_buffer_size);
+
+ private:
+ scoped_array<char> data_;
+ int data_len_;
+
+ // Offset into data where the next read will happen. This will be equal to
+ // data_len_ when all data has been consumed.
+ int data_read_cursor_;
+};
+
+NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
+ : data_len_(0),
+ data_read_cursor_(0) {
+}
+
+void NaClIPCAdapter::RewrittenMessage::SetData(
+ const NaClIPCAdapter::NaClMessageHeader& header,
+ const void* payload,
+ size_t payload_length) {
+ DCHECK(!data_.get() && data_len_ == 0);
+ int header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
+ data_len_ = header_len + static_cast<int>(payload_length);
+ data_.reset(new char[data_len_]);
+
+ memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
+ memcpy(&data_[header_len], payload, payload_length);
+}
+
+int NaClIPCAdapter::RewrittenMessage::Read(char* dest_buffer,
+ int dest_buffer_size) {
+ int bytes_to_write = std::min(dest_buffer_size,
+ data_len_ - data_read_cursor_);
+ if (bytes_to_write == 0)
+ return 0;
+
+ memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
+ data_read_cursor_ += bytes_to_write;
+ return bytes_to_write;
+}
+
+NaClIPCAdapter::LockedData::LockedData() : channel_closed_(false) {
+}
+
+NaClIPCAdapter::IOThreadData::IOThreadData() {
+}
+
+NaClIPCAdapter::NaClIPCAdapter(const IPC::ChannelHandle& handle,
+ base::TaskRunner* runner)
+ : lock_(),
+ cond_var_(&lock_),
+ task_runner_(runner),
+ locked_data_() {
+ io_thread_data_.channel_.reset(
+ new IPC::Channel(handle, IPC::Channel::MODE_SERVER, this));
+}
+
+NaClIPCAdapter::NaClIPCAdapter(scoped_ptr<IPC::Channel> channel,
+ base::TaskRunner* runner)
+ : lock_(),
+ cond_var_(&lock_),
+ task_runner_(runner),
+ locked_data_() {
+ io_thread_data_.channel_ = channel.Pass();
+}
+
+NaClIPCAdapter::~NaClIPCAdapter() {
+}
+
+// Note that this message is controlled by the untrusted code. So we should be
+// skeptical of anything it contains and quick to give up if anything is fishy.
+int NaClIPCAdapter::Send(const char* input_data, size_t input_data_len) {
+ base::AutoLock lock(lock_);
+
+ if (input_data_len > IPC::Channel::kMaximumMessageSize) {
+ ClearToBeSent();
+ return -1;
+ }
+
+ const char* current_message = input_data;
+ size_t current_message_len = input_data_len;
+ if (!locked_data_.to_be_sent_.empty()) {
+ // We've already accumulated some data.
+
+ // Make sure our accumulated message size doesn't overflow our max. Since
+ // we know that data_len < max size (checked above) and our current
+ // accumulated value is also < max size, we just need to make sure that
+ // 2x max size can never overflow.
+ COMPILE_ASSERT(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
+ MaximumMessageSizeWillOverflow);
+ size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
+ if (new_size > IPC::Channel::kMaximumMessageSize) {
+ ClearToBeSent();
+ return -1;
+ }
+
+ locked_data_.to_be_sent_.append(input_data, input_data_len);
+ current_message = &locked_data_.to_be_sent_[0];
+ current_message_len = locked_data_.to_be_sent_.size();
+ }
+
+ // Nothing accumulated, so see if the input data contains a full message.
bbudge 2012/03/26 22:52:30 Something seems wrong here. The above 'if' block w
brettw 2012/03/27 17:40:27 The comment was misleading but I think the code wa
+ // This will be the common case.
+ switch (GetBufferStatus(current_message, current_message_len)) {
+ case MESSAGE_IS_COMPLETE: {
+ // Got a complete message, can send it out.
+ bool success = SendCompleteMessage(current_message, current_message_len);
+ ClearToBeSent();
+ return success ? input_data_len : -1;
+ }
+ case MESSAGE_IS_TRUNCATED:
+ // For truncated messages, just accumulate the new data and go back to
+ // waiting for more.
+ locked_data_.to_be_sent_.append(input_data, input_data_len);
bbudge 2012/03/26 22:52:30 Seems like data could get added twice.
brettw 2012/03/27 17:40:27 Whoops! Nice catch. I modified the test to send in
+ return input_data_len;
+ case BUFFER_UNDERFLOW:
+ default:
+ // When the plugin gives us too much data, it's an error.
+ ClearToBeSent();
+ return -1;
+ }
+}
+
+int NaClIPCAdapter::BlockingReceive(char* output_buffer,
+ int output_buffer_size) {
+ int retval = 0;
+ {
+ base::AutoLock lock(lock_);
+ while (locked_data_.to_be_received_.empty() &&
+ !locked_data_.channel_closed_)
+ cond_var_.Wait();
+ if (locked_data_.channel_closed_) {
+ retval = -1;
+ } else {
+ retval = LockedReceive(output_buffer, output_buffer_size);
+ DCHECK(retval > 0);
+ }
+ }
+ cond_var_.Signal();
+ return retval;
+}
+
+void NaClIPCAdapter::CloseChannel() {
+ {
+ base::AutoLock lock(lock_);
+ locked_data_.channel_closed_ = true;
+ }
+ cond_var_.Signal();
+
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&NaClIPCAdapter::CloseChannelOnIOThread, this));
+}
+
+bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& message) {
+ {
+ base::AutoLock lock(lock_);
+
+ // There is some padding in this structure (the "padding" member is 16
+ // bits but this then gets padded to 32 bits). We want to be sure not to
+ // leak memory to the untrusted plugin, so zero everything out first.
bbudge 2012/03/26 22:52:30 Maybe memory -> data so it doesn't sound like a me
+ NaClMessageHeader header;
+ memset(&header, 0, sizeof(NaClMessageHeader));
+
+ header.payload_size = message.payload_size();
+ header.routing = message.routing_id();
+ header.type = message.type();
+ header.flags = message.flags();
+ header.num_fds = 0; // TODO(brettw) hook this up.
+
+ scoped_refptr<RewrittenMessage> dest(new RewrittenMessage);
+ dest->SetData(header, message.payload(), message.payload_size());
+ locked_data_.to_be_received_.push(dest);
+ }
+ cond_var_.Signal();
+ return true;
+}
+
+void NaClIPCAdapter::OnChannelConnected(int32 peer_pid) {
+}
+
+void NaClIPCAdapter::OnChannelError() {
+ CloseChannel();
+}
+
+int NaClIPCAdapter::LockedReceive(char* output_buffer, int output_buffer_size) {
+ lock_.AssertAcquired();
+
+ if (locked_data_.to_be_received_.empty())
+ return 0;
+ scoped_refptr<RewrittenMessage> current =
+ locked_data_.to_be_received_.front();
+
+ int retval = current->Read(output_buffer, output_buffer_size);
+
+ // When a message is entirely consumed, remove if from the waiting queue.
+ if (current->is_consumed())
+ locked_data_.to_be_received_.pop();
+ return retval;
+}
+
+bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
+ size_t buffer_len) {
+
+ // The message will have already been validated, so we know it's large enough
+ // for our header.
+ const NaClMessageHeader* header =
+ reinterpret_cast<const NaClMessageHeader*>(buffer);
+
+ // Length of the message not including the body. The data passed to us by the
+ // plugin should match that in the message header. This should have already
+ // been validated by GetBufferStatus.
+ int body_len = buffer_len - sizeof(NaClMessageHeader);
+ DCHECK(body_len == static_cast<int>(header->payload_size));
+
+ // We actually discard the flags and only copy the ones we care about. This
+ // is just because message doesn't have a constructor that takes raw flags.
+ scoped_ptr<IPC::Message> msg(
+ new IPC::Message(header->routing, header->type,
+ IPC::Message::PRIORITY_NORMAL));
+ if (header->flags & IPC::Message::SYNC_BIT)
+ msg->set_sync();
+ if (header->flags & IPC::Message::REPLY_BIT)
+ msg->set_reply();
+ if (header->flags & IPC::Message::REPLY_ERROR_BIT)
+ msg->set_reply_error();
+ if (header->flags & IPC::Message::UNBLOCK_BIT)
+ msg->set_unblock(true);
+
+ msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
+
+ // Technically we didn't have to do any of the previous work in the lock. But
+ // sometimes out buffer will point to the to_be_sent_ string which is
bbudge 2012/03/26 22:52:30 s/out/our
+ // protected by the lock, and it's messier to factor Send() such that it can
+ // unlock for us. Holding the lock for the message construction, which is
+ // just some memcpys, shouldn't be a big deal.
+ lock_.AssertAcquired();
+ if (locked_data_.channel_closed_)
+ return false; // TODO(brettw) clean up handles here when we add support!
+
+ // Actual send must be done on the I/O thread.
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
+ base::Passed(&msg)));
+ return true;
+}
+
+void NaClIPCAdapter::CloseChannelOnIOThread() {
+ io_thread_data_.channel_->Close();
+}
+
+void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
+ io_thread_data_.channel_->Send(message.release());
+}
+
+void NaClIPCAdapter::ClearToBeSent() {
+ lock_.AssertAcquired();
+
+ // Don't let the string keep its buffer behind our back.
+ std::string empty;
+ locked_data_.to_be_sent_.swap(empty);
+}
« no previous file with comments | « chrome/nacl/nacl_ipc_adapter.h ('k') | chrome/nacl/nacl_ipc_adapter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698