// Copyright 2014 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/core/shared_buffer_dispatcher.h" #include <stddef.h> #include <stdint.h> #include <limits> #include <memory> #include <utility> #include "base/logging.h" #include "base/memory/ptr_util.h" #include "build/build_config.h" #include "mojo/core/configuration.h" #include "mojo/core/node_controller.h" #include "mojo/core/options_validation.h" #include "mojo/core/platform_handle_utils.h" #include "mojo/core/platform_shared_memory_mapping.h" #include "mojo/public/c/system/platform_handle.h" namespace mojo { namespace core { namespace { #pragma pack(push, 1) struct SerializedState { uint64_t num_bytes; uint32_t access_mode; uint64_t guid_high; uint64_t guid_low; uint32_t padding; }; #pragma pack(pop) static_assert(sizeof(SerializedState) % 8 == 0, "Invalid SerializedState size."); } // namespace // static const MojoCreateSharedBufferOptions SharedBufferDispatcher::kDefaultCreateOptions = { static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)), MOJO_CREATE_SHARED_BUFFER_FLAG_NONE}; // static MojoResult SharedBufferDispatcher::ValidateCreateOptions( const MojoCreateSharedBufferOptions* in_options, MojoCreateSharedBufferOptions* out_options) { const MojoCreateSharedBufferFlags kKnownFlags = MOJO_CREATE_SHARED_BUFFER_FLAG_NONE; *out_options = kDefaultCreateOptions; if (!in_options) return MOJO_RESULT_OK; UserOptionsReader<MojoCreateSharedBufferOptions> reader(in_options); if (!reader.is_valid()) return MOJO_RESULT_INVALID_ARGUMENT; if (!OPTIONS_STRUCT_HAS_MEMBER(MojoCreateSharedBufferOptions, flags, reader)) return MOJO_RESULT_OK; if ((reader.options().flags & ~kKnownFlags)) return MOJO_RESULT_UNIMPLEMENTED; out_options->flags = reader.options().flags; // Checks for fields beyond |flags|: // (Nothing here yet.) return MOJO_RESULT_OK; } // static MojoResult SharedBufferDispatcher::Create( const MojoCreateSharedBufferOptions& /*validated_options*/, NodeController* node_controller, uint64_t num_bytes, scoped_refptr<SharedBufferDispatcher>* result) { if (!num_bytes) return MOJO_RESULT_INVALID_ARGUMENT; if (num_bytes > GetConfiguration().max_shared_memory_num_bytes) return MOJO_RESULT_RESOURCE_EXHAUSTED; base::WritableSharedMemoryRegion writable_region; if (node_controller) { writable_region = node_controller->CreateSharedBuffer(static_cast<size_t>(num_bytes)); } else { writable_region = base::WritableSharedMemoryRegion::Create( static_cast<size_t>(num_bytes)); } if (!writable_region.IsValid()) return MOJO_RESULT_RESOURCE_EXHAUSTED; *result = CreateInternal( base::WritableSharedMemoryRegion::TakeHandleForSerialization( std::move(writable_region))); return MOJO_RESULT_OK; } // static MojoResult SharedBufferDispatcher::CreateFromPlatformSharedMemoryRegion( base::subtle::PlatformSharedMemoryRegion region, scoped_refptr<SharedBufferDispatcher>* result) { if (!region.IsValid()) return MOJO_RESULT_INVALID_ARGUMENT; *result = CreateInternal(std::move(region)); return MOJO_RESULT_OK; } // static scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize( const void* bytes, size_t num_bytes, const ports::PortName* ports, size_t num_ports, PlatformHandle* platform_handles, size_t num_platform_handles) { if (num_bytes != sizeof(SerializedState)) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)"; return nullptr; } const SerializedState* serialized_state = static_cast<const SerializedState*>(bytes); if (!serialized_state->num_bytes) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (invalid num_bytes)"; return nullptr; } if (num_ports) return nullptr; PlatformHandle handles[2]; #if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (serialized_state->access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) { if (num_platform_handles != 2) return nullptr; handles[1] = std::move(platform_handles[1]); } else { if (num_platform_handles != 1) return nullptr; } #else if (num_platform_handles != 1) return nullptr; #endif handles[0] = std::move(platform_handles[0]); base::UnguessableToken guid = base::UnguessableToken::Deserialize( serialized_state->guid_high, serialized_state->guid_low); base::subtle::PlatformSharedMemoryRegion::Mode mode; switch (serialized_state->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: LOG(ERROR) << "Invalid serialized shared buffer access mode."; return nullptr; } auto region = base::subtle::PlatformSharedMemoryRegion::Take( CreateSharedMemoryRegionHandleFromPlatformHandles(std::move(handles[0]), std::move(handles[1])), mode, static_cast<size_t>(serialized_state->num_bytes), guid); if (!region.IsValid()) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)"; return nullptr; } return CreateInternal(std::move(region)); } base::subtle::PlatformSharedMemoryRegion SharedBufferDispatcher::PassPlatformSharedMemoryRegion() { base::AutoLock lock(lock_); if (!region_.IsValid() || in_transit_) return base::subtle::PlatformSharedMemoryRegion(); return std::move(region_); } Dispatcher::Type SharedBufferDispatcher::GetType() const { return Type::SHARED_BUFFER; } MojoResult SharedBufferDispatcher::Close() { base::AutoLock lock(lock_); if (in_transit_) return MOJO_RESULT_INVALID_ARGUMENT; region_ = base::subtle::PlatformSharedMemoryRegion(); return MOJO_RESULT_OK; } MojoResult SharedBufferDispatcher::DuplicateBufferHandle( const MojoDuplicateBufferHandleOptions* options, scoped_refptr<Dispatcher>* new_dispatcher) { MojoDuplicateBufferHandleOptions validated_options; MojoResult result = ValidateDuplicateOptions(options, &validated_options); if (result != MOJO_RESULT_OK) return result; base::AutoLock lock(lock_); if (in_transit_) return MOJO_RESULT_INVALID_ARGUMENT; if ((validated_options.flags & MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY)) { // If a read-only duplicate is requested and this handle is not already // read-only, we need to make it read-only before duplicating. If it's // unsafe it can't be made read-only, and we must fail instead. if (region_.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe) { return MOJO_RESULT_FAILED_PRECONDITION; } else if (region_.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { region_ = base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization( base::WritableSharedMemoryRegion::ConvertToReadOnly( base::WritableSharedMemoryRegion::Deserialize( std::move(region_)))); } DCHECK_EQ(region_.GetMode(), base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly); } else { // A writable duplicate was requested. If this is already a read-only handle // we have to reject. Otherwise we have to convert to unsafe to ensure that // no future read-only duplication requests can succeed. if (region_.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly) { return MOJO_RESULT_FAILED_PRECONDITION; } else if (region_.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { auto handle = region_.PassPlatformHandle(); #if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) // On POSIX systems excluding Android, Fuchsia, and OSX, we explicitly // wipe out the secondary (read-only) FD from the platform handle to // repurpose it for exclusive unsafe usage. handle.readonly_fd.reset(); #endif region_ = base::subtle::PlatformSharedMemoryRegion::Take( std::move(handle), base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe, region_.GetSize(), region_.GetGUID()); } } *new_dispatcher = CreateInternal(region_.Duplicate()); return MOJO_RESULT_OK; } MojoResult SharedBufferDispatcher::MapBuffer( uint64_t offset, uint64_t num_bytes, std::unique_ptr<PlatformSharedMemoryMapping>* mapping) { if (offset > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) return MOJO_RESULT_INVALID_ARGUMENT; if (num_bytes > static_cast<uint64_t>(std::numeric_limits<size_t>::max())) return MOJO_RESULT_INVALID_ARGUMENT; base::AutoLock lock(lock_); DCHECK(region_.IsValid()); if (in_transit_ || num_bytes == 0 || static_cast<size_t>(offset + num_bytes) > region_.GetSize()) { return MOJO_RESULT_INVALID_ARGUMENT; } DCHECK(mapping); *mapping = std::make_unique<PlatformSharedMemoryMapping>( ®ion_, static_cast<size_t>(offset), static_cast<size_t>(num_bytes)); if (!(*mapping)->IsValid()) { LOG(ERROR) << "Failed to map shared memory region."; return MOJO_RESULT_RESOURCE_EXHAUSTED; } return MOJO_RESULT_OK; } MojoResult SharedBufferDispatcher::GetBufferInfo(MojoSharedBufferInfo* info) { if (!info) return MOJO_RESULT_INVALID_ARGUMENT; base::AutoLock lock(lock_); info->struct_size = sizeof(*info); info->size = region_.GetSize(); return MOJO_RESULT_OK; } void SharedBufferDispatcher::StartSerialize(uint32_t* num_bytes, uint32_t* num_ports, uint32_t* num_platform_handles) { *num_bytes = sizeof(SerializedState); *num_ports = 0; *num_platform_handles = 1; #if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (region_.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { *num_platform_handles = 2; } #endif } bool SharedBufferDispatcher::EndSerialize(void* destination, ports::PortName* ports, PlatformHandle* handles) { SerializedState* serialized_state = static_cast<SerializedState*>(destination); base::AutoLock lock(lock_); serialized_state->num_bytes = region_.GetSize(); switch (region_.GetMode()) { case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly: serialized_state->access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY; break; case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable: serialized_state->access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE; break; case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe: serialized_state->access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE; break; default: NOTREACHED(); return false; } const base::UnguessableToken& guid = region_.GetGUID(); serialized_state->guid_high = guid.GetHighForSerialization(); serialized_state->guid_low = guid.GetLowForSerialization(); serialized_state->padding = 0; auto region = std::move(region_); #if defined(OS_POSIX) && !defined(OS_ANDROID) && \ (!defined(OS_MACOSX) || defined(OS_IOS)) if (region.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { PlatformHandle platform_handles[2]; ExtractPlatformHandlesFromSharedMemoryRegionHandle( region.PassPlatformHandle(), &platform_handles[0], &platform_handles[1]); handles[0] = std::move(platform_handles[0]); handles[1] = std::move(platform_handles[1]); return true; } #endif PlatformHandle platform_handle; PlatformHandle ignored_handle; ExtractPlatformHandlesFromSharedMemoryRegionHandle( region.PassPlatformHandle(), &platform_handle, &ignored_handle); handles[0] = std::move(platform_handle); return true; } bool SharedBufferDispatcher::BeginTransit() { base::AutoLock lock(lock_); if (in_transit_) return false; in_transit_ = region_.IsValid(); return in_transit_; } void SharedBufferDispatcher::CompleteTransitAndClose() { base::AutoLock lock(lock_); in_transit_ = false; region_ = base::subtle::PlatformSharedMemoryRegion(); } void SharedBufferDispatcher::CancelTransit() { base::AutoLock lock(lock_); in_transit_ = false; } SharedBufferDispatcher::SharedBufferDispatcher( base::subtle::PlatformSharedMemoryRegion region) : region_(std::move(region)) { DCHECK(region_.IsValid()); } SharedBufferDispatcher::~SharedBufferDispatcher() { DCHECK(!region_.IsValid() && !in_transit_); } // static scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::CreateInternal( base::subtle::PlatformSharedMemoryRegion region) { return base::WrapRefCounted(new SharedBufferDispatcher(std::move(region))); } // static MojoResult SharedBufferDispatcher::ValidateDuplicateOptions( const MojoDuplicateBufferHandleOptions* in_options, MojoDuplicateBufferHandleOptions* out_options) { const MojoDuplicateBufferHandleFlags kKnownFlags = MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY; static const MojoDuplicateBufferHandleOptions kDefaultOptions = { static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)), MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_NONE}; *out_options = kDefaultOptions; if (!in_options) return MOJO_RESULT_OK; UserOptionsReader<MojoDuplicateBufferHandleOptions> reader(in_options); if (!reader.is_valid()) return MOJO_RESULT_INVALID_ARGUMENT; if (!OPTIONS_STRUCT_HAS_MEMBER(MojoDuplicateBufferHandleOptions, flags, reader)) return MOJO_RESULT_OK; if ((reader.options().flags & ~kKnownFlags)) return MOJO_RESULT_UNIMPLEMENTED; out_options->flags = reader.options().flags; // Checks for fields beyond |flags|: // (Nothing here yet.) return MOJO_RESULT_OK; } } // namespace core } // namespace mojo