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