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

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