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/core/shared_buffer_dispatcher.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <limits>
11 #include <memory>
12 #include <utility>
13
14 #include "base/logging.h"
15 #include "base/memory/ptr_util.h"
16 #include "build/build_config.h"
17 #include "mojo/core/configuration.h"
18 #include "mojo/core/node_controller.h"
19 #include "mojo/core/options_validation.h"
20 #include "mojo/core/platform_handle_utils.h"
21 #include "mojo/core/platform_shared_memory_mapping.h"
22 #include "mojo/public/c/system/platform_handle.h"
23
24 namespace mojo {
25 namespace core {
26
27 namespace {
28
29 #pragma pack(push, 1)
30
31 struct SerializedState {
32 uint64_t num_bytes;
33 uint32_t access_mode;
34 uint64_t guid_high;
35 uint64_t guid_low;
36 uint32_t padding;
37 };
38
39 #pragma pack(pop)
40
41 static_assert(sizeof(SerializedState) % 8 == 0,
42 "Invalid SerializedState size.");
43
44 } // namespace
45
46 // static
47 const MojoCreateSharedBufferOptions
48 SharedBufferDispatcher::kDefaultCreateOptions = {
49 static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)),
50 MOJO_CREATE_SHARED_BUFFER_FLAG_NONE};
51
52 // static
ValidateCreateOptions(const MojoCreateSharedBufferOptions * in_options,MojoCreateSharedBufferOptions * out_options)53 MojoResult SharedBufferDispatcher::ValidateCreateOptions(
54 const MojoCreateSharedBufferOptions* in_options,
55 MojoCreateSharedBufferOptions* out_options) {
56 const MojoCreateSharedBufferFlags kKnownFlags =
57 MOJO_CREATE_SHARED_BUFFER_FLAG_NONE;
58
59 *out_options = kDefaultCreateOptions;
60 if (!in_options)
61 return MOJO_RESULT_OK;
62
63 UserOptionsReader<MojoCreateSharedBufferOptions> reader(in_options);
64 if (!reader.is_valid())
65 return MOJO_RESULT_INVALID_ARGUMENT;
66
67 if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateSharedBufferOptions, flags, reader))
68 return MOJO_RESULT_OK;
69 if ((reader.options().flags & ~kKnownFlags))
70 return MOJO_RESULT_UNIMPLEMENTED;
71 out_options->flags = reader.options().flags;
72
73 // Checks for fields beyond |flags|:
74
75 // (Nothing here yet.)
76
77 return MOJO_RESULT_OK;
78 }
79
80 // static
Create(const MojoCreateSharedBufferOptions &,NodeController * node_controller,uint64_t num_bytes,scoped_refptr<SharedBufferDispatcher> * result)81 MojoResult SharedBufferDispatcher::Create(
82 const MojoCreateSharedBufferOptions& /*validated_options*/,
83 NodeController* node_controller,
84 uint64_t num_bytes,
85 scoped_refptr<SharedBufferDispatcher>* result) {
86 if (!num_bytes)
87 return MOJO_RESULT_INVALID_ARGUMENT;
88 if (num_bytes > GetConfiguration().max_shared_memory_num_bytes)
89 return MOJO_RESULT_RESOURCE_EXHAUSTED;
90
91 base::WritableSharedMemoryRegion writable_region;
92 if (node_controller) {
93 writable_region =
94 node_controller->CreateSharedBuffer(static_cast<size_t>(num_bytes));
95 } else {
96 writable_region = base::WritableSharedMemoryRegion::Create(
97 static_cast<size_t>(num_bytes));
98 }
99 if (!writable_region.IsValid())
100 return MOJO_RESULT_RESOURCE_EXHAUSTED;
101
102 *result = CreateInternal(
103 base::WritableSharedMemoryRegion::TakeHandleForSerialization(
104 std::move(writable_region)));
105 return MOJO_RESULT_OK;
106 }
107
108 // static
CreateFromPlatformSharedMemoryRegion(base::subtle::PlatformSharedMemoryRegion region,scoped_refptr<SharedBufferDispatcher> * result)109 MojoResult SharedBufferDispatcher::CreateFromPlatformSharedMemoryRegion(
110 base::subtle::PlatformSharedMemoryRegion region,
111 scoped_refptr<SharedBufferDispatcher>* result) {
112 if (!region.IsValid())
113 return MOJO_RESULT_INVALID_ARGUMENT;
114
115 *result = CreateInternal(std::move(region));
116 return MOJO_RESULT_OK;
117 }
118
119 // static
Deserialize(const void * bytes,size_t num_bytes,const ports::PortName * ports,size_t num_ports,PlatformHandle * platform_handles,size_t num_platform_handles)120 scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize(
121 const void* bytes,
122 size_t num_bytes,
123 const ports::PortName* ports,
124 size_t num_ports,
125 PlatformHandle* platform_handles,
126 size_t num_platform_handles) {
127 if (num_bytes != sizeof(SerializedState)) {
128 LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)";
129 return nullptr;
130 }
131
132 const SerializedState* serialized_state =
133 static_cast<const SerializedState*>(bytes);
134 if (!serialized_state->num_bytes) {
135 LOG(ERROR)
136 << "Invalid serialized shared buffer dispatcher (invalid num_bytes)";
137 return nullptr;
138 }
139
140 if (num_ports)
141 return nullptr;
142
143 PlatformHandle handles[2];
144 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
145 (!defined(OS_MACOSX) || defined(OS_IOS))
146 if (serialized_state->access_mode ==
147 MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) {
148 if (num_platform_handles != 2)
149 return nullptr;
150 handles[1] = std::move(platform_handles[1]);
151 } else {
152 if (num_platform_handles != 1)
153 return nullptr;
154 }
155 #else
156 if (num_platform_handles != 1)
157 return nullptr;
158 #endif
159 handles[0] = std::move(platform_handles[0]);
160
161 base::UnguessableToken guid = base::UnguessableToken::Deserialize(
162 serialized_state->guid_high, serialized_state->guid_low);
163
164 base::subtle::PlatformSharedMemoryRegion::Mode mode;
165 switch (serialized_state->access_mode) {
166 case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY:
167 mode = base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly;
168 break;
169 case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE:
170 mode = base::subtle::PlatformSharedMemoryRegion::Mode::kWritable;
171 break;
172 case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE:
173 mode = base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe;
174 break;
175 default:
176 LOG(ERROR) << "Invalid serialized shared buffer access mode.";
177 return nullptr;
178 }
179
180 auto region = base::subtle::PlatformSharedMemoryRegion::Take(
181 CreateSharedMemoryRegionHandleFromPlatformHandles(std::move(handles[0]),
182 std::move(handles[1])),
183 mode, static_cast<size_t>(serialized_state->num_bytes), guid);
184 if (!region.IsValid()) {
185 LOG(ERROR)
186 << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)";
187 return nullptr;
188 }
189
190 return CreateInternal(std::move(region));
191 }
192
193 base::subtle::PlatformSharedMemoryRegion
PassPlatformSharedMemoryRegion()194 SharedBufferDispatcher::PassPlatformSharedMemoryRegion() {
195 base::AutoLock lock(lock_);
196 if (!region_.IsValid() || in_transit_)
197 return base::subtle::PlatformSharedMemoryRegion();
198
199 return std::move(region_);
200 }
201
GetType() const202 Dispatcher::Type SharedBufferDispatcher::GetType() const {
203 return Type::SHARED_BUFFER;
204 }
205
Close()206 MojoResult SharedBufferDispatcher::Close() {
207 base::AutoLock lock(lock_);
208 if (in_transit_)
209 return MOJO_RESULT_INVALID_ARGUMENT;
210
211 region_ = base::subtle::PlatformSharedMemoryRegion();
212 return MOJO_RESULT_OK;
213 }
214
DuplicateBufferHandle(const MojoDuplicateBufferHandleOptions * options,scoped_refptr<Dispatcher> * new_dispatcher)215 MojoResult SharedBufferDispatcher::DuplicateBufferHandle(
216 const MojoDuplicateBufferHandleOptions* options,
217 scoped_refptr<Dispatcher>* new_dispatcher) {
218 MojoDuplicateBufferHandleOptions validated_options;
219 MojoResult result = ValidateDuplicateOptions(options, &validated_options);
220 if (result != MOJO_RESULT_OK)
221 return result;
222
223 base::AutoLock lock(lock_);
224 if (in_transit_)
225 return MOJO_RESULT_INVALID_ARGUMENT;
226
227 if ((validated_options.flags & MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY)) {
228 // If a read-only duplicate is requested and this handle is not already
229 // read-only, we need to make it read-only before duplicating. If it's
230 // unsafe it can't be made read-only, and we must fail instead.
231 if (region_.GetMode() ==
232 base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe) {
233 return MOJO_RESULT_FAILED_PRECONDITION;
234 } else if (region_.GetMode() ==
235 base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
236 region_ = base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
237 base::WritableSharedMemoryRegion::ConvertToReadOnly(
238 base::WritableSharedMemoryRegion::Deserialize(
239 std::move(region_))));
240 }
241
242 DCHECK_EQ(region_.GetMode(),
243 base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly);
244 } else {
245 // A writable duplicate was requested. If this is already a read-only handle
246 // we have to reject. Otherwise we have to convert to unsafe to ensure that
247 // no future read-only duplication requests can succeed.
248 if (region_.GetMode() ==
249 base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly) {
250 return MOJO_RESULT_FAILED_PRECONDITION;
251 } else if (region_.GetMode() ==
252 base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
253 auto handle = region_.PassPlatformHandle();
254 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
255 (!defined(OS_MACOSX) || defined(OS_IOS))
256 // On POSIX systems excluding Android, Fuchsia, and OSX, we explicitly
257 // wipe out the secondary (read-only) FD from the platform handle to
258 // repurpose it for exclusive unsafe usage.
259 handle.readonly_fd.reset();
260 #endif
261 region_ = base::subtle::PlatformSharedMemoryRegion::Take(
262 std::move(handle),
263 base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
264 region_.GetSize(), region_.GetGUID());
265 }
266 }
267
268 *new_dispatcher = CreateInternal(region_.Duplicate());
269 return MOJO_RESULT_OK;
270 }
271
MapBuffer(uint64_t offset,uint64_t num_bytes,std::unique_ptr<PlatformSharedMemoryMapping> * mapping)272 MojoResult SharedBufferDispatcher::MapBuffer(
273 uint64_t offset,
274 uint64_t num_bytes,
275 std::unique_ptr<PlatformSharedMemoryMapping>* mapping) {
276 if (offset > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
277 return MOJO_RESULT_INVALID_ARGUMENT;
278 if (num_bytes > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
279 return MOJO_RESULT_INVALID_ARGUMENT;
280
281 base::AutoLock lock(lock_);
282 DCHECK(region_.IsValid());
283 if (in_transit_ || num_bytes == 0 ||
284 static_cast<size_t>(offset + num_bytes) > region_.GetSize()) {
285 return MOJO_RESULT_INVALID_ARGUMENT;
286 }
287
288 DCHECK(mapping);
289 *mapping = std::make_unique<PlatformSharedMemoryMapping>(
290 ®ion_, static_cast<size_t>(offset), static_cast<size_t>(num_bytes));
291 if (!(*mapping)->IsValid()) {
292 LOG(ERROR) << "Failed to map shared memory region.";
293 return MOJO_RESULT_RESOURCE_EXHAUSTED;
294 }
295
296 return MOJO_RESULT_OK;
297 }
298
GetBufferInfo(MojoSharedBufferInfo * info)299 MojoResult SharedBufferDispatcher::GetBufferInfo(MojoSharedBufferInfo* info) {
300 if (!info)
301 return MOJO_RESULT_INVALID_ARGUMENT;
302
303 base::AutoLock lock(lock_);
304 info->struct_size = sizeof(*info);
305 info->size = region_.GetSize();
306 return MOJO_RESULT_OK;
307 }
308
StartSerialize(uint32_t * num_bytes,uint32_t * num_ports,uint32_t * num_platform_handles)309 void SharedBufferDispatcher::StartSerialize(uint32_t* num_bytes,
310 uint32_t* num_ports,
311 uint32_t* num_platform_handles) {
312 *num_bytes = sizeof(SerializedState);
313 *num_ports = 0;
314 *num_platform_handles = 1;
315 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
316 (!defined(OS_MACOSX) || defined(OS_IOS))
317 if (region_.GetMode() ==
318 base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
319 *num_platform_handles = 2;
320 }
321 #endif
322 }
323
EndSerialize(void * destination,ports::PortName * ports,PlatformHandle * handles)324 bool SharedBufferDispatcher::EndSerialize(void* destination,
325 ports::PortName* ports,
326 PlatformHandle* handles) {
327 SerializedState* serialized_state =
328 static_cast<SerializedState*>(destination);
329 base::AutoLock lock(lock_);
330 serialized_state->num_bytes = region_.GetSize();
331 switch (region_.GetMode()) {
332 case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly:
333 serialized_state->access_mode =
334 MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY;
335 break;
336 case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable:
337 serialized_state->access_mode =
338 MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE;
339 break;
340 case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe:
341 serialized_state->access_mode =
342 MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE;
343 break;
344 default:
345 NOTREACHED();
346 return false;
347 }
348
349 const base::UnguessableToken& guid = region_.GetGUID();
350 serialized_state->guid_high = guid.GetHighForSerialization();
351 serialized_state->guid_low = guid.GetLowForSerialization();
352 serialized_state->padding = 0;
353
354 auto region = std::move(region_);
355 #if defined(OS_POSIX) && !defined(OS_ANDROID) && \
356 (!defined(OS_MACOSX) || defined(OS_IOS))
357 if (region.GetMode() ==
358 base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
359 PlatformHandle platform_handles[2];
360 ExtractPlatformHandlesFromSharedMemoryRegionHandle(
361 region.PassPlatformHandle(), &platform_handles[0],
362 &platform_handles[1]);
363 handles[0] = std::move(platform_handles[0]);
364 handles[1] = std::move(platform_handles[1]);
365 return true;
366 }
367 #endif
368
369 PlatformHandle platform_handle;
370 PlatformHandle ignored_handle;
371 ExtractPlatformHandlesFromSharedMemoryRegionHandle(
372 region.PassPlatformHandle(), &platform_handle, &ignored_handle);
373 handles[0] = std::move(platform_handle);
374 return true;
375 }
376
BeginTransit()377 bool SharedBufferDispatcher::BeginTransit() {
378 base::AutoLock lock(lock_);
379 if (in_transit_)
380 return false;
381 in_transit_ = region_.IsValid();
382 return in_transit_;
383 }
384
CompleteTransitAndClose()385 void SharedBufferDispatcher::CompleteTransitAndClose() {
386 base::AutoLock lock(lock_);
387 in_transit_ = false;
388 region_ = base::subtle::PlatformSharedMemoryRegion();
389 }
390
CancelTransit()391 void SharedBufferDispatcher::CancelTransit() {
392 base::AutoLock lock(lock_);
393 in_transit_ = false;
394 }
395
SharedBufferDispatcher(base::subtle::PlatformSharedMemoryRegion region)396 SharedBufferDispatcher::SharedBufferDispatcher(
397 base::subtle::PlatformSharedMemoryRegion region)
398 : region_(std::move(region)) {
399 DCHECK(region_.IsValid());
400 }
401
~SharedBufferDispatcher()402 SharedBufferDispatcher::~SharedBufferDispatcher() {
403 DCHECK(!region_.IsValid() && !in_transit_);
404 }
405
406 // static
CreateInternal(base::subtle::PlatformSharedMemoryRegion region)407 scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::CreateInternal(
408 base::subtle::PlatformSharedMemoryRegion region) {
409 return base::WrapRefCounted(new SharedBufferDispatcher(std::move(region)));
410 }
411
412 // static
ValidateDuplicateOptions(const MojoDuplicateBufferHandleOptions * in_options,MojoDuplicateBufferHandleOptions * out_options)413 MojoResult SharedBufferDispatcher::ValidateDuplicateOptions(
414 const MojoDuplicateBufferHandleOptions* in_options,
415 MojoDuplicateBufferHandleOptions* out_options) {
416 const MojoDuplicateBufferHandleFlags kKnownFlags =
417 MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY;
418 static const MojoDuplicateBufferHandleOptions kDefaultOptions = {
419 static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)),
420 MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_NONE};
421
422 *out_options = kDefaultOptions;
423 if (!in_options)
424 return MOJO_RESULT_OK;
425
426 UserOptionsReader<MojoDuplicateBufferHandleOptions> reader(in_options);
427 if (!reader.is_valid())
428 return MOJO_RESULT_INVALID_ARGUMENT;
429
430 if (!OPTIONS_STRUCT_HAS_MEMBER(MojoDuplicateBufferHandleOptions, flags,
431 reader))
432 return MOJO_RESULT_OK;
433 if ((reader.options().flags & ~kKnownFlags))
434 return MOJO_RESULT_UNIMPLEMENTED;
435 out_options->flags = reader.options().flags;
436
437 // Checks for fields beyond |flags|:
438
439 // (Nothing here yet.)
440
441 return MOJO_RESULT_OK;
442 }
443
444 } // namespace core
445 } // namespace mojo
446