1 // Copyright 2016 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 "mojo/core/channel.h"
6
7 #include <stddef.h>
8 #include <string.h>
9
10 #include <algorithm>
11 #include <limits>
12 #include <utility>
13
14 #include "base/macros.h"
15 #include "base/memory/aligned_memory.h"
16 #include "base/numerics/safe_math.h"
17 #include "base/process/process_handle.h"
18 #include "build/build_config.h"
19 #include "mojo/core/configuration.h"
20 #include "mojo/core/core.h"
21
22 #if defined(OS_MACOSX) && !defined(OS_IOS)
23 #include "base/mac/mach_logging.h"
24 #elif defined(OS_WIN)
25 #include "base/win/win_util.h"
26 #endif
27
28 namespace mojo {
29 namespace core {
30
31 namespace {
32
33 static_assert(
34 IsAlignedForChannelMessage(sizeof(Channel::Message::LegacyHeader)),
35 "Invalid LegacyHeader size.");
36
37 static_assert(IsAlignedForChannelMessage(sizeof(Channel::Message::Header)),
38 "Invalid Header size.");
39
40 static_assert(sizeof(Channel::Message::LegacyHeader) == 8,
41 "LegacyHeader must be 8 bytes on ChromeOS and Android");
42
43 static_assert(offsetof(Channel::Message::LegacyHeader, num_bytes) ==
44 offsetof(Channel::Message::Header, num_bytes),
45 "num_bytes should be at the same offset in both Header structs.");
46 static_assert(offsetof(Channel::Message::LegacyHeader, message_type) ==
47 offsetof(Channel::Message::Header, message_type),
48 "message_type should be at the same offset in both Header "
49 "structs.");
50
51 } // namespace
52
53 const size_t kReadBufferSize = 4096;
54 const size_t kMaxUnusedReadBufferCapacity = 4096;
55
56 // TODO(rockot): Increase this if/when Channel implementations support more.
57 // Linux: The platform imposes a limit of 253 handles per sendmsg().
58 // Fuchsia: The zx_channel_write() API supports up to 64 handles.
59 const size_t kMaxAttachedHandles = 64;
60
Message(size_t payload_size,size_t max_handles)61 Channel::Message::Message(size_t payload_size, size_t max_handles)
62 : Message(payload_size, payload_size, max_handles) {}
63
Message(size_t payload_size,size_t max_handles,MessageType message_type)64 Channel::Message::Message(size_t payload_size,
65 size_t max_handles,
66 MessageType message_type)
67 : Message(payload_size, payload_size, max_handles, message_type) {}
68
Message(size_t capacity,size_t payload_size,size_t max_handles)69 Channel::Message::Message(size_t capacity,
70 size_t payload_size,
71 size_t max_handles)
72 #if defined(MOJO_CORE_LEGACY_PROTOCOL)
73 : Message(capacity, payload_size, max_handles, MessageType::NORMAL_LEGACY) {
74 }
75 #else
76 : Message(capacity, payload_size, max_handles, MessageType::NORMAL) {
77 }
78 #endif
79
Message(size_t capacity,size_t payload_size,size_t max_handles,MessageType message_type)80 Channel::Message::Message(size_t capacity,
81 size_t payload_size,
82 size_t max_handles,
83 MessageType message_type)
84 : max_handles_(max_handles) {
85 DCHECK_GE(capacity, payload_size);
86 DCHECK_LE(max_handles_, kMaxAttachedHandles);
87
88 const bool is_legacy_message = (message_type == MessageType::NORMAL_LEGACY);
89 size_t extra_header_size = 0;
90 #if defined(OS_WIN)
91 // On Windows we serialize HANDLEs into the extra header space.
92 extra_header_size = max_handles_ * sizeof(HandleEntry);
93 #elif defined(OS_FUCHSIA)
94 // On Fuchsia we serialize handle types into the extra header space.
95 extra_header_size = max_handles_ * sizeof(HandleInfoEntry);
96 #elif defined(OS_MACOSX) && !defined(OS_IOS)
97 // On OSX, some of the platform handles may be mach ports, which are
98 // serialised into the message buffer. Since there could be a mix of fds and
99 // mach ports, we store the mach ports as an <index, port> pair (of uint32_t),
100 // so that the original ordering of handles can be re-created.
101 if (max_handles) {
102 extra_header_size =
103 sizeof(MachPortsExtraHeader) + (max_handles * sizeof(MachPortsEntry));
104 }
105 #endif
106 // Pad extra header data to be aliged to |kChannelMessageAlignment| bytes.
107 if (!IsAlignedForChannelMessage(extra_header_size)) {
108 extra_header_size += kChannelMessageAlignment -
109 (extra_header_size % kChannelMessageAlignment);
110 }
111 DCHECK(IsAlignedForChannelMessage(extra_header_size));
112 const size_t header_size =
113 is_legacy_message ? sizeof(LegacyHeader) : sizeof(Header);
114 DCHECK(extra_header_size == 0 || !is_legacy_message);
115
116 capacity_ = header_size + extra_header_size + capacity;
117 size_ = header_size + extra_header_size + payload_size;
118 data_ = static_cast<char*>(
119 base::AlignedAlloc(capacity_, kChannelMessageAlignment));
120 // Only zero out the header and not the payload. Since the payload is going to
121 // be memcpy'd, zeroing the payload is unnecessary work and a significant
122 // performance issue when dealing with large messages. Any sanitizer errors
123 // complaining about an uninitialized read in the payload area should be
124 // treated as an error and fixed.
125 memset(data_, 0, header_size + extra_header_size);
126
127 DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size_));
128 legacy_header()->num_bytes = static_cast<uint32_t>(size_);
129
130 DCHECK(base::IsValueInRangeForNumericType<uint16_t>(header_size +
131 extra_header_size));
132 legacy_header()->message_type = message_type;
133
134 if (is_legacy_message) {
135 legacy_header()->num_handles = static_cast<uint16_t>(max_handles);
136 } else {
137 header()->num_header_bytes =
138 static_cast<uint16_t>(header_size + extra_header_size);
139 }
140
141 if (max_handles_ > 0) {
142 #if defined(OS_WIN)
143 handles_ = reinterpret_cast<HandleEntry*>(mutable_extra_header());
144 // Initialize all handles to invalid values.
145 for (size_t i = 0; i < max_handles_; ++i)
146 handles_[i].handle = base::win::HandleToUint32(INVALID_HANDLE_VALUE);
147 #elif defined(OS_MACOSX) && !defined(OS_IOS)
148 mach_ports_header_ =
149 reinterpret_cast<MachPortsExtraHeader*>(mutable_extra_header());
150 mach_ports_header_->num_ports = 0;
151 // Initialize all handles to invalid values.
152 for (size_t i = 0; i < max_handles_; ++i) {
153 mach_ports_header_->entries[i] = {0,
154 static_cast<uint32_t>(MACH_PORT_NULL)};
155 }
156 #endif
157 }
158 }
159
~Message()160 Channel::Message::~Message() {
161 base::AlignedFree(data_);
162 }
163
164 // static
Deserialize(const void * data,size_t data_num_bytes,base::ProcessHandle from_process)165 Channel::MessagePtr Channel::Message::Deserialize(
166 const void* data,
167 size_t data_num_bytes,
168 base::ProcessHandle from_process) {
169 if (data_num_bytes < sizeof(LegacyHeader))
170 return nullptr;
171
172 const LegacyHeader* legacy_header =
173 reinterpret_cast<const LegacyHeader*>(data);
174 if (legacy_header->num_bytes != data_num_bytes) {
175 DLOG(ERROR) << "Decoding invalid message: " << legacy_header->num_bytes
176 << " != " << data_num_bytes;
177 return nullptr;
178 }
179
180 const Header* header = nullptr;
181 if (legacy_header->message_type == MessageType::NORMAL)
182 header = reinterpret_cast<const Header*>(data);
183
184 uint32_t extra_header_size = 0;
185 size_t payload_size = 0;
186 const char* payload = nullptr;
187 if (!header) {
188 payload_size = data_num_bytes - sizeof(LegacyHeader);
189 payload = static_cast<const char*>(data) + sizeof(LegacyHeader);
190 } else {
191 if (header->num_bytes < header->num_header_bytes ||
192 header->num_header_bytes < sizeof(Header)) {
193 DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes << " < "
194 << header->num_header_bytes;
195 return nullptr;
196 }
197 extra_header_size = header->num_header_bytes - sizeof(Header);
198 payload_size = data_num_bytes - header->num_header_bytes;
199 payload = static_cast<const char*>(data) + header->num_header_bytes;
200 }
201
202 #if defined(OS_WIN)
203 uint32_t max_handles = extra_header_size / sizeof(HandleEntry);
204 #elif defined(OS_FUCHSIA)
205 uint32_t max_handles = extra_header_size / sizeof(HandleInfoEntry);
206 #elif defined(OS_MACOSX) && !defined(OS_IOS)
207 if (extra_header_size > 0 &&
208 extra_header_size < sizeof(MachPortsExtraHeader)) {
209 DLOG(ERROR) << "Decoding invalid message: " << extra_header_size << " < "
210 << sizeof(MachPortsExtraHeader);
211 return nullptr;
212 }
213 uint32_t max_handles =
214 extra_header_size == 0
215 ? 0
216 : (extra_header_size - sizeof(MachPortsExtraHeader)) /
217 sizeof(MachPortsEntry);
218 #else
219 const uint32_t max_handles = 0;
220 #endif // defined(OS_WIN)
221
222 const uint16_t num_handles =
223 header ? header->num_handles : legacy_header->num_handles;
224 if (num_handles > max_handles || max_handles > kMaxAttachedHandles) {
225 DLOG(ERROR) << "Decoding invalid message: " << num_handles << " > "
226 << max_handles;
227 return nullptr;
228 }
229
230 MessagePtr message(
231 new Message(payload_size, max_handles, legacy_header->message_type));
232 DCHECK_EQ(message->data_num_bytes(), data_num_bytes);
233
234 // Copy all payload bytes.
235 if (payload_size)
236 memcpy(message->mutable_payload(), payload, payload_size);
237
238 if (header) {
239 DCHECK_EQ(message->extra_header_size(), extra_header_size);
240 DCHECK_EQ(message->header()->num_header_bytes, header->num_header_bytes);
241
242 if (message->extra_header_size()) {
243 // Copy extra header bytes.
244 memcpy(message->mutable_extra_header(),
245 static_cast<const char*>(data) + sizeof(Header),
246 message->extra_header_size());
247 }
248 message->header()->num_handles = header->num_handles;
249 } else {
250 message->legacy_header()->num_handles = legacy_header->num_handles;
251 }
252
253 #if defined(OS_WIN)
254 std::vector<PlatformHandleInTransit> handles(num_handles);
255 for (size_t i = 0; i < num_handles; i++) {
256 HANDLE handle = base::win::Uint32ToHandle(message->handles_[i].handle);
257 if (from_process == base::kNullProcessHandle) {
258 handles[i] = PlatformHandleInTransit(
259 PlatformHandle(base::win::ScopedHandle(handle)));
260 } else {
261 handles[i] = PlatformHandleInTransit(
262 PlatformHandleInTransit::TakeIncomingRemoteHandle(handle,
263 from_process));
264 }
265 }
266 message->SetHandles(std::move(handles));
267 #endif
268
269 return message;
270 }
271
capacity() const272 size_t Channel::Message::capacity() const {
273 if (is_legacy_message())
274 return capacity_ - sizeof(LegacyHeader);
275 return capacity_ - header()->num_header_bytes;
276 }
277
ExtendPayload(size_t new_payload_size)278 void Channel::Message::ExtendPayload(size_t new_payload_size) {
279 size_t capacity_without_header = capacity();
280 size_t header_size = capacity_ - capacity_without_header;
281 if (new_payload_size > capacity_without_header) {
282 size_t new_capacity =
283 std::max(capacity_without_header * 2, new_payload_size) + header_size;
284 void* new_data = base::AlignedAlloc(new_capacity, kChannelMessageAlignment);
285 memcpy(new_data, data_, capacity_);
286 base::AlignedFree(data_);
287 data_ = static_cast<char*>(new_data);
288 capacity_ = new_capacity;
289
290 if (max_handles_ > 0) {
291 // We also need to update the cached extra header addresses in case the
292 // payload buffer has been relocated.
293 #if defined(OS_WIN)
294 handles_ = reinterpret_cast<HandleEntry*>(mutable_extra_header());
295 #elif defined(OS_MACOSX) && !defined(OS_IOS)
296 mach_ports_header_ =
297 reinterpret_cast<MachPortsExtraHeader*>(mutable_extra_header());
298 #endif
299 }
300 }
301 size_ = header_size + new_payload_size;
302 DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size_));
303 legacy_header()->num_bytes = static_cast<uint32_t>(size_);
304 }
305
extra_header() const306 const void* Channel::Message::extra_header() const {
307 DCHECK(!is_legacy_message());
308 return data_ + sizeof(Header);
309 }
310
mutable_extra_header()311 void* Channel::Message::mutable_extra_header() {
312 DCHECK(!is_legacy_message());
313 return data_ + sizeof(Header);
314 }
315
extra_header_size() const316 size_t Channel::Message::extra_header_size() const {
317 return header()->num_header_bytes - sizeof(Header);
318 }
319
mutable_payload()320 void* Channel::Message::mutable_payload() {
321 if (is_legacy_message())
322 return static_cast<void*>(legacy_header() + 1);
323 return data_ + header()->num_header_bytes;
324 }
325
payload() const326 const void* Channel::Message::payload() const {
327 if (is_legacy_message())
328 return static_cast<const void*>(legacy_header() + 1);
329 return data_ + header()->num_header_bytes;
330 }
331
payload_size() const332 size_t Channel::Message::payload_size() const {
333 if (is_legacy_message())
334 return legacy_header()->num_bytes - sizeof(LegacyHeader);
335 return size_ - header()->num_header_bytes;
336 }
337
num_handles() const338 size_t Channel::Message::num_handles() const {
339 return is_legacy_message() ? legacy_header()->num_handles
340 : header()->num_handles;
341 }
342
has_handles() const343 bool Channel::Message::has_handles() const {
344 return (is_legacy_message() ? legacy_header()->num_handles
345 : header()->num_handles) > 0;
346 }
347
348 #if defined(OS_MACOSX) && !defined(OS_IOS)
has_mach_ports() const349 bool Channel::Message::has_mach_ports() const {
350 if (!has_handles())
351 return false;
352
353 for (const auto& handle : handle_vector_) {
354 if (handle.is_mach_port_name() || handle.handle().is_mach_port())
355 return true;
356 }
357 return false;
358 }
359 #endif
360
is_legacy_message() const361 bool Channel::Message::is_legacy_message() const {
362 return legacy_header()->message_type == MessageType::NORMAL_LEGACY;
363 }
364
legacy_header() const365 Channel::Message::LegacyHeader* Channel::Message::legacy_header() const {
366 return reinterpret_cast<LegacyHeader*>(data_);
367 }
368
header() const369 Channel::Message::Header* Channel::Message::header() const {
370 DCHECK(!is_legacy_message());
371 return reinterpret_cast<Header*>(data_);
372 }
373
SetHandles(std::vector<PlatformHandle> new_handles)374 void Channel::Message::SetHandles(std::vector<PlatformHandle> new_handles) {
375 std::vector<PlatformHandleInTransit> handles;
376 handles.reserve(new_handles.size());
377 for (auto& h : new_handles) {
378 handles.emplace_back(PlatformHandleInTransit(std::move(h)));
379 }
380 SetHandles(std::move(handles));
381 }
382
SetHandles(std::vector<PlatformHandleInTransit> new_handles)383 void Channel::Message::SetHandles(
384 std::vector<PlatformHandleInTransit> new_handles) {
385 if (is_legacy_message()) {
386 // Old semantics for ChromeOS and Android
387 if (legacy_header()->num_handles == 0) {
388 CHECK(new_handles.empty());
389 return;
390 }
391 CHECK_EQ(new_handles.size(), legacy_header()->num_handles);
392 std::swap(handle_vector_, new_handles);
393 return;
394 }
395
396 if (max_handles_ == 0) {
397 CHECK(new_handles.empty());
398 return;
399 }
400
401 CHECK_LE(new_handles.size(), max_handles_);
402 header()->num_handles = static_cast<uint16_t>(new_handles.size());
403 std::swap(handle_vector_, new_handles);
404 #if defined(OS_WIN)
405 memset(handles_, 0, extra_header_size());
406 for (size_t i = 0; i < handle_vector_.size(); i++) {
407 HANDLE handle = handle_vector_[i].remote_handle();
408 if (handle == INVALID_HANDLE_VALUE)
409 handle = handle_vector_[i].handle().GetHandle().Get();
410 handles_[i].handle = base::win::HandleToUint32(handle);
411 }
412 #endif // defined(OS_WIN)
413
414 #if defined(OS_MACOSX) && !defined(OS_IOS)
415 size_t mach_port_index = 0;
416 if (mach_ports_header_) {
417 for (size_t i = 0; i < max_handles_; ++i) {
418 mach_ports_header_->entries[i] = {0,
419 static_cast<uint32_t>(MACH_PORT_NULL)};
420 }
421 for (size_t i = 0; i < handle_vector_.size(); i++) {
422 if (!handle_vector_[i].is_mach_port_name() &&
423 !handle_vector_[i].handle().is_mach_port()) {
424 DCHECK(handle_vector_[i].handle().is_valid_fd());
425 continue;
426 }
427
428 mach_port_t port = handle_vector_[i].is_mach_port_name()
429 ? handle_vector_[i].mach_port_name()
430 : handle_vector_[i].handle().GetMachPort().get();
431 mach_ports_header_->entries[mach_port_index].index = i;
432 mach_ports_header_->entries[mach_port_index].mach_port = port;
433 mach_port_index++;
434 }
435 mach_ports_header_->num_ports = static_cast<uint16_t>(mach_port_index);
436 }
437 #endif
438 }
439
TakeHandles()440 std::vector<PlatformHandleInTransit> Channel::Message::TakeHandles() {
441 #if defined(OS_MACOSX) && !defined(OS_IOS)
442 if (mach_ports_header_) {
443 for (size_t i = 0; i < max_handles_; ++i) {
444 mach_ports_header_->entries[i] = {0,
445 static_cast<uint32_t>(MACH_PORT_NULL)};
446 }
447 mach_ports_header_->num_ports = 0;
448 }
449 #endif
450 if (is_legacy_message())
451 legacy_header()->num_handles = 0;
452 else
453 header()->num_handles = 0;
454 return std::move(handle_vector_);
455 }
456
457 std::vector<PlatformHandleInTransit>
TakeHandlesForTransport()458 Channel::Message::TakeHandlesForTransport() {
459 #if defined(OS_WIN)
460 // Not necessary on Windows.
461 NOTREACHED();
462 return std::vector<PlatformHandleInTransit>();
463 #elif defined(OS_MACOSX) && !defined(OS_IOS)
464 std::vector<PlatformHandleInTransit> non_mach_handles;
465 for (auto& handle : handle_vector_) {
466 if (handle.is_mach_port_name() || handle.handle().is_mach_port()) {
467 // Ownership is effectively transferred to the receiving process
468 // out-of-band via MachPortRelay.
469 handle.CompleteTransit();
470 } else {
471 non_mach_handles.emplace_back(std::move(handle));
472 }
473 }
474 handle_vector_.clear();
475 return non_mach_handles;
476 #else
477 return std::move(handle_vector_);
478 #endif
479 }
480
481 // Helper class for managing a Channel's read buffer allocations. This maintains
482 // a single contiguous buffer with the layout:
483 //
484 // [discarded bytes][occupied bytes][unoccupied bytes]
485 //
486 // The Reserve() method ensures that a certain capacity of unoccupied bytes are
487 // available. It does not claim that capacity and only allocates new capacity
488 // when strictly necessary.
489 //
490 // Claim() marks unoccupied bytes as occupied.
491 //
492 // Discard() marks occupied bytes as discarded, signifying that their contents
493 // can be forgotten or overwritten.
494 //
495 // Realign() moves occupied bytes to the front of the buffer so that those
496 // occupied bytes are properly aligned.
497 //
498 // The most common Channel behavior in practice should result in very few
499 // allocations and copies, as memory is claimed and discarded shortly after
500 // being reserved, and future reservations will immediately reuse discarded
501 // memory.
502 class Channel::ReadBuffer {
503 public:
ReadBuffer()504 ReadBuffer() {
505 size_ = kReadBufferSize;
506 data_ =
507 static_cast<char*>(base::AlignedAlloc(size_, kChannelMessageAlignment));
508 }
509
~ReadBuffer()510 ~ReadBuffer() {
511 DCHECK(data_);
512 base::AlignedFree(data_);
513 }
514
occupied_bytes() const515 const char* occupied_bytes() const { return data_ + num_discarded_bytes_; }
516
num_occupied_bytes() const517 size_t num_occupied_bytes() const {
518 return num_occupied_bytes_ - num_discarded_bytes_;
519 }
520
521 // Ensures the ReadBuffer has enough contiguous space allocated to hold
522 // |num_bytes| more bytes; returns the address of the first available byte.
Reserve(size_t num_bytes)523 char* Reserve(size_t num_bytes) {
524 if (num_occupied_bytes_ + num_bytes > size_) {
525 size_ = std::max(size_ * 2, num_occupied_bytes_ + num_bytes);
526 void* new_data = base::AlignedAlloc(size_, kChannelMessageAlignment);
527 memcpy(new_data, data_, num_occupied_bytes_);
528 base::AlignedFree(data_);
529 data_ = static_cast<char*>(new_data);
530 }
531
532 return data_ + num_occupied_bytes_;
533 }
534
535 // Marks the first |num_bytes| unoccupied bytes as occupied.
Claim(size_t num_bytes)536 void Claim(size_t num_bytes) {
537 DCHECK_LE(num_occupied_bytes_ + num_bytes, size_);
538 num_occupied_bytes_ += num_bytes;
539 }
540
541 // Marks the first |num_bytes| occupied bytes as discarded. This may result in
542 // shrinkage of the internal buffer, and it is not safe to assume the result
543 // of a previous Reserve() call is still valid after this.
Discard(size_t num_bytes)544 void Discard(size_t num_bytes) {
545 DCHECK_LE(num_discarded_bytes_ + num_bytes, num_occupied_bytes_);
546 num_discarded_bytes_ += num_bytes;
547
548 if (num_discarded_bytes_ == num_occupied_bytes_) {
549 // We can just reuse the buffer from the beginning in this common case.
550 num_discarded_bytes_ = 0;
551 num_occupied_bytes_ = 0;
552 }
553
554 if (num_discarded_bytes_ > kMaxUnusedReadBufferCapacity) {
555 // In the uncommon case that we have a lot of discarded data at the
556 // front of the buffer, simply move remaining data to a smaller buffer.
557 size_t num_preserved_bytes = num_occupied_bytes_ - num_discarded_bytes_;
558 size_ = std::max(num_preserved_bytes, kReadBufferSize);
559 char* new_data = static_cast<char*>(
560 base::AlignedAlloc(size_, kChannelMessageAlignment));
561 memcpy(new_data, data_ + num_discarded_bytes_, num_preserved_bytes);
562 base::AlignedFree(data_);
563 data_ = new_data;
564 num_discarded_bytes_ = 0;
565 num_occupied_bytes_ = num_preserved_bytes;
566 }
567
568 if (num_occupied_bytes_ == 0 && size_ > kMaxUnusedReadBufferCapacity) {
569 // Opportunistically shrink the read buffer back down to a small size if
570 // it's grown very large. We only do this if there are no remaining
571 // unconsumed bytes in the buffer to avoid copies in most the common
572 // cases.
573 size_ = kMaxUnusedReadBufferCapacity;
574 base::AlignedFree(data_);
575 data_ = static_cast<char*>(
576 base::AlignedAlloc(size_, kChannelMessageAlignment));
577 }
578 }
579
Realign()580 void Realign() {
581 size_t num_bytes = num_occupied_bytes();
582 memmove(data_, occupied_bytes(), num_bytes);
583 num_discarded_bytes_ = 0;
584 num_occupied_bytes_ = num_bytes;
585 }
586
587 private:
588 char* data_ = nullptr;
589
590 // The total size of the allocated buffer.
591 size_t size_ = 0;
592
593 // The number of discarded bytes at the beginning of the allocated buffer.
594 size_t num_discarded_bytes_ = 0;
595
596 // The total number of occupied bytes, including discarded bytes.
597 size_t num_occupied_bytes_ = 0;
598
599 DISALLOW_COPY_AND_ASSIGN(ReadBuffer);
600 };
601
Channel(Delegate * delegate)602 Channel::Channel(Delegate* delegate)
603 : delegate_(delegate), read_buffer_(new ReadBuffer) {}
604
~Channel()605 Channel::~Channel() {}
606
ShutDown()607 void Channel::ShutDown() {
608 ShutDownImpl();
609 delegate_ = nullptr;
610 }
611
GetReadBuffer(size_t * buffer_capacity)612 char* Channel::GetReadBuffer(size_t* buffer_capacity) {
613 DCHECK(read_buffer_);
614 size_t required_capacity = *buffer_capacity;
615 if (!required_capacity)
616 required_capacity = kReadBufferSize;
617
618 *buffer_capacity = required_capacity;
619 return read_buffer_->Reserve(required_capacity);
620 }
621
OnReadComplete(size_t bytes_read,size_t * next_read_size_hint)622 bool Channel::OnReadComplete(size_t bytes_read, size_t* next_read_size_hint) {
623 bool did_consume_message = false;
624 read_buffer_->Claim(bytes_read);
625 while (read_buffer_->num_occupied_bytes() >= sizeof(Message::LegacyHeader)) {
626 // Ensure the occupied data is properly aligned. If it isn't, a SIGBUS could
627 // happen on architectures that don't allow misaligned words access (i.e.
628 // anything other than x86). Only re-align when necessary to avoid copies.
629 if (!IsAlignedForChannelMessage(
630 reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()))) {
631 read_buffer_->Realign();
632 }
633
634 // We have at least enough data available for a LegacyHeader.
635 const Message::LegacyHeader* legacy_header =
636 reinterpret_cast<const Message::LegacyHeader*>(
637 read_buffer_->occupied_bytes());
638
639 const size_t kMaxMessageSize = GetConfiguration().max_message_num_bytes;
640 if (legacy_header->num_bytes < sizeof(Message::LegacyHeader) ||
641 legacy_header->num_bytes > kMaxMessageSize) {
642 LOG(ERROR) << "Invalid message size: " << legacy_header->num_bytes;
643 return false;
644 }
645
646 if (read_buffer_->num_occupied_bytes() < legacy_header->num_bytes) {
647 // Not enough data available to read the full message. Hint to the
648 // implementation that it should try reading the full size of the message.
649 *next_read_size_hint =
650 legacy_header->num_bytes - read_buffer_->num_occupied_bytes();
651 return true;
652 }
653
654 const Message::Header* header = nullptr;
655 if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY) {
656 header = reinterpret_cast<const Message::Header*>(legacy_header);
657 }
658
659 size_t extra_header_size = 0;
660 const void* extra_header = nullptr;
661 size_t payload_size = 0;
662 void* payload = nullptr;
663 if (header) {
664 if (header->num_header_bytes < sizeof(Message::Header) ||
665 header->num_header_bytes > header->num_bytes) {
666 LOG(ERROR) << "Invalid message header size: "
667 << header->num_header_bytes;
668 return false;
669 }
670 extra_header_size = header->num_header_bytes - sizeof(Message::Header);
671 extra_header = extra_header_size ? header + 1 : nullptr;
672 payload_size = header->num_bytes - header->num_header_bytes;
673 payload = payload_size
674 ? reinterpret_cast<Message::Header*>(
675 const_cast<char*>(read_buffer_->occupied_bytes()) +
676 header->num_header_bytes)
677 : nullptr;
678 } else {
679 payload_size = legacy_header->num_bytes - sizeof(Message::LegacyHeader);
680 payload = payload_size
681 ? const_cast<Message::LegacyHeader*>(&legacy_header[1])
682 : nullptr;
683 }
684
685 const uint16_t num_handles =
686 header ? header->num_handles : legacy_header->num_handles;
687 std::vector<PlatformHandle> handles;
688 bool deferred = false;
689 if (num_handles > 0) {
690 if (!GetReadPlatformHandles(payload, payload_size, num_handles,
691 extra_header, extra_header_size, &handles,
692 &deferred)) {
693 return false;
694 }
695
696 if (handles.empty()) {
697 // Not enough handles available for this message.
698 break;
699 }
700 }
701
702 // We've got a complete message! Dispatch it and try another.
703 if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY &&
704 legacy_header->message_type != Message::MessageType::NORMAL) {
705 DCHECK(!deferred);
706 if (!OnControlMessage(legacy_header->message_type, payload, payload_size,
707 std::move(handles))) {
708 return false;
709 }
710 did_consume_message = true;
711 } else if (deferred) {
712 did_consume_message = true;
713 } else if (delegate_) {
714 delegate_->OnChannelMessage(payload, payload_size, std::move(handles));
715 did_consume_message = true;
716 }
717
718 read_buffer_->Discard(legacy_header->num_bytes);
719 }
720
721 *next_read_size_hint = did_consume_message ? 0 : kReadBufferSize;
722 return true;
723 }
724
OnError(Error error)725 void Channel::OnError(Error error) {
726 if (delegate_)
727 delegate_->OnChannelError(error);
728 }
729
OnControlMessage(Message::MessageType message_type,const void * payload,size_t payload_size,std::vector<PlatformHandle> handles)730 bool Channel::OnControlMessage(Message::MessageType message_type,
731 const void* payload,
732 size_t payload_size,
733 std::vector<PlatformHandle> handles) {
734 return false;
735 }
736
737 } // namespace core
738 } // namespace mojo
739