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.
5 #include "mojo/system/transport_data.h"
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"
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
);
29 STATIC_CONST_MEMBER_DEFINITION
const size_t
30 TransportData::kMaxSerializedDispatcherSize
;
31 STATIC_CONST_MEMBER_DEFINITION
const size_t
32 TransportData::kMaxSerializedDispatcherPlatformHandles
;
35 const size_t TransportData::kMaxPlatformHandles
=
36 kMaxMessageNumHandles
* kMaxSerializedDispatcherPlatformHandles
;
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).
43 const size_t TransportData::kMaxBufferSize
=
45 kMaxMessageNumHandles
* (sizeof(HandleTableEntry
) +
46 kMaxSerializedDispatcherSize
) +
47 kMaxPlatformHandles
* kMaxSizePerPlatformHandle
;
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
);
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
);
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
);
65 TransportData::TransportData(scoped_ptr
<DispatcherVector
> dispatchers
,
70 const size_t num_handles
= dispatchers
->size();
71 DCHECK_GT(num_handles
, 0u);
73 // The offset to the start of the (Mojo) handle table.
74 const size_t handle_table_start_offset
= sizeof(Header
);
75 // The offset to the start of the serialized dispatcher data.
76 const size_t serialized_dispatcher_start_offset
=
77 handle_table_start_offset
+ num_handles
* sizeof(HandleTableEntry
);
78 // The estimated size of the secondary buffer. We compute this estimate below.
79 // It must be at least as big as the (eventual) actual size.
80 size_t estimated_size
= serialized_dispatcher_start_offset
;
81 size_t estimated_num_platform_handles
= 0;
83 std::vector
<size_t> all_max_sizes(num_handles
);
84 std::vector
<size_t> all_max_platform_handles(num_handles
);
86 for (size_t i
= 0; i
< num_handles
; i
++) {
87 if (Dispatcher
* dispatcher
= (*dispatchers
)[i
].get()) {
89 size_t max_platform_handles
= 0;
90 Dispatcher::TransportDataAccess::StartSerialize(
91 dispatcher
, channel
, &max_size
, &max_platform_handles
);
93 DCHECK_LE(max_size
, kMaxSerializedDispatcherSize
);
94 estimated_size
+= MessageInTransit::RoundUpMessageAlignment(max_size
);
95 DCHECK_LE(estimated_size
, kMaxBufferSize
);
97 DCHECK_LE(max_platform_handles
,
98 kMaxSerializedDispatcherPlatformHandles
);
99 estimated_num_platform_handles
+= max_platform_handles
;
100 DCHECK_LE(estimated_num_platform_handles
, kMaxPlatformHandles
);
103 all_max_sizes
[i
] = max_size
;
104 all_max_platform_handles
[i
] = max_platform_handles
;
109 size_t size_per_platform_handle
= 0;
110 if (estimated_num_platform_handles
> 0) {
111 size_per_platform_handle
= channel
->GetSerializedPlatformHandleSize();
112 DCHECK_LE(size_per_platform_handle
, kMaxSizePerPlatformHandle
);
113 estimated_size
+= estimated_num_platform_handles
* size_per_platform_handle
;
114 estimated_size
= MessageInTransit::RoundUpMessageAlignment(estimated_size
);
115 DCHECK_LE(estimated_size
, kMaxBufferSize
);
118 buffer_
.reset(static_cast<char*>(
119 base::AlignedAlloc(estimated_size
, MessageInTransit::kMessageAlignment
)));
120 // Entirely clear out the secondary buffer, since then we won't have to worry
121 // about clearing padding or unused space (e.g., if a dispatcher fails to
123 memset(buffer_
.get(), 0, estimated_size
);
125 if (estimated_num_platform_handles
> 0) {
126 DCHECK(!platform_handles_
);
127 platform_handles_
.reset(new embedder::PlatformHandleVector());
130 Header
* header
= reinterpret_cast<Header
*>(buffer_
.get());
131 header
->num_handles
= static_cast<uint32_t>(num_handles
);
132 // (Okay to leave |platform_handle_table_offset|, |num_platform_handles|, and
133 // |unused| be zero; we'll set the former two later if necessary.)
135 HandleTableEntry
* handle_table
= reinterpret_cast<HandleTableEntry
*>(
136 buffer_
.get() + handle_table_start_offset
);
137 size_t current_offset
= serialized_dispatcher_start_offset
;
138 for (size_t i
= 0; i
< num_handles
; i
++) {
139 Dispatcher
* dispatcher
= (*dispatchers
)[i
].get();
141 COMPILE_ASSERT(Dispatcher::kTypeUnknown
== 0,
142 value_of_Dispatcher_kTypeUnknown_must_be_zero
);
147 size_t old_platform_handles_size
=
148 platform_handles_
? platform_handles_
->size() : 0;
151 void* destination
= buffer_
.get() + current_offset
;
152 size_t actual_size
= 0;
153 if (Dispatcher::TransportDataAccess::EndSerializeAndClose(
154 dispatcher
, channel
, destination
, &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.)
162 DCHECK_LE(actual_size
, all_max_sizes
[i
]);
163 DCHECK_LE(platform_handles_
? (platform_handles_
->size() -
164 old_platform_handles_size
) : 0,
165 all_max_platform_handles
[i
]);
168 // Nothing to do on failure, since |buffer_| was cleared, and
169 // |kTypeUnknown| is zero. The handle was simply closed.
170 LOG(ERROR
) << "Failed to serialize handle to remote message pipe";
173 current_offset
+= MessageInTransit::RoundUpMessageAlignment(actual_size
);
174 DCHECK_LE(current_offset
, estimated_size
);
175 DCHECK_LE(platform_handles_
? platform_handles_
->size() : 0,
176 estimated_num_platform_handles
);
179 if (platform_handles_
&& platform_handles_
->size() > 0) {
180 header
->platform_handle_table_offset
=
181 static_cast<uint32_t>(current_offset
);
182 header
->num_platform_handles
=
183 static_cast<uint32_t>(platform_handles_
->size());
184 current_offset
+= platform_handles_
->size() * size_per_platform_handle
;
185 current_offset
= MessageInTransit::RoundUpMessageAlignment(current_offset
);
188 // There's no aligned realloc, so it's no good way to release unused space (if
189 // we overshot our estimated space requirements).
190 buffer_size_
= current_offset
;
192 // |dispatchers_| will be destroyed as it goes out of scope.
195 #if defined(OS_POSIX)
196 TransportData::TransportData(
197 embedder::ScopedPlatformHandleVectorPtr platform_handles
)
198 : buffer_size_(sizeof(Header
)),
199 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_
);
204 #endif // defined(OS_POSIX)
206 TransportData::~TransportData() {
210 const char* TransportData::ValidateBuffer(
211 size_t serialized_platform_handle_size
,
213 size_t buffer_size
) {
215 DCHECK_GT(buffer_size
, 0u);
217 // Always make sure that the buffer size is sane; if it's not, someone's
219 if (buffer_size
< sizeof(Header
) || buffer_size
> kMaxBufferSize
||
220 buffer_size
% MessageInTransit::kMessageAlignment
!= 0)
221 return "Invalid message secondary buffer size";
223 const Header
* header
= static_cast<const Header
*>(buffer
);
224 const size_t num_handles
= header
->num_handles
;
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";
235 // Sanity-check |num_handles| (before multiplying it against anything).
236 if (num_handles
> kMaxMessageNumHandles
)
237 return "Message handle payload too large";
239 if (buffer_size
< sizeof(Header
) + num_handles
* sizeof(HandleTableEntry
))
240 return "Message secondary buffer too small";
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) {
246 "Message has no handles attached, but platform handle table present";
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";
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
!= 0)
260 return kInvalidPlatformHandleTableOffset
;
262 // ">" instead of ">=" since the size per handle may be zero.
263 if (header
->platform_handle_table_offset
> buffer_size
)
264 return kInvalidPlatformHandleTableOffset
;
266 // We already checked |platform_handle_table_offset| and
267 // |num_platform_handles|, so the addition and multiplication are okay.
268 if (header
->platform_handle_table_offset
+
269 header
->num_platform_handles
* serialized_platform_handle_size
>
271 return kInvalidPlatformHandleTableOffset
;
274 const HandleTableEntry
* handle_table
=
275 reinterpret_cast<const HandleTableEntry
*>(
276 static_cast<const char*>(buffer
) + sizeof(Header
));
277 static const char kInvalidSerializedDispatcher
[] =
278 "Message contains invalid serialized dispatcher";
279 for (size_t i
= 0; i
< num_handles
; i
++) {
280 size_t offset
= handle_table
[i
].offset
;
281 if (offset
% MessageInTransit::kMessageAlignment
!= 0)
282 return kInvalidSerializedDispatcher
;
284 size_t size
= handle_table
[i
].size
;
285 if (size
> kMaxSerializedDispatcherSize
|| size
> buffer_size
)
286 return kInvalidSerializedDispatcher
;
288 // Note: This is an overflow-safe check for |offset + size > buffer_size|
289 // (we know that |size <= buffer_size| from the previous check).
290 if (offset
> buffer_size
- size
)
291 return kInvalidSerializedDispatcher
;
298 void TransportData::GetPlatformHandleTable(const void* transport_data_buffer
,
299 size_t* num_platform_handles
,
300 const void** platform_handle_table
) {
301 DCHECK(transport_data_buffer
);
302 DCHECK(num_platform_handles
);
303 DCHECK(platform_handle_table
);
305 const Header
* header
= static_cast<const Header
*>(transport_data_buffer
);
306 *num_platform_handles
= header
->num_platform_handles
;
307 *platform_handle_table
= static_cast<const char*>(transport_data_buffer
) +
308 header
->platform_handle_table_offset
;
312 scoped_ptr
<DispatcherVector
> TransportData::DeserializeDispatchers(
315 embedder::ScopedPlatformHandleVectorPtr platform_handles
,
318 DCHECK_GT(buffer_size
, 0u);
321 const Header
* header
= static_cast<const Header
*>(buffer
);
322 const size_t num_handles
= header
->num_handles
;
323 scoped_ptr
<DispatcherVector
> dispatchers(new DispatcherVector(num_handles
));
325 const HandleTableEntry
* handle_table
=
326 reinterpret_cast<const HandleTableEntry
*>(
327 static_cast<const char*>(buffer
) + sizeof(Header
));
328 for (size_t i
= 0; i
< num_handles
; i
++) {
329 size_t offset
= handle_table
[i
].offset
;
330 size_t size
= handle_table
[i
].size
;
331 // Should already have been checked by |ValidateBuffer()|:
332 DCHECK_EQ(offset
% MessageInTransit::kMessageAlignment
, 0u);
333 DCHECK_LE(offset
, buffer_size
);
334 DCHECK_LE(offset
+ size
, buffer_size
);
336 const void* source
= static_cast<const char*>(buffer
) + offset
;
337 (*dispatchers
)[i
] = Dispatcher::TransportDataAccess::Deserialize(
338 channel
, handle_table
[i
].type
, source
, size
, platform_handles
.get());
341 return dispatchers
.Pass();
344 } // namespace system