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/public/cpp/system/platform_handle.h"
6
7 #include "base/memory/platform_shared_memory_region.h"
8 #include "base/numerics/safe_conversions.h"
9 #include "build/build_config.h"
10
11 #if defined(OS_MACOSX) && !defined(OS_IOS)
12 #include <mach/mach.h>
13 #include "base/mac/mach_logging.h"
14 #endif
15
16 namespace mojo {
17
18 namespace {
19
PlatformHandleValueFromPlatformFile(base::PlatformFile file)20 uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) {
21 #if defined(OS_WIN)
22 return reinterpret_cast<uint64_t>(file);
23 #else
24 return static_cast<uint64_t>(file);
25 #endif
26 }
27
PlatformFileFromPlatformHandleValue(uint64_t value)28 base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) {
29 #if defined(OS_WIN)
30 return reinterpret_cast<base::PlatformFile>(value);
31 #else
32 return static_cast<base::PlatformFile>(value);
33 #endif
34 }
35
WrapPlatformSharedMemoryRegion(base::subtle::PlatformSharedMemoryRegion region)36 ScopedSharedBufferHandle WrapPlatformSharedMemoryRegion(
37 base::subtle::PlatformSharedMemoryRegion region) {
38 if (!region.IsValid())
39 return ScopedSharedBufferHandle();
40
41 MojoPlatformSharedMemoryRegionAccessMode access_mode;
42 switch (region.GetMode()) {
43 case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly:
44 access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY;
45 break;
46 case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable:
47 access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE;
48 break;
49 case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe:
50 access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE;
51 break;
52 default:
53 NOTREACHED();
54 return ScopedSharedBufferHandle();
55 }
56
57 base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle handle =
58 region.PassPlatformHandle();
59 MojoPlatformHandle platform_handles[2];
60 uint32_t num_platform_handles = 1;
61 platform_handles[0].struct_size = sizeof(platform_handles[0]);
62 #if defined(OS_WIN)
63 platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE;
64 platform_handles[0].value = reinterpret_cast<uint64_t>(handle.Take());
65 #elif defined(OS_FUCHSIA)
66 platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE;
67 platform_handles[0].value = static_cast<uint64_t>(handle.release());
68 #elif defined(OS_MACOSX) && !defined(OS_IOS)
69 platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
70 platform_handles[0].value = static_cast<uint64_t>(handle.release());
71 #elif defined(OS_ANDROID)
72 platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
73 platform_handles[0].value = static_cast<uint64_t>(handle.release());
74 #else
75 platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
76 platform_handles[0].value = static_cast<uint64_t>(handle.fd.release());
77
78 if (region.GetMode() ==
79 base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
80 num_platform_handles = 2;
81 platform_handles[1].struct_size = sizeof(platform_handles[1]);
82 platform_handles[1].type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR;
83 platform_handles[1].value =
84 static_cast<uint64_t>(handle.readonly_fd.release());
85 }
86 #endif
87 const auto& guid = region.GetGUID();
88 MojoSharedBufferGuid mojo_guid = {guid.GetHighForSerialization(),
89 guid.GetLowForSerialization()};
90 MojoHandle mojo_handle;
91 MojoResult result = MojoWrapPlatformSharedMemoryRegion(
92 platform_handles, num_platform_handles, region.GetSize(), &mojo_guid,
93 access_mode, nullptr, &mojo_handle);
94 if (result != MOJO_RESULT_OK)
95 return ScopedSharedBufferHandle();
96 return ScopedSharedBufferHandle(SharedBufferHandle(mojo_handle));
97 }
98
UnwrapPlatformSharedMemoryRegion(ScopedSharedBufferHandle mojo_handle)99 base::subtle::PlatformSharedMemoryRegion UnwrapPlatformSharedMemoryRegion(
100 ScopedSharedBufferHandle mojo_handle) {
101 if (!mojo_handle.is_valid())
102 return base::subtle::PlatformSharedMemoryRegion();
103
104 MojoPlatformHandle platform_handles[2];
105 platform_handles[0].struct_size = sizeof(platform_handles[0]);
106 platform_handles[1].struct_size = sizeof(platform_handles[1]);
107 uint32_t num_platform_handles = 2;
108 uint64_t size;
109 MojoSharedBufferGuid mojo_guid;
110 MojoPlatformSharedMemoryRegionAccessMode access_mode;
111 MojoResult result = MojoUnwrapPlatformSharedMemoryRegion(
112 mojo_handle.release().value(), nullptr, platform_handles,
113 &num_platform_handles, &size, &mojo_guid, &access_mode);
114 if (result != MOJO_RESULT_OK)
115 return base::subtle::PlatformSharedMemoryRegion();
116
117 base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle region_handle;
118 #if defined(OS_WIN)
119 if (num_platform_handles != 1)
120 return base::subtle::PlatformSharedMemoryRegion();
121 if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE)
122 return base::subtle::PlatformSharedMemoryRegion();
123 region_handle.Set(reinterpret_cast<HANDLE>(platform_handles[0].value));
124 #elif defined(OS_FUCHSIA)
125 if (num_platform_handles != 1)
126 return base::subtle::PlatformSharedMemoryRegion();
127 if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE)
128 return base::subtle::PlatformSharedMemoryRegion();
129 region_handle.reset(static_cast<zx_handle_t>(platform_handles[0].value));
130 #elif defined(OS_MACOSX) && !defined(OS_IOS)
131 if (num_platform_handles != 1)
132 return base::subtle::PlatformSharedMemoryRegion();
133 if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT)
134 return base::subtle::PlatformSharedMemoryRegion();
135 region_handle.reset(static_cast<mach_port_t>(platform_handles[0].value));
136 #elif defined(OS_ANDROID)
137 if (num_platform_handles != 1)
138 return base::subtle::PlatformSharedMemoryRegion();
139 if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR)
140 return base::subtle::PlatformSharedMemoryRegion();
141 region_handle.reset(static_cast<int>(platform_handles[0].value));
142 #else
143 if (access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) {
144 if (num_platform_handles != 2)
145 return base::subtle::PlatformSharedMemoryRegion();
146 } else if (num_platform_handles != 1) {
147 return base::subtle::PlatformSharedMemoryRegion();
148 }
149 if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR)
150 return base::subtle::PlatformSharedMemoryRegion();
151 region_handle.fd.reset(static_cast<int>(platform_handles[0].value));
152 if (num_platform_handles == 2) {
153 if (platform_handles[1].type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR)
154 return base::subtle::PlatformSharedMemoryRegion();
155 region_handle.readonly_fd.reset(
156 static_cast<int>(platform_handles[1].value));
157 }
158 #endif
159
160 base::subtle::PlatformSharedMemoryRegion::Mode mode;
161 switch (access_mode) {
162 case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY:
163 mode = base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly;
164 break;
165 case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE:
166 mode = base::subtle::PlatformSharedMemoryRegion::Mode::kWritable;
167 break;
168 case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE:
169 mode = base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe;
170 break;
171 default:
172 return base::subtle::PlatformSharedMemoryRegion();
173 }
174
175 return base::subtle::PlatformSharedMemoryRegion::Take(
176 std::move(region_handle), mode, size,
177 base::UnguessableToken::Deserialize(mojo_guid.high, mojo_guid.low));
178 }
179
180 } // namespace
181
WrapPlatformHandle(PlatformHandle handle)182 ScopedHandle WrapPlatformHandle(PlatformHandle handle) {
183 MojoPlatformHandle platform_handle;
184 PlatformHandle::ToMojoPlatformHandle(std::move(handle), &platform_handle);
185
186 MojoHandle wrapped_handle;
187 MojoResult result =
188 MojoWrapPlatformHandle(&platform_handle, nullptr, &wrapped_handle);
189 if (result != MOJO_RESULT_OK)
190 return ScopedHandle();
191 return ScopedHandle(Handle(wrapped_handle));
192 }
193
UnwrapPlatformHandle(ScopedHandle handle)194 PlatformHandle UnwrapPlatformHandle(ScopedHandle handle) {
195 MojoPlatformHandle platform_handle;
196 platform_handle.struct_size = sizeof(platform_handle);
197 MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(),
198 nullptr, &platform_handle);
199 if (result != MOJO_RESULT_OK)
200 return PlatformHandle();
201 return PlatformHandle::FromMojoPlatformHandle(&platform_handle);
202 }
203
204 // Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object.
WrapPlatformFile(base::PlatformFile platform_file)205 ScopedHandle WrapPlatformFile(base::PlatformFile platform_file) {
206 MojoPlatformHandle platform_handle;
207 platform_handle.struct_size = sizeof(MojoPlatformHandle);
208 platform_handle.type = kPlatformFileHandleType;
209 platform_handle.value = PlatformHandleValueFromPlatformFile(platform_file);
210
211 MojoHandle mojo_handle;
212 MojoResult result =
213 MojoWrapPlatformHandle(&platform_handle, nullptr, &mojo_handle);
214 CHECK_EQ(result, MOJO_RESULT_OK);
215
216 return ScopedHandle(Handle(mojo_handle));
217 }
218
UnwrapPlatformFile(ScopedHandle handle,base::PlatformFile * file)219 MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file) {
220 MojoPlatformHandle platform_handle;
221 platform_handle.struct_size = sizeof(MojoPlatformHandle);
222 MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(),
223 nullptr, &platform_handle);
224 if (result != MOJO_RESULT_OK)
225 return result;
226
227 if (platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) {
228 *file = base::kInvalidPlatformFile;
229 } else {
230 CHECK_EQ(platform_handle.type, kPlatformFileHandleType);
231 *file = PlatformFileFromPlatformHandleValue(platform_handle.value);
232 }
233
234 return MOJO_RESULT_OK;
235 }
236
WrapSharedMemoryHandle(const base::SharedMemoryHandle & memory_handle,size_t size,UnwrappedSharedMemoryHandleProtection protection)237 ScopedSharedBufferHandle WrapSharedMemoryHandle(
238 const base::SharedMemoryHandle& memory_handle,
239 size_t size,
240 UnwrappedSharedMemoryHandleProtection protection) {
241 if (!memory_handle.IsValid())
242 return ScopedSharedBufferHandle();
243 MojoPlatformHandle platform_handle;
244 platform_handle.struct_size = sizeof(MojoPlatformHandle);
245 platform_handle.type = kPlatformSharedBufferHandleType;
246 #if defined(OS_MACOSX) && !defined(OS_IOS)
247 platform_handle.value =
248 static_cast<uint64_t>(memory_handle.GetMemoryObject());
249 #else
250 platform_handle.value =
251 PlatformHandleValueFromPlatformFile(memory_handle.GetHandle());
252 #endif
253
254 MojoPlatformSharedMemoryRegionAccessMode access_mode =
255 MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE;
256 if (protection == UnwrappedSharedMemoryHandleProtection::kReadOnly) {
257 access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY;
258
259 #if defined(OS_ANDROID)
260 // Many callers assume that base::SharedMemory::GetReadOnlyHandle() gives
261 // them a handle which is actually read-only. This assumption is invalid on
262 // Android. As a precursor to migrating all base::SharedMemory usage --
263 // including Mojo internals -- to the new base shared memory API, we ensure
264 // that regions are set to read-only if any of their handles are wrapped
265 // read-only. This relies on existing usages not attempting to map the
266 // region writable any time after this call.
267 if (!memory_handle.IsRegionReadOnly())
268 memory_handle.SetRegionReadOnly();
269 #endif
270 }
271
272 MojoSharedBufferGuid guid;
273 guid.high = memory_handle.GetGUID().GetHighForSerialization();
274 guid.low = memory_handle.GetGUID().GetLowForSerialization();
275 MojoHandle mojo_handle;
276 MojoResult result = MojoWrapPlatformSharedMemoryRegion(
277 &platform_handle, 1, size, &guid, access_mode, nullptr, &mojo_handle);
278 CHECK_EQ(result, MOJO_RESULT_OK);
279
280 return ScopedSharedBufferHandle(SharedBufferHandle(mojo_handle));
281 }
282
UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,base::SharedMemoryHandle * memory_handle,size_t * size,UnwrappedSharedMemoryHandleProtection * protection)283 MojoResult UnwrapSharedMemoryHandle(
284 ScopedSharedBufferHandle handle,
285 base::SharedMemoryHandle* memory_handle,
286 size_t* size,
287 UnwrappedSharedMemoryHandleProtection* protection) {
288 if (!handle.is_valid())
289 return MOJO_RESULT_INVALID_ARGUMENT;
290 MojoPlatformHandle platform_handles[2];
291 platform_handles[0].struct_size = sizeof(platform_handles[0]);
292 platform_handles[1].struct_size = sizeof(platform_handles[1]);
293
294 uint32_t num_platform_handles = 2;
295 uint64_t num_bytes;
296 MojoSharedBufferGuid mojo_guid;
297 MojoPlatformSharedMemoryRegionAccessMode access_mode;
298 MojoResult result = MojoUnwrapPlatformSharedMemoryRegion(
299 handle.release().value(), nullptr, platform_handles,
300 &num_platform_handles, &num_bytes, &mojo_guid, &access_mode);
301 if (result != MOJO_RESULT_OK)
302 return result;
303
304 if (size) {
305 DCHECK(base::IsValueInRangeForNumericType<size_t>(num_bytes));
306 *size = static_cast<size_t>(num_bytes);
307 }
308
309 if (protection) {
310 *protection =
311 access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY
312 ? UnwrappedSharedMemoryHandleProtection::kReadOnly
313 : UnwrappedSharedMemoryHandleProtection::kReadWrite;
314 }
315
316 base::UnguessableToken guid =
317 base::UnguessableToken::Deserialize(mojo_guid.high, mojo_guid.low);
318 #if defined(OS_MACOSX) && !defined(OS_IOS)
319 DCHECK_EQ(platform_handles[0].type, MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT);
320 DCHECK_EQ(num_platform_handles, 1u);
321 *memory_handle = base::SharedMemoryHandle(
322 static_cast<mach_port_t>(platform_handles[0].value), num_bytes, guid);
323 #elif defined(OS_FUCHSIA)
324 DCHECK_EQ(platform_handles[0].type, MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE);
325 DCHECK_EQ(num_platform_handles, 1u);
326 *memory_handle = base::SharedMemoryHandle(
327 static_cast<zx_handle_t>(platform_handles[0].value), num_bytes, guid);
328 #elif defined(OS_POSIX)
329 DCHECK_EQ(platform_handles[0].type,
330 MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR);
331 *memory_handle = base::SharedMemoryHandle(
332 base::FileDescriptor(static_cast<int>(platform_handles[0].value), false),
333 num_bytes, guid);
334 #if !defined(OS_ANDROID)
335 if (access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) {
336 DCHECK_EQ(num_platform_handles, 2u);
337 // When unwrapping as a base::SharedMemoryHandle, make sure to discard the
338 // extra file descriptor if the region is writable. base::SharedMemoryHandle
339 // effectively only supports read-only and unsafe usage modes when wrapping
340 // or unwrapping to and from Mojo handles.
341 base::ScopedFD discarded_readonly_fd(
342 static_cast<int>(platform_handles[1].value));
343 } else {
344 DCHECK_EQ(num_platform_handles, 1u);
345 }
346 #else // !defined(OS_ANDROID)
347 DCHECK_EQ(num_platform_handles, 1u);
348 #endif // !defined(OS_ANDROID)
349 #elif defined(OS_WIN)
350 DCHECK_EQ(platform_handles[0].type, MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE);
351 DCHECK_EQ(num_platform_handles, 1u);
352 *memory_handle = base::SharedMemoryHandle(
353 reinterpret_cast<HANDLE>(platform_handles[0].value), num_bytes, guid);
354 #endif
355
356 return MOJO_RESULT_OK;
357 }
358
WrapReadOnlySharedMemoryRegion(base::ReadOnlySharedMemoryRegion region)359 ScopedSharedBufferHandle WrapReadOnlySharedMemoryRegion(
360 base::ReadOnlySharedMemoryRegion region) {
361 return WrapPlatformSharedMemoryRegion(
362 base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
363 std::move(region)));
364 }
365
WrapUnsafeSharedMemoryRegion(base::UnsafeSharedMemoryRegion region)366 ScopedSharedBufferHandle WrapUnsafeSharedMemoryRegion(
367 base::UnsafeSharedMemoryRegion region) {
368 return WrapPlatformSharedMemoryRegion(
369 base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
370 std::move(region)));
371 }
372
WrapWritableSharedMemoryRegion(base::WritableSharedMemoryRegion region)373 ScopedSharedBufferHandle WrapWritableSharedMemoryRegion(
374 base::WritableSharedMemoryRegion region) {
375 return WrapPlatformSharedMemoryRegion(
376 base::WritableSharedMemoryRegion::TakeHandleForSerialization(
377 std::move(region)));
378 }
379
UnwrapReadOnlySharedMemoryRegion(ScopedSharedBufferHandle handle)380 base::ReadOnlySharedMemoryRegion UnwrapReadOnlySharedMemoryRegion(
381 ScopedSharedBufferHandle handle) {
382 return base::ReadOnlySharedMemoryRegion::Deserialize(
383 UnwrapPlatformSharedMemoryRegion(std::move(handle)));
384 }
385
UnwrapUnsafeSharedMemoryRegion(ScopedSharedBufferHandle handle)386 base::UnsafeSharedMemoryRegion UnwrapUnsafeSharedMemoryRegion(
387 ScopedSharedBufferHandle handle) {
388 return base::UnsafeSharedMemoryRegion::Deserialize(
389 UnwrapPlatformSharedMemoryRegion(std::move(handle)));
390 }
391
UnwrapWritableSharedMemoryRegion(ScopedSharedBufferHandle handle)392 base::WritableSharedMemoryRegion UnwrapWritableSharedMemoryRegion(
393 ScopedSharedBufferHandle handle) {
394 return base::WritableSharedMemoryRegion::Deserialize(
395 UnwrapPlatformSharedMemoryRegion(std::move(handle)));
396 }
397
398 #if defined(OS_MACOSX) && !defined(OS_IOS)
WrapMachPort(mach_port_t port)399 ScopedHandle WrapMachPort(mach_port_t port) {
400 kern_return_t kr =
401 mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1);
402 MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
403 << "MachPortAttachmentMac mach_port_mod_refs";
404 if (kr != KERN_SUCCESS)
405 return ScopedHandle();
406
407 MojoPlatformHandle platform_handle;
408 platform_handle.struct_size = sizeof(MojoPlatformHandle);
409 platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
410 platform_handle.value = static_cast<uint64_t>(port);
411
412 MojoHandle mojo_handle;
413 MojoResult result =
414 MojoWrapPlatformHandle(&platform_handle, nullptr, &mojo_handle);
415 CHECK_EQ(result, MOJO_RESULT_OK);
416
417 return ScopedHandle(Handle(mojo_handle));
418 }
419
UnwrapMachPort(ScopedHandle handle,mach_port_t * port)420 MojoResult UnwrapMachPort(ScopedHandle handle, mach_port_t* port) {
421 MojoPlatformHandle platform_handle;
422 platform_handle.struct_size = sizeof(MojoPlatformHandle);
423 MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(),
424 nullptr, &platform_handle);
425 if (result != MOJO_RESULT_OK)
426 return result;
427
428 CHECK(platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT ||
429 platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_INVALID);
430 *port = static_cast<mach_port_t>(platform_handle.value);
431 return MOJO_RESULT_OK;
432 }
433 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
434
435 } // namespace mojo
436