1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
|
// 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 {
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.
MESSAGE_HAS_EXTRA_DATA
};
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 MESSAGE_HAS_EXTRA_DATA;
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:
friend class base::RefCounted<RewrittenMessage>;
~RewrittenMessage() {}
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::LockedData::~LockedData() {
}
NaClIPCAdapter::IOThreadData::IOThreadData() {
}
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();
}
// 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;
}
// current_message[_len] refers to the total input data received so far.
const char* current_message;
size_t current_message_len;
bool did_append_input_data;
if (locked_data_.to_be_sent_.empty()) {
// No accumulated data, we can avoid a copy by referring to the input
// buffer (the entire message fitting in one call is the common case).
current_message = input_data;
current_message_len = input_data_len;
did_append_input_data = false;
} else {
// We've already accumulated some data, accumulate this new data and
// point to the beginning of the buffer.
// 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();
did_append_input_data = true;
}
// Check the total data we've accumulated so far to see if it contains a full
// message.
switch (GetBufferStatus(current_message, current_message_len)) {
case MESSAGE_IS_COMPLETE: {
// Got a complete message, can send it out. This will be the common case.
bool success = SendCompleteMessage(current_message, current_message_len);
ClearToBeSent();
return success ? static_cast<int>(input_data_len) : -1;
}
case MESSAGE_IS_TRUNCATED:
// For truncated messages, just accumulate the new data (if we didn't
// already do so above) and go back to waiting for more.
if (!did_append_input_data)
locked_data_.to_be_sent_.append(input_data, input_data_len);
return static_cast<int>(input_data_len);
case MESSAGE_HAS_EXTRA_DATA:
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 data to the untrusted plugin, so zero everything out first.
NaClMessageHeader header;
memset(&header, 0, sizeof(NaClMessageHeader));
header.payload_size = static_cast<uint32>(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();
}
NaClIPCAdapter::~NaClIPCAdapter() {
}
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 = static_cast<int>(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 our buffer will point to the to_be_sent_ string which is
// 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::ClearToBeSent() {
lock_.AssertAcquired();
// Don't let the string keep its buffer behind our back.
std::string empty;
locked_data_.to_be_sent_.swap(empty);
}
void NaClIPCAdapter::CloseChannelOnIOThread() {
io_thread_data_.channel_->Close();
}
void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
io_thread_data_.channel_->Send(message.release());
}
|