Home | History | Annotate | Download | only in system
      1 // Copyright 2014 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/system/transport_data.h"
      6 
      7 #include <string.h>
      8 
      9 #include "base/compiler_specific.h"
     10 #include "base/logging.h"
     11 #include "mojo/system/channel.h"
     12 #include "mojo/system/constants.h"
     13 #include "mojo/system/message_in_transit.h"
     14 
     15 namespace mojo {
     16 namespace system {
     17 
     18 // The maximum amount of space needed per platform handle.
     19 // (|{Channel,RawChannel}::GetSerializedPlatformHandleSize()| should always
     20 // return a value which is at most this. This is only used to calculate
     21 // |TransportData::kMaxBufferSize|. This value should be a multiple of the
     22 // alignment in order to simplify calculations, even though the actual amount of
     23 // space needed need not be a multiple of the alignment.
     24 const size_t kMaxSizePerPlatformHandle = 8;
     25 static_assert(kMaxSizePerPlatformHandle % MessageInTransit::kMessageAlignment ==
     26                   0,
     27               "kMaxSizePerPlatformHandle not a multiple of alignment");
     28 
     29 STATIC_CONST_MEMBER_DEFINITION const size_t
     30     TransportData::kMaxSerializedDispatcherSize;
     31 STATIC_CONST_MEMBER_DEFINITION const size_t
     32     TransportData::kMaxSerializedDispatcherPlatformHandles;
     33 
     34 // static
     35 const size_t TransportData::kMaxPlatformHandles =
     36     kMaxMessageNumHandles * kMaxSerializedDispatcherPlatformHandles;
     37 
     38 // In additional to the header, for each attached (Mojo) handle there'll be a
     39 // handle table entry and serialized dispatcher data.
     40 // Note: This definition must follow the one for |kMaxPlatformHandles|;
     41 // otherwise, we get a static initializer with gcc (but not clang).
     42 // static
     43 const size_t TransportData::kMaxBufferSize =
     44     sizeof(Header) +
     45     kMaxMessageNumHandles *
     46         (sizeof(HandleTableEntry) + kMaxSerializedDispatcherSize) +
     47     kMaxPlatformHandles * kMaxSizePerPlatformHandle;
     48 
     49 struct TransportData::PrivateStructForCompileAsserts {
     50   static_assert(sizeof(Header) % MessageInTransit::kMessageAlignment == 0,
     51                 "sizeof(MessageInTransit::Header) not a multiple of alignment");
     52   static_assert(kMaxSerializedDispatcherSize %
     53                         MessageInTransit::kMessageAlignment ==
     54                     0,
     55                 "kMaxSerializedDispatcherSize not a multiple of alignment");
     56   static_assert(sizeof(HandleTableEntry) %
     57                         MessageInTransit::kMessageAlignment ==
     58                     0,
     59                 "sizeof(MessageInTransit::HandleTableEntry) not a multiple of "
     60                 "alignment");
     61 };
     62 
     63 TransportData::TransportData(scoped_ptr<DispatcherVector> dispatchers,
     64                              Channel* channel) {
     65   DCHECK(dispatchers);
     66   DCHECK(channel);
     67 
     68   const size_t num_handles = dispatchers->size();
     69   DCHECK_GT(num_handles, 0u);
     70 
     71   // The offset to the start of the (Mojo) handle table.
     72   const size_t handle_table_start_offset = sizeof(Header);
     73   // The offset to the start of the serialized dispatcher data.
     74   const size_t serialized_dispatcher_start_offset =
     75       handle_table_start_offset + num_handles * sizeof(HandleTableEntry);
     76   // The estimated size of the secondary buffer. We compute this estimate below.
     77   // It must be at least as big as the (eventual) actual size.
     78   size_t estimated_size = serialized_dispatcher_start_offset;
     79   size_t estimated_num_platform_handles = 0;
     80 #if DCHECK_IS_ON
     81   std::vector<size_t> all_max_sizes(num_handles);
     82   std::vector<size_t> all_max_platform_handles(num_handles);
     83 #endif
     84   for (size_t i = 0; i < num_handles; i++) {
     85     if (Dispatcher* dispatcher = (*dispatchers)[i].get()) {
     86       size_t max_size = 0;
     87       size_t max_platform_handles = 0;
     88       Dispatcher::TransportDataAccess::StartSerialize(
     89           dispatcher, channel, &max_size, &max_platform_handles);
     90 
     91       DCHECK_LE(max_size, kMaxSerializedDispatcherSize);
     92       estimated_size += MessageInTransit::RoundUpMessageAlignment(max_size);
     93       DCHECK_LE(estimated_size, kMaxBufferSize);
     94 
     95       DCHECK_LE(max_platform_handles, kMaxSerializedDispatcherPlatformHandles);
     96       estimated_num_platform_handles += max_platform_handles;
     97       DCHECK_LE(estimated_num_platform_handles, kMaxPlatformHandles);
     98 
     99 #if DCHECK_IS_ON
    100       all_max_sizes[i] = max_size;
    101       all_max_platform_handles[i] = max_platform_handles;
    102 #endif
    103     }
    104   }
    105 
    106   size_t size_per_platform_handle = 0;
    107   if (estimated_num_platform_handles > 0) {
    108     size_per_platform_handle = channel->GetSerializedPlatformHandleSize();
    109     DCHECK_LE(size_per_platform_handle, kMaxSizePerPlatformHandle);
    110     estimated_size += estimated_num_platform_handles * size_per_platform_handle;
    111     estimated_size = MessageInTransit::RoundUpMessageAlignment(estimated_size);
    112     DCHECK_LE(estimated_size, kMaxBufferSize);
    113   }
    114 
    115   buffer_.reset(static_cast<char*>(
    116       base::AlignedAlloc(estimated_size, MessageInTransit::kMessageAlignment)));
    117   // Entirely clear out the secondary buffer, since then we won't have to worry
    118   // about clearing padding or unused space (e.g., if a dispatcher fails to
    119   // serialize).
    120   memset(buffer_.get(), 0, estimated_size);
    121 
    122   if (estimated_num_platform_handles > 0) {
    123     DCHECK(!platform_handles_);
    124     platform_handles_.reset(new embedder::PlatformHandleVector());
    125   }
    126 
    127   Header* header = reinterpret_cast<Header*>(buffer_.get());
    128   header->num_handles = static_cast<uint32_t>(num_handles);
    129   // (Okay to leave |platform_handle_table_offset|, |num_platform_handles|, and
    130   // |unused| be zero; we'll set the former two later if necessary.)
    131 
    132   HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>(
    133       buffer_.get() + handle_table_start_offset);
    134   size_t current_offset = serialized_dispatcher_start_offset;
    135   for (size_t i = 0; i < num_handles; i++) {
    136     Dispatcher* dispatcher = (*dispatchers)[i].get();
    137     if (!dispatcher) {
    138       static_assert(Dispatcher::kTypeUnknown == 0,
    139                     "Value of Dispatcher::kTypeUnknown must be 0");
    140       continue;
    141     }
    142 
    143 #if DCHECK_IS_ON
    144     size_t old_platform_handles_size =
    145         platform_handles_ ? platform_handles_->size() : 0;
    146 #endif
    147 
    148     void* destination = buffer_.get() + current_offset;
    149     size_t actual_size = 0;
    150     if (Dispatcher::TransportDataAccess::EndSerializeAndClose(
    151             dispatcher,
    152             channel,
    153             destination,
    154             &actual_size,
    155             platform_handles_.get())) {
    156       handle_table[i].type = static_cast<int32_t>(dispatcher->GetType());
    157       handle_table[i].offset = static_cast<uint32_t>(current_offset);
    158       handle_table[i].size = static_cast<uint32_t>(actual_size);
    159 // (Okay to not set |unused| since we cleared the entire buffer.)
    160 
    161 #if DCHECK_IS_ON
    162       DCHECK_LE(actual_size, all_max_sizes[i]);
    163       DCHECK_LE(platform_handles_
    164                     ? (platform_handles_->size() - old_platform_handles_size)
    165                     : 0,
    166                 all_max_platform_handles[i]);
    167 #endif
    168     } else {
    169       // Nothing to do on failure, since |buffer_| was cleared, and
    170       // |kTypeUnknown| is zero. The handle was simply closed.
    171       LOG(ERROR) << "Failed to serialize handle to remote message pipe";
    172     }
    173 
    174     current_offset += MessageInTransit::RoundUpMessageAlignment(actual_size);
    175     DCHECK_LE(current_offset, estimated_size);
    176     DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0,
    177               estimated_num_platform_handles);
    178   }
    179 
    180   if (platform_handles_ && platform_handles_->size() > 0) {
    181     header->platform_handle_table_offset =
    182         static_cast<uint32_t>(current_offset);
    183     header->num_platform_handles =
    184         static_cast<uint32_t>(platform_handles_->size());
    185     current_offset += platform_handles_->size() * size_per_platform_handle;
    186     current_offset = MessageInTransit::RoundUpMessageAlignment(current_offset);
    187   }
    188 
    189   // There's no aligned realloc, so it's no good way to release unused space (if
    190   // we overshot our estimated space requirements).
    191   buffer_size_ = current_offset;
    192 
    193   // |dispatchers_| will be destroyed as it goes out of scope.
    194 }
    195 
    196 #if defined(OS_POSIX)
    197 TransportData::TransportData(
    198     embedder::ScopedPlatformHandleVectorPtr platform_handles)
    199     : buffer_size_(sizeof(Header)), platform_handles_(platform_handles.Pass()) {
    200   buffer_.reset(static_cast<char*>(
    201       base::AlignedAlloc(buffer_size_, MessageInTransit::kMessageAlignment)));
    202   memset(buffer_.get(), 0, buffer_size_);
    203 }
    204 #endif  // defined(OS_POSIX)
    205 
    206 TransportData::~TransportData() {
    207 }
    208 
    209 // static
    210 const char* TransportData::ValidateBuffer(
    211     size_t serialized_platform_handle_size,
    212     const void* buffer,
    213     size_t buffer_size) {
    214   DCHECK(buffer);
    215   DCHECK_GT(buffer_size, 0u);
    216 
    217   // Always make sure that the buffer size is sane; if it's not, someone's
    218   // messing with us.
    219   if (buffer_size < sizeof(Header) || buffer_size > kMaxBufferSize ||
    220       buffer_size % MessageInTransit::kMessageAlignment != 0)
    221     return "Invalid message secondary buffer size";
    222 
    223   const Header* header = static_cast<const Header*>(buffer);
    224   const size_t num_handles = header->num_handles;
    225 
    226 #if !defined(OS_POSIX)
    227   // On POSIX, we send control messages with platform handles (but no handles)
    228   // attached (see the comments for
    229   // |TransportData(embedder::ScopedPlatformHandleVectorPtr)|. (This check isn't
    230   // important security-wise anyway.)
    231   if (num_handles == 0)
    232     return "Message has no handles attached, but secondary buffer present";
    233 #endif
    234 
    235   // Sanity-check |num_handles| (before multiplying it against anything).
    236   if (num_handles > kMaxMessageNumHandles)
    237     return "Message handle payload too large";
    238 
    239   if (buffer_size < sizeof(Header) + num_handles * sizeof(HandleTableEntry))
    240     return "Message secondary buffer too small";
    241 
    242   if (header->num_platform_handles == 0) {
    243     // Then |platform_handle_table_offset| should also be zero.
    244     if (header->platform_handle_table_offset != 0) {
    245       return "Message has no handles attached, but platform handle table "
    246              "present";
    247     }
    248   } else {
    249     // |num_handles| has already been validated, so the multiplication is okay.
    250     if (header->num_platform_handles >
    251         num_handles * kMaxSerializedDispatcherPlatformHandles)
    252       return "Message has too many platform handles attached";
    253 
    254     static const char kInvalidPlatformHandleTableOffset[] =
    255         "Message has invalid platform handle table offset";
    256     // This doesn't check that the platform handle table doesn't alias other
    257     // stuff, but it doesn't matter, since it's all read-only.
    258     if (header->platform_handle_table_offset %
    259             MessageInTransit::kMessageAlignment !=
    260         0)
    261       return kInvalidPlatformHandleTableOffset;
    262 
    263     // ">" instead of ">=" since the size per handle may be zero.
    264     if (header->platform_handle_table_offset > buffer_size)
    265       return kInvalidPlatformHandleTableOffset;
    266 
    267     // We already checked |platform_handle_table_offset| and
    268     // |num_platform_handles|, so the addition and multiplication are okay.
    269     if (header->platform_handle_table_offset +
    270             header->num_platform_handles * serialized_platform_handle_size >
    271         buffer_size)
    272       return kInvalidPlatformHandleTableOffset;
    273   }
    274 
    275   const HandleTableEntry* handle_table =
    276       reinterpret_cast<const HandleTableEntry*>(
    277           static_cast<const char*>(buffer) + sizeof(Header));
    278   static const char kInvalidSerializedDispatcher[] =
    279       "Message contains invalid serialized dispatcher";
    280   for (size_t i = 0; i < num_handles; i++) {
    281     size_t offset = handle_table[i].offset;
    282     if (offset % MessageInTransit::kMessageAlignment != 0)
    283       return kInvalidSerializedDispatcher;
    284 
    285     size_t size = handle_table[i].size;
    286     if (size > kMaxSerializedDispatcherSize || size > buffer_size)
    287       return kInvalidSerializedDispatcher;
    288 
    289     // Note: This is an overflow-safe check for |offset + size > buffer_size|
    290     // (we know that |size <= buffer_size| from the previous check).
    291     if (offset > buffer_size - size)
    292       return kInvalidSerializedDispatcher;
    293   }
    294 
    295   return nullptr;
    296 }
    297 
    298 // static
    299 void TransportData::GetPlatformHandleTable(const void* transport_data_buffer,
    300                                            size_t* num_platform_handles,
    301                                            const void** platform_handle_table) {
    302   DCHECK(transport_data_buffer);
    303   DCHECK(num_platform_handles);
    304   DCHECK(platform_handle_table);
    305 
    306   const Header* header = static_cast<const Header*>(transport_data_buffer);
    307   *num_platform_handles = header->num_platform_handles;
    308   *platform_handle_table = static_cast<const char*>(transport_data_buffer) +
    309                            header->platform_handle_table_offset;
    310 }
    311 
    312 // static
    313 scoped_ptr<DispatcherVector> TransportData::DeserializeDispatchers(
    314     const void* buffer,
    315     size_t buffer_size,
    316     embedder::ScopedPlatformHandleVectorPtr platform_handles,
    317     Channel* channel) {
    318   DCHECK(buffer);
    319   DCHECK_GT(buffer_size, 0u);
    320   DCHECK(channel);
    321 
    322   const Header* header = static_cast<const Header*>(buffer);
    323   const size_t num_handles = header->num_handles;
    324   scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector(num_handles));
    325 
    326   const HandleTableEntry* handle_table =
    327       reinterpret_cast<const HandleTableEntry*>(
    328           static_cast<const char*>(buffer) + sizeof(Header));
    329   for (size_t i = 0; i < num_handles; i++) {
    330     size_t offset = handle_table[i].offset;
    331     size_t size = handle_table[i].size;
    332     // Should already have been checked by |ValidateBuffer()|:
    333     DCHECK_EQ(offset % MessageInTransit::kMessageAlignment, 0u);
    334     DCHECK_LE(offset, buffer_size);
    335     DCHECK_LE(offset + size, buffer_size);
    336 
    337     const void* source = static_cast<const char*>(buffer) + offset;
    338     (*dispatchers)[i] = Dispatcher::TransportDataAccess::Deserialize(
    339         channel, handle_table[i].type, source, size, platform_handles.get());
    340   }
    341 
    342   return dispatchers.Pass();
    343 }
    344 
    345 }  // namespace system
    346 }  // namespace mojo
    347