| // 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 |