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