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

Side by Side 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, 8 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 unified diff | Download patch | Annotate | Revision Log
« 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "chrome/nacl/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 "build/build_config.h"
15
16 namespace {
17
18 enum BufferSizeStatus {
19 // The buffer contains a full message with no extra bytes.
20 MESSAGE_IS_COMPLETE,
21
22 // The message doesn't fit and the buffer contains only some of it.
23 MESSAGE_IS_TRUNCATED,
24
25 // The buffer contains a full message + extra data.
26 MESSAGE_HAS_EXTRA_DATA
27 };
28
29 BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
30 if (len < sizeof(NaClIPCAdapter::NaClMessageHeader))
31 return MESSAGE_IS_TRUNCATED;
32
33 const NaClIPCAdapter::NaClMessageHeader* header =
34 reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(data);
35 uint32 message_size =
36 sizeof(NaClIPCAdapter::NaClMessageHeader) + header->payload_size;
37
38 if (len == message_size)
39 return MESSAGE_IS_COMPLETE;
40 if (len > message_size)
41 return MESSAGE_HAS_EXTRA_DATA;
42 return MESSAGE_IS_TRUNCATED;
43 }
44
45 } // namespace
46
47 class NaClIPCAdapter::RewrittenMessage
48 : public base::RefCounted<RewrittenMessage> {
49 public:
50 RewrittenMessage();
51
52 bool is_consumed() const { return data_read_cursor_ == data_len_; }
53
54 void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
55 const void* payload, size_t payload_length);
56
57 int Read(char* dest_buffer, int dest_buffer_size);
58
59 private:
60 scoped_array<char> data_;
61 int data_len_;
62
63 // Offset into data where the next read will happen. This will be equal to
64 // data_len_ when all data has been consumed.
65 int data_read_cursor_;
66 };
67
68 NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
69 : data_len_(0),
70 data_read_cursor_(0) {
71 }
72
73 void NaClIPCAdapter::RewrittenMessage::SetData(
74 const NaClIPCAdapter::NaClMessageHeader& header,
75 const void* payload,
76 size_t payload_length) {
77 DCHECK(!data_.get() && data_len_ == 0);
78 int header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
79 data_len_ = header_len + static_cast<int>(payload_length);
80 data_.reset(new char[data_len_]);
81
82 memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
83 memcpy(&data_[header_len], payload, payload_length);
84 }
85
86 int NaClIPCAdapter::RewrittenMessage::Read(char* dest_buffer,
87 int dest_buffer_size) {
88 int bytes_to_write = std::min(dest_buffer_size,
89 data_len_ - data_read_cursor_);
90 if (bytes_to_write == 0)
91 return 0;
92
93 memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
94 data_read_cursor_ += bytes_to_write;
95 return bytes_to_write;
96 }
97
98 NaClIPCAdapter::LockedData::LockedData() : channel_closed_(false) {
99 }
100
101 NaClIPCAdapter::LockedData::~LockedData() {
102 }
103
104 NaClIPCAdapter::IOThreadData::IOThreadData() {
105 }
106
107 NaClIPCAdapter::IOThreadData::~IOThreadData() {
108 }
109
110 NaClIPCAdapter::NaClIPCAdapter(const IPC::ChannelHandle& handle,
111 base::TaskRunner* runner)
112 : lock_(),
113 cond_var_(&lock_),
114 task_runner_(runner),
115 locked_data_() {
116 io_thread_data_.channel_.reset(
117 new IPC::Channel(handle, IPC::Channel::MODE_SERVER, this));
118 }
119
120 NaClIPCAdapter::NaClIPCAdapter(scoped_ptr<IPC::Channel> channel,
121 base::TaskRunner* runner)
122 : lock_(),
123 cond_var_(&lock_),
124 task_runner_(runner),
125 locked_data_() {
126 io_thread_data_.channel_ = channel.Pass();
127 }
128
129 NaClIPCAdapter::~NaClIPCAdapter() {
130 }
131
132 // Note that this message is controlled by the untrusted code. So we should be
133 // skeptical of anything it contains and quick to give up if anything is fishy.
134 int NaClIPCAdapter::Send(const char* input_data, size_t input_data_len) {
135 base::AutoLock lock(lock_);
136
137 if (input_data_len > IPC::Channel::kMaximumMessageSize) {
138 ClearToBeSent();
139 return -1;
140 }
141
142 // current_message[_len] refers to the total input data received so far.
143 const char* current_message;
144 size_t current_message_len;
145 bool did_append_input_data;
146 if (locked_data_.to_be_sent_.empty()) {
147 // No accumulated data, we can avoid a copy by referring to the input
148 // buffer (the entire message fitting in one call is the common case).
149 current_message = input_data;
150 current_message_len = input_data_len;
151 did_append_input_data = false;
152 } else {
153 // We've already accumulated some data, accumulate this new data and
154 // point to the beginning of the buffer.
155
156 // Make sure our accumulated message size doesn't overflow our max. Since
157 // we know that data_len < max size (checked above) and our current
158 // accumulated value is also < max size, we just need to make sure that
159 // 2x max size can never overflow.
160 COMPILE_ASSERT(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
161 MaximumMessageSizeWillOverflow);
162 size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
163 if (new_size > IPC::Channel::kMaximumMessageSize) {
164 ClearToBeSent();
165 return -1;
166 }
167
168 locked_data_.to_be_sent_.append(input_data, input_data_len);
169 current_message = &locked_data_.to_be_sent_[0];
170 current_message_len = locked_data_.to_be_sent_.size();
171 did_append_input_data = true;
172 }
173
174 // Check the total data we've accumulated so far to see if it contains a full
175 // message.
176 switch (GetBufferStatus(current_message, current_message_len)) {
177 case MESSAGE_IS_COMPLETE: {
178 // Got a complete message, can send it out. This will be the common case.
179 bool success = SendCompleteMessage(current_message, current_message_len);
180 ClearToBeSent();
181 return success ? static_cast<int>(input_data_len) : -1;
182 }
183 case MESSAGE_IS_TRUNCATED:
184 // For truncated messages, just accumulate the new data (if we didn't
185 // already do so above) and go back to waiting for more.
186 if (!did_append_input_data)
187 locked_data_.to_be_sent_.append(input_data, input_data_len);
188 return static_cast<int>(input_data_len);
189 case MESSAGE_HAS_EXTRA_DATA:
190 default:
191 // When the plugin gives us too much data, it's an error.
192 ClearToBeSent();
193 return -1;
194 }
195 }
196
197 int NaClIPCAdapter::BlockingReceive(char* output_buffer,
198 int output_buffer_size) {
199 int retval = 0;
200 {
201 base::AutoLock lock(lock_);
202 while (locked_data_.to_be_received_.empty() &&
203 !locked_data_.channel_closed_)
204 cond_var_.Wait();
205 if (locked_data_.channel_closed_) {
206 retval = -1;
207 } else {
208 retval = LockedReceive(output_buffer, output_buffer_size);
209 DCHECK(retval > 0);
210 }
211 }
212 cond_var_.Signal();
213 return retval;
214 }
215
216 void NaClIPCAdapter::CloseChannel() {
217 {
218 base::AutoLock lock(lock_);
219 locked_data_.channel_closed_ = true;
220 }
221 cond_var_.Signal();
222
223 task_runner_->PostTask(FROM_HERE,
224 base::Bind(&NaClIPCAdapter::CloseChannelOnIOThread, this));
225 }
226
227 bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& message) {
228 {
229 base::AutoLock lock(lock_);
230
231 // There is some padding in this structure (the "padding" member is 16
232 // bits but this then gets padded to 32 bits). We want to be sure not to
233 // leak data to the untrusted plugin, so zero everything out first.
234 NaClMessageHeader header;
235 memset(&header, 0, sizeof(NaClMessageHeader));
236
237 header.payload_size = static_cast<uint32>(message.payload_size());
238 header.routing = message.routing_id();
239 header.type = message.type();
240 header.flags = message.flags();
241 header.num_fds = 0; // TODO(brettw) hook this up.
242
243 scoped_refptr<RewrittenMessage> dest(new RewrittenMessage);
244 dest->SetData(header, message.payload(), message.payload_size());
245 locked_data_.to_be_received_.push(dest);
246 }
247 cond_var_.Signal();
248 return true;
249 }
250
251 void NaClIPCAdapter::OnChannelConnected(int32 peer_pid) {
252 }
253
254 void NaClIPCAdapter::OnChannelError() {
255 CloseChannel();
256 }
257
258 int NaClIPCAdapter::LockedReceive(char* output_buffer, int output_buffer_size) {
259 lock_.AssertAcquired();
260
261 if (locked_data_.to_be_received_.empty())
262 return 0;
263 scoped_refptr<RewrittenMessage> current =
264 locked_data_.to_be_received_.front();
265
266 int retval = current->Read(output_buffer, output_buffer_size);
267
268 // When a message is entirely consumed, remove if from the waiting queue.
269 if (current->is_consumed())
270 locked_data_.to_be_received_.pop();
271 return retval;
272 }
273
274 bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
275 size_t buffer_len) {
276 // The message will have already been validated, so we know it's large enough
277 // for our header.
278 const NaClMessageHeader* header =
279 reinterpret_cast<const NaClMessageHeader*>(buffer);
280
281 // Length of the message not including the body. The data passed to us by the
282 // plugin should match that in the message header. This should have already
283 // been validated by GetBufferStatus.
284 int body_len = static_cast<int>(buffer_len - sizeof(NaClMessageHeader));
285 DCHECK(body_len == static_cast<int>(header->payload_size));
286
287 // We actually discard the flags and only copy the ones we care about. This
288 // is just because message doesn't have a constructor that takes raw flags.
289 scoped_ptr<IPC::Message> msg(
290 new IPC::Message(header->routing, header->type,
291 IPC::Message::PRIORITY_NORMAL));
292 if (header->flags & IPC::Message::SYNC_BIT)
293 msg->set_sync();
294 if (header->flags & IPC::Message::REPLY_BIT)
295 msg->set_reply();
296 if (header->flags & IPC::Message::REPLY_ERROR_BIT)
297 msg->set_reply_error();
298 if (header->flags & IPC::Message::UNBLOCK_BIT)
299 msg->set_unblock(true);
300
301 msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
302
303 // Technically we didn't have to do any of the previous work in the lock. But
304 // sometimes our buffer will point to the to_be_sent_ string which is
305 // protected by the lock, and it's messier to factor Send() such that it can
306 // unlock for us. Holding the lock for the message construction, which is
307 // just some memcpys, shouldn't be a big deal.
308 lock_.AssertAcquired();
309 if (locked_data_.channel_closed_)
310 return false; // TODO(brettw) clean up handles here when we add support!
311
312 // Actual send must be done on the I/O thread.
313 task_runner_->PostTask(FROM_HERE,
314 base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
315 base::Passed(&msg)));
316 return true;
317 }
318
319 void NaClIPCAdapter::CloseChannelOnIOThread() {
320 io_thread_data_.channel_->Close();
321 }
322
323 void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
324 io_thread_data_.channel_->Send(message.release());
325 }
326
327 void NaClIPCAdapter::ClearToBeSent() {
328 lock_.AssertAcquired();
329
330 // Don't let the string keep its buffer behind our back.
331 std::string empty;
332 locked_data_.to_be_sent_.swap(empty);
333 }
OLDNEW
« 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