Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / mojo / system / transport_data.cc
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 COMPILE_ASSERT(kMaxSizePerPlatformHandle %
26                    MessageInTransit::kMessageAlignment == 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 * (sizeof(HandleTableEntry) +
46                                  kMaxSerializedDispatcherSize) +
47     kMaxPlatformHandles * kMaxSizePerPlatformHandle;
48
49 struct TransportData::PrivateStructForCompileAsserts {
50   // The size of |Header| must be a multiple of the alignment.
51   COMPILE_ASSERT(sizeof(Header) % MessageInTransit::kMessageAlignment == 0,
52                  sizeof_MessageInTransit_Header_invalid);
53
54   // The maximum serialized dispatcher size must be a multiple of the alignment.
55   COMPILE_ASSERT(kMaxSerializedDispatcherSize %
56                      MessageInTransit::kMessageAlignment == 0,
57                  kMaxSerializedDispatcherSize_not_a_multiple_of_alignment);
58
59   // The size of |HandleTableEntry| must be a multiple of the alignment.
60   COMPILE_ASSERT(sizeof(HandleTableEntry) %
61                      MessageInTransit::kMessageAlignment == 0,
62                  sizeof_MessageInTransit_HandleTableEntry_invalid);
63 };
64
65 TransportData::TransportData(scoped_ptr<DispatcherVector> dispatchers,
66                              Channel* channel)
67     : buffer_size_(0) {
68   DCHECK(dispatchers);
69   DCHECK(channel);
70
71   const size_t num_handles = dispatchers->size();
72   DCHECK_GT(num_handles, 0u);
73
74   // The offset to the start of the (Mojo) handle table.
75   const size_t handle_table_start_offset = sizeof(Header);
76   // The offset to the start of the serialized dispatcher data.
77   const size_t serialized_dispatcher_start_offset =
78       handle_table_start_offset + num_handles * sizeof(HandleTableEntry);
79   // The estimated size of the secondary buffer. We compute this estimate below.
80   // It must be at least as big as the (eventual) actual size.
81   size_t estimated_size = serialized_dispatcher_start_offset;
82   size_t estimated_num_platform_handles = 0;
83 #if DCHECK_IS_ON
84   std::vector<size_t> all_max_sizes(num_handles);
85   std::vector<size_t> all_max_platform_handles(num_handles);
86 #endif
87   for (size_t i = 0; i < num_handles; i++) {
88     if (Dispatcher* dispatcher = (*dispatchers)[i].get()) {
89       size_t max_size = 0;
90       size_t max_platform_handles = 0;
91       Dispatcher::TransportDataAccess::StartSerialize(
92               dispatcher, channel, &max_size, &max_platform_handles);
93
94       DCHECK_LE(max_size, kMaxSerializedDispatcherSize);
95       estimated_size += MessageInTransit::RoundUpMessageAlignment(max_size);
96       DCHECK_LE(estimated_size, kMaxBufferSize);
97
98       DCHECK_LE(max_platform_handles,
99                 kMaxSerializedDispatcherPlatformHandles);
100       estimated_num_platform_handles += max_platform_handles;
101       DCHECK_LE(estimated_num_platform_handles, kMaxPlatformHandles);
102
103 #if DCHECK_IS_ON
104       all_max_sizes[i] = max_size;
105       all_max_platform_handles[i] = max_platform_handles;
106 #endif
107     }
108   }
109
110   size_t size_per_platform_handle = 0;
111   if (estimated_num_platform_handles > 0) {
112     size_per_platform_handle = channel->GetSerializedPlatformHandleSize();
113     DCHECK_LE(size_per_platform_handle, kMaxSizePerPlatformHandle);
114     estimated_size += estimated_num_platform_handles * size_per_platform_handle;
115     estimated_size = MessageInTransit::RoundUpMessageAlignment(estimated_size);
116     DCHECK_LE(estimated_size, kMaxBufferSize);
117   }
118
119   buffer_.reset(static_cast<char*>(
120       base::AlignedAlloc(estimated_size, MessageInTransit::kMessageAlignment)));
121   // Entirely clear out the secondary buffer, since then we won't have to worry
122   // about clearing padding or unused space (e.g., if a dispatcher fails to
123   // serialize).
124   memset(buffer_.get(), 0, estimated_size);
125
126   if (estimated_num_platform_handles > 0) {
127     DCHECK(!platform_handles_);
128     platform_handles_.reset(new std::vector<embedder::PlatformHandle>());
129   }
130
131   Header* header = reinterpret_cast<Header*>(buffer_.get());
132   header->num_handles = static_cast<uint32_t>(num_handles);
133   // (Okay to leave |platform_handle_table_offset|, |num_platform_handles|, and
134   // |unused| be zero; we'll set the former two later if necessary.)
135
136   HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>(
137       buffer_.get() + handle_table_start_offset);
138   size_t current_offset = serialized_dispatcher_start_offset;
139   for (size_t i = 0; i < num_handles; i++) {
140     Dispatcher* dispatcher = (*dispatchers)[i].get();
141     if (!dispatcher) {
142       COMPILE_ASSERT(Dispatcher::kTypeUnknown == 0,
143                      value_of_Dispatcher_kTypeUnknown_must_be_zero);
144       continue;
145     }
146
147 #if DCHECK_IS_ON
148     size_t old_platform_handles_size =
149         platform_handles_ ? platform_handles_->size() : 0;
150 #endif
151
152     void* destination = buffer_.get() + current_offset;
153     size_t actual_size = 0;
154     if (Dispatcher::TransportDataAccess::EndSerializeAndClose(
155             dispatcher, channel, destination, &actual_size,
156             platform_handles_.get())) {
157       handle_table[i].type = static_cast<int32_t>(dispatcher->GetType());
158       handle_table[i].offset = static_cast<uint32_t>(current_offset);
159       handle_table[i].size = static_cast<uint32_t>(actual_size);
160       // (Okay to not set |unused| since we cleared the entire buffer.)
161
162 #if DCHECK_IS_ON
163       DCHECK_LE(actual_size, all_max_sizes[i]);
164       DCHECK_LE(platform_handles_ ? (platform_handles_->size() -
165                                          old_platform_handles_size) : 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 TransportData::~TransportData() {
197   if (platform_handles_) {
198     for (size_t i = 0; i < platform_handles_->size(); i++)
199       (*platform_handles_)[i].CloseIfNecessary();
200   }
201 }
202
203 // static
204 const char* TransportData::ValidateBuffer(const void* buffer,
205                                           size_t buffer_size) {
206   DCHECK(buffer);
207   DCHECK_GT(buffer_size, 0u);
208
209   // Always make sure that the buffer size is sane; if it's not, someone's
210   // messing with us.
211   if (buffer_size < sizeof(Header) || buffer_size > kMaxBufferSize ||
212       buffer_size % MessageInTransit::kMessageAlignment != 0)
213     return "Invalid message secondary buffer size";
214
215   const Header* header = static_cast<const Header*>(buffer);
216   const size_t num_handles = header->num_handles;
217   if (num_handles == 0)
218     return "Message has no handles attached, but secondary buffer present";
219
220   // Sanity-check |num_handles| (before multiplying it against anything).
221   if (num_handles > kMaxMessageNumHandles)
222     return "Message handle payload too large";
223
224   if (buffer_size < sizeof(Header) + num_handles * sizeof(HandleTableEntry))
225     return "Message secondary buffer too small";
226
227   // TODO(vtl): Check |platform_handle_table_offset| and |num_platform_handles|
228   // once they're used.
229   if (header->platform_handle_table_offset != 0 ||
230       header->num_platform_handles != 0)
231     return "Bad message secondary buffer header values";
232
233   const HandleTableEntry* handle_table =
234       reinterpret_cast<const HandleTableEntry*>(
235           static_cast<const char*>(buffer) + sizeof(Header));
236   static const char kInvalidSerializedDispatcher[] =
237       "Message contains invalid serialized dispatcher";
238   for (size_t i = 0; i < num_handles; i++) {
239     size_t offset = handle_table[i].offset;
240     if (offset % MessageInTransit::kMessageAlignment != 0)
241       return kInvalidSerializedDispatcher;
242
243     size_t size = handle_table[i].size;
244     if (size > kMaxSerializedDispatcherSize || size > buffer_size)
245       return kInvalidSerializedDispatcher;
246
247     // Note: This is an overflow-safe check for |offset + size > buffer_size|
248     // (we know that |size <= buffer_size| from the previous check).
249     if (offset > buffer_size - size)
250       return kInvalidSerializedDispatcher;
251   }
252
253   return NULL;
254 }
255
256 // static
257 scoped_ptr<DispatcherVector> TransportData::DeserializeDispatchersFromBuffer(
258     const void* buffer,
259     size_t buffer_size,
260     Channel* channel) {
261   DCHECK(buffer);
262   DCHECK_GT(buffer_size, 0u);
263   DCHECK(channel);
264
265   const Header* header = static_cast<const Header*>(buffer);
266   const size_t num_handles = header->num_handles;
267   scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector(num_handles));
268
269   const HandleTableEntry* handle_table =
270       reinterpret_cast<const HandleTableEntry*>(
271           static_cast<const char*>(buffer) + sizeof(Header));
272   for (size_t i = 0; i < num_handles; i++) {
273     size_t offset = handle_table[i].offset;
274     size_t size = handle_table[i].size;
275     // Should already have been checked by |ValidateBuffer()|:
276     DCHECK_EQ(offset % MessageInTransit::kMessageAlignment, 0u);
277     DCHECK_LE(offset, buffer_size);
278     DCHECK_LE(offset + size, buffer_size);
279
280     const void* source = static_cast<const char*>(buffer) + offset;
281     (*dispatchers)[i] = Dispatcher::TransportDataAccess::Deserialize(
282         channel, handle_table[i].type, source, size);
283   }
284
285   return dispatchers.Pass();
286 }
287
288 }  // namespace system
289 }  // namespace mojo