// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "mojo/public/cpp/system/platform_handle.h" #include "base/memory/platform_shared_memory_region.h" #include "base/numerics/safe_conversions.h" #include "build/build_config.h" #if defined(OS_MACOSX) && !defined(OS_IOS) #include #include "base/mac/mach_logging.h" #endif namespace mojo { namespace { uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) { #if defined(OS_WIN) return reinterpret_cast(file); #else return static_cast(file); #endif } base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) { #if defined(OS_WIN) return reinterpret_cast(value); #else return static_cast(value); #endif } ScopedSharedBufferHandle WrapPlatformSharedMemoryRegion( base::subtle::PlatformSharedMemoryRegion region) { if (!region.IsValid()) return ScopedSharedBufferHandle(); MojoPlatformSharedMemoryRegionAccessMode access_mode; switch (region.GetMode()) { case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly: access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY; break; case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable: access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE; break; case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe: access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE; break; default: NOTREACHED(); return ScopedSharedBufferHandle(); } base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle handle = region.PassPlatformHandle(); MojoPlatformHandle platform_handles[2]; uint32_t num_platform_handles = 1; platform_handles[0].struct_size = sizeof(platform_handles[0]); #if defined(OS_WIN) platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE; platform_handles[0].value = reinterpret_cast(handle.Take()); #elif defined(OS_FUCHSIA) platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE; platform_handles[0].value = static_cast(handle.release()); #elif defined(OS_MACOSX) && !defined(OS_IOS) platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT; platform_handles[0].value = static_cast(handle.release()); #elif defined(OS_ANDROID) platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; platform_handles[0].value = static_cast(handle.release()); #else platform_handles[0].type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; platform_handles[0].value = static_cast(handle.fd.release()); if (region.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { num_platform_handles = 2; platform_handles[1].struct_size = sizeof(platform_handles[1]); platform_handles[1].type = MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR; platform_handles[1].value = static_cast(handle.readonly_fd.release()); } #endif const auto& guid = region.GetGUID(); MojoSharedBufferGuid mojo_guid = {guid.GetHighForSerialization(), guid.GetLowForSerialization()}; MojoHandle mojo_handle; MojoResult result = MojoWrapPlatformSharedMemoryRegion( platform_handles, num_platform_handles, region.GetSize(), &mojo_guid, access_mode, nullptr, &mojo_handle); if (result != MOJO_RESULT_OK) return ScopedSharedBufferHandle(); return ScopedSharedBufferHandle(SharedBufferHandle(mojo_handle)); } base::subtle::PlatformSharedMemoryRegion UnwrapPlatformSharedMemoryRegion( ScopedSharedBufferHandle mojo_handle) { if (!mojo_handle.is_valid()) return base::subtle::PlatformSharedMemoryRegion(); MojoPlatformHandle platform_handles[2]; platform_handles[0].struct_size = sizeof(platform_handles[0]); platform_handles[1].struct_size = sizeof(platform_handles[1]); uint32_t num_platform_handles = 2; uint64_t size; MojoSharedBufferGuid mojo_guid; MojoPlatformSharedMemoryRegionAccessMode access_mode; MojoResult result = MojoUnwrapPlatformSharedMemoryRegion( mojo_handle.release().value(), nullptr, platform_handles, &num_platform_handles, &size, &mojo_guid, &access_mode); if (result != MOJO_RESULT_OK) return base::subtle::PlatformSharedMemoryRegion(); base::subtle::PlatformSharedMemoryRegion::ScopedPlatformHandle region_handle; #if defined(OS_WIN) if (num_platform_handles != 1) return base::subtle::PlatformSharedMemoryRegion(); if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE) return base::subtle::PlatformSharedMemoryRegion(); region_handle.Set(reinterpret_cast(platform_handles[0].value)); #elif defined(OS_FUCHSIA) if (num_platform_handles != 1) return base::subtle::PlatformSharedMemoryRegion(); if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE) return base::subtle::PlatformSharedMemoryRegion(); region_handle.reset(static_cast(platform_handles[0].value)); #elif defined(OS_MACOSX) && !defined(OS_IOS) if (num_platform_handles != 1) return base::subtle::PlatformSharedMemoryRegion(); if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT) return base::subtle::PlatformSharedMemoryRegion(); region_handle.reset(static_cast(platform_handles[0].value)); #elif defined(OS_ANDROID) if (num_platform_handles != 1) return base::subtle::PlatformSharedMemoryRegion(); if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR) return base::subtle::PlatformSharedMemoryRegion(); region_handle.reset(static_cast(platform_handles[0].value)); #else if (access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) { if (num_platform_handles != 2) return base::subtle::PlatformSharedMemoryRegion(); } else if (num_platform_handles != 1) { return base::subtle::PlatformSharedMemoryRegion(); } if (platform_handles[0].type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR) return base::subtle::PlatformSharedMemoryRegion(); region_handle.fd.reset(static_cast(platform_handles[0].value)); if (num_platform_handles == 2) { if (platform_handles[1].type != MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR) return base::subtle::PlatformSharedMemoryRegion(); region_handle.readonly_fd.reset( static_cast(platform_handles[1].value)); } #endif base::subtle::PlatformSharedMemoryRegion::Mode mode; switch (access_mode) { case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY: mode = base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly; break; case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE: mode = base::subtle::PlatformSharedMemoryRegion::Mode::kWritable; break; case MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE: mode = base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe; break; default: return base::subtle::PlatformSharedMemoryRegion(); } return base::subtle::PlatformSharedMemoryRegion::Take( std::move(region_handle), mode, size, base::UnguessableToken::Deserialize(mojo_guid.high, mojo_guid.low)); } } // namespace ScopedHandle WrapPlatformHandle(PlatformHandle handle) { MojoPlatformHandle platform_handle; PlatformHandle::ToMojoPlatformHandle(std::move(handle), &platform_handle); MojoHandle wrapped_handle; MojoResult result = MojoWrapPlatformHandle(&platform_handle, nullptr, &wrapped_handle); if (result != MOJO_RESULT_OK) return ScopedHandle(); return ScopedHandle(Handle(wrapped_handle)); } PlatformHandle UnwrapPlatformHandle(ScopedHandle handle) { MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(platform_handle); MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(), nullptr, &platform_handle); if (result != MOJO_RESULT_OK) return PlatformHandle(); return PlatformHandle::FromMojoPlatformHandle(&platform_handle); } // Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object. ScopedHandle WrapPlatformFile(base::PlatformFile platform_file) { MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(MojoPlatformHandle); platform_handle.type = kPlatformFileHandleType; platform_handle.value = PlatformHandleValueFromPlatformFile(platform_file); MojoHandle mojo_handle; MojoResult result = MojoWrapPlatformHandle(&platform_handle, nullptr, &mojo_handle); CHECK_EQ(result, MOJO_RESULT_OK); return ScopedHandle(Handle(mojo_handle)); } MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file) { MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(MojoPlatformHandle); MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(), nullptr, &platform_handle); if (result != MOJO_RESULT_OK) return result; if (platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_INVALID) { *file = base::kInvalidPlatformFile; } else { CHECK_EQ(platform_handle.type, kPlatformFileHandleType); *file = PlatformFileFromPlatformHandleValue(platform_handle.value); } return MOJO_RESULT_OK; } ScopedSharedBufferHandle WrapSharedMemoryHandle( const base::SharedMemoryHandle& memory_handle, size_t size, UnwrappedSharedMemoryHandleProtection protection) { if (!memory_handle.IsValid()) return ScopedSharedBufferHandle(); MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(MojoPlatformHandle); platform_handle.type = kPlatformSharedBufferHandleType; #if defined(OS_MACOSX) && !defined(OS_IOS) platform_handle.value = static_cast(memory_handle.GetMemoryObject()); #else platform_handle.value = PlatformHandleValueFromPlatformFile(memory_handle.GetHandle()); #endif MojoPlatformSharedMemoryRegionAccessMode access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE; if (protection == UnwrappedSharedMemoryHandleProtection::kReadOnly) { access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY; #if defined(OS_ANDROID) // Many callers assume that base::SharedMemory::GetReadOnlyHandle() gives // them a handle which is actually read-only. This assumption is invalid on // Android. As a precursor to migrating all base::SharedMemory usage -- // including Mojo internals -- to the new base shared memory API, we ensure // that regions are set to read-only if any of their handles are wrapped // read-only. This relies on existing usages not attempting to map the // region writable any time after this call. if (!memory_handle.IsRegionReadOnly()) memory_handle.SetRegionReadOnly(); #endif } MojoSharedBufferGuid guid; guid.high = memory_handle.GetGUID().GetHighForSerialization(); guid.low = memory_handle.GetGUID().GetLowForSerialization(); MojoHandle mojo_handle; MojoResult result = MojoWrapPlatformSharedMemoryRegion( &platform_handle, 1, size, &guid, access_mode, nullptr, &mojo_handle); CHECK_EQ(result, MOJO_RESULT_OK); return ScopedSharedBufferHandle(SharedBufferHandle(mojo_handle)); } MojoResult UnwrapSharedMemoryHandle( ScopedSharedBufferHandle handle, base::SharedMemoryHandle* memory_handle, size_t* size, UnwrappedSharedMemoryHandleProtection* protection) { if (!handle.is_valid()) return MOJO_RESULT_INVALID_ARGUMENT; MojoPlatformHandle platform_handles[2]; platform_handles[0].struct_size = sizeof(platform_handles[0]); platform_handles[1].struct_size = sizeof(platform_handles[1]); uint32_t num_platform_handles = 2; uint64_t num_bytes; MojoSharedBufferGuid mojo_guid; MojoPlatformSharedMemoryRegionAccessMode access_mode; MojoResult result = MojoUnwrapPlatformSharedMemoryRegion( handle.release().value(), nullptr, platform_handles, &num_platform_handles, &num_bytes, &mojo_guid, &access_mode); if (result != MOJO_RESULT_OK) return result; if (size) { DCHECK(base::IsValueInRangeForNumericType(num_bytes)); *size = static_cast(num_bytes); } if (protection) { *protection = access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY ? UnwrappedSharedMemoryHandleProtection::kReadOnly : UnwrappedSharedMemoryHandleProtection::kReadWrite; } base::UnguessableToken guid = base::UnguessableToken::Deserialize(mojo_guid.high, mojo_guid.low); #if defined(OS_MACOSX) && !defined(OS_IOS) DCHECK_EQ(platform_handles[0].type, MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT); DCHECK_EQ(num_platform_handles, 1u); *memory_handle = base::SharedMemoryHandle( static_cast(platform_handles[0].value), num_bytes, guid); #elif defined(OS_FUCHSIA) DCHECK_EQ(platform_handles[0].type, MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE); DCHECK_EQ(num_platform_handles, 1u); *memory_handle = base::SharedMemoryHandle( static_cast(platform_handles[0].value), num_bytes, guid); #elif defined(OS_POSIX) DCHECK_EQ(platform_handles[0].type, MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR); *memory_handle = base::SharedMemoryHandle( base::FileDescriptor(static_cast(platform_handles[0].value), false), num_bytes, guid); #if !defined(OS_ANDROID) if (access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) { DCHECK_EQ(num_platform_handles, 2u); // When unwrapping as a base::SharedMemoryHandle, make sure to discard the // extra file descriptor if the region is writable. base::SharedMemoryHandle // effectively only supports read-only and unsafe usage modes when wrapping // or unwrapping to and from Mojo handles. base::ScopedFD discarded_readonly_fd( static_cast(platform_handles[1].value)); } else { DCHECK_EQ(num_platform_handles, 1u); } #else // !defined(OS_ANDROID) DCHECK_EQ(num_platform_handles, 1u); #endif // !defined(OS_ANDROID) #elif defined(OS_WIN) DCHECK_EQ(platform_handles[0].type, MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE); DCHECK_EQ(num_platform_handles, 1u); *memory_handle = base::SharedMemoryHandle( reinterpret_cast(platform_handles[0].value), num_bytes, guid); #endif return MOJO_RESULT_OK; } ScopedSharedBufferHandle WrapReadOnlySharedMemoryRegion( base::ReadOnlySharedMemoryRegion region) { return WrapPlatformSharedMemoryRegion( base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization( std::move(region))); } ScopedSharedBufferHandle WrapUnsafeSharedMemoryRegion( base::UnsafeSharedMemoryRegion region) { return WrapPlatformSharedMemoryRegion( base::UnsafeSharedMemoryRegion::TakeHandleForSerialization( std::move(region))); } ScopedSharedBufferHandle WrapWritableSharedMemoryRegion( base::WritableSharedMemoryRegion region) { return WrapPlatformSharedMemoryRegion( base::WritableSharedMemoryRegion::TakeHandleForSerialization( std::move(region))); } base::ReadOnlySharedMemoryRegion UnwrapReadOnlySharedMemoryRegion( ScopedSharedBufferHandle handle) { return base::ReadOnlySharedMemoryRegion::Deserialize( UnwrapPlatformSharedMemoryRegion(std::move(handle))); } base::UnsafeSharedMemoryRegion UnwrapUnsafeSharedMemoryRegion( ScopedSharedBufferHandle handle) { return base::UnsafeSharedMemoryRegion::Deserialize( UnwrapPlatformSharedMemoryRegion(std::move(handle))); } base::WritableSharedMemoryRegion UnwrapWritableSharedMemoryRegion( ScopedSharedBufferHandle handle) { return base::WritableSharedMemoryRegion::Deserialize( UnwrapPlatformSharedMemoryRegion(std::move(handle))); } #if defined(OS_MACOSX) && !defined(OS_IOS) ScopedHandle WrapMachPort(mach_port_t port) { kern_return_t kr = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1); MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "MachPortAttachmentMac mach_port_mod_refs"; if (kr != KERN_SUCCESS) return ScopedHandle(); MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(MojoPlatformHandle); platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT; platform_handle.value = static_cast(port); MojoHandle mojo_handle; MojoResult result = MojoWrapPlatformHandle(&platform_handle, nullptr, &mojo_handle); CHECK_EQ(result, MOJO_RESULT_OK); return ScopedHandle(Handle(mojo_handle)); } MojoResult UnwrapMachPort(ScopedHandle handle, mach_port_t* port) { MojoPlatformHandle platform_handle; platform_handle.struct_size = sizeof(MojoPlatformHandle); MojoResult result = MojoUnwrapPlatformHandle(handle.release().value(), nullptr, &platform_handle); if (result != MOJO_RESULT_OK) return result; CHECK(platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT || platform_handle.type == MOJO_PLATFORM_HANDLE_TYPE_INVALID); *port = static_cast(platform_handle.value); return MOJO_RESULT_OK; } #endif // defined(OS_MACOSX) && !defined(OS_IOS) } // namespace mojo