// 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/edk/system/shared_buffer_dispatcher.h" #include <stddef.h> #include <stdint.h> #include <limits> #include <memory> #include <utility> #include "base/logging.h" #include "mojo/edk/embedder/embedder_internal.h" #include "mojo/edk/system/configuration.h" #include "mojo/edk/system/node_controller.h" #include "mojo/edk/system/options_validation.h" namespace mojo { namespace edk { namespace { #pragma pack(push, 1) struct SerializedState { uint64_t num_bytes; uint32_t flags; uint32_t padding; }; const uint32_t kSerializedStateFlagsReadOnly = 1 << 0; #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_OPTIONS_FLAG_NONE}; // static MojoResult SharedBufferDispatcher::ValidateCreateOptions( const MojoCreateSharedBufferOptions* in_options, MojoCreateSharedBufferOptions* out_options) { const MojoCreateSharedBufferOptionsFlags kKnownFlags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_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; scoped_refptr<PlatformSharedBuffer> shared_buffer; if (node_controller) { shared_buffer = node_controller->CreateSharedBuffer(static_cast<size_t>(num_bytes)); } else { shared_buffer = PlatformSharedBuffer::Create(static_cast<size_t>(num_bytes)); } if (!shared_buffer) return MOJO_RESULT_RESOURCE_EXHAUSTED; *result = CreateInternal(std::move(shared_buffer)); return MOJO_RESULT_OK; } // static MojoResult SharedBufferDispatcher::CreateFromPlatformSharedBuffer( const scoped_refptr<PlatformSharedBuffer>& shared_buffer, scoped_refptr<SharedBufferDispatcher>* result) { if (!shared_buffer) return MOJO_RESULT_INVALID_ARGUMENT; *result = CreateInternal(shared_buffer); 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* serialization = static_cast<const SerializedState*>(bytes); if (!serialization->num_bytes) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (invalid num_bytes)"; return nullptr; } if (!platform_handles || num_platform_handles != 1 || num_ports) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (missing handles)"; return nullptr; } // Starts off invalid, which is what we want. PlatformHandle platform_handle; // We take ownership of the handle, so we have to invalidate the one in // |platform_handles|. std::swap(platform_handle, *platform_handles); // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be // closed even if creation fails. bool read_only = (serialization->flags & kSerializedStateFlagsReadOnly); scoped_refptr<PlatformSharedBuffer> shared_buffer( PlatformSharedBuffer::CreateFromPlatformHandle( static_cast<size_t>(serialization->num_bytes), read_only, ScopedPlatformHandle(platform_handle))); if (!shared_buffer) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)"; return nullptr; } return CreateInternal(std::move(shared_buffer)); } scoped_refptr<PlatformSharedBuffer> SharedBufferDispatcher::PassPlatformSharedBuffer() { base::AutoLock lock(lock_); if (!shared_buffer_ || in_transit_) return nullptr; scoped_refptr<PlatformSharedBuffer> retval = shared_buffer_; shared_buffer_ = nullptr; return retval; } Dispatcher::Type SharedBufferDispatcher::GetType() const { return Type::SHARED_BUFFER; } MojoResult SharedBufferDispatcher::Close() { base::AutoLock lock(lock_); if (in_transit_) return MOJO_RESULT_INVALID_ARGUMENT; shared_buffer_ = nullptr; 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; // Note: Since this is "duplicate", we keep our ref to |shared_buffer_|. base::AutoLock lock(lock_); if (in_transit_) return MOJO_RESULT_INVALID_ARGUMENT; if ((validated_options.flags & MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY) && (!shared_buffer_->IsReadOnly())) { // If a read-only duplicate is requested and |shared_buffer_| is not // read-only, make a read-only duplicate of |shared_buffer_|. scoped_refptr<PlatformSharedBuffer> read_only_buffer = shared_buffer_->CreateReadOnlyDuplicate(); if (!read_only_buffer) return MOJO_RESULT_FAILED_PRECONDITION; DCHECK(read_only_buffer->IsReadOnly()); *new_dispatcher = CreateInternal(std::move(read_only_buffer)); return MOJO_RESULT_OK; } *new_dispatcher = CreateInternal(shared_buffer_); return MOJO_RESULT_OK; } MojoResult SharedBufferDispatcher::MapBuffer( uint64_t offset, uint64_t num_bytes, MojoMapBufferFlags flags, std::unique_ptr<PlatformSharedBufferMapping>* 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(shared_buffer_); if (in_transit_ || !shared_buffer_->IsValidMap(static_cast<size_t>(offset), static_cast<size_t>(num_bytes))) { return MOJO_RESULT_INVALID_ARGUMENT; } DCHECK(mapping); *mapping = shared_buffer_->MapNoCheck(static_cast<size_t>(offset), static_cast<size_t>(num_bytes)); if (!*mapping) { LOG(ERROR) << "Unable to map: read_only" << shared_buffer_->IsReadOnly(); return MOJO_RESULT_RESOURCE_EXHAUSTED; } 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; } bool SharedBufferDispatcher::EndSerialize(void* destination, ports::PortName* ports, PlatformHandle* handles) { SerializedState* serialization = static_cast<SerializedState*>(destination); base::AutoLock lock(lock_); serialization->num_bytes = static_cast<uint64_t>(shared_buffer_->GetNumBytes()); serialization->flags = (shared_buffer_->IsReadOnly() ? kSerializedStateFlagsReadOnly : 0); serialization->padding = 0; handle_for_transit_ = shared_buffer_->DuplicatePlatformHandle(); if (!handle_for_transit_.is_valid()) { shared_buffer_ = nullptr; return false; } handles[0] = handle_for_transit_.get(); return true; } bool SharedBufferDispatcher::BeginTransit() { base::AutoLock lock(lock_); if (in_transit_) return false; in_transit_ = static_cast<bool>(shared_buffer_); return in_transit_; } void SharedBufferDispatcher::CompleteTransitAndClose() { base::AutoLock lock(lock_); in_transit_ = false; shared_buffer_ = nullptr; ignore_result(handle_for_transit_.release()); } void SharedBufferDispatcher::CancelTransit() { base::AutoLock lock(lock_); in_transit_ = false; handle_for_transit_.reset(); } SharedBufferDispatcher::SharedBufferDispatcher( scoped_refptr<PlatformSharedBuffer> shared_buffer) : shared_buffer_(shared_buffer) { DCHECK(shared_buffer_); } SharedBufferDispatcher::~SharedBufferDispatcher() { DCHECK(!shared_buffer_ && !in_transit_); } // static MojoResult SharedBufferDispatcher::ValidateDuplicateOptions( const MojoDuplicateBufferHandleOptions* in_options, MojoDuplicateBufferHandleOptions* out_options) { const MojoDuplicateBufferHandleOptionsFlags kKnownFlags = MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY; static const MojoDuplicateBufferHandleOptions kDefaultOptions = { static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)), MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_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 edk } // namespace mojo