// 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 #include #include "base/logging.h" #include "mojo/edk/embedder/platform_support.h" #include "mojo/edk/platform/platform_shared_buffer.h" #include "mojo/edk/system/channel.h" #include "mojo/edk/system/configuration.h" #include "mojo/edk/system/memory.h" #include "mojo/edk/system/options_validation.h" #include "mojo/public/c/system/macros.h" using mojo::platform::PlatformSharedBuffer; using mojo::platform::PlatformSharedBufferMapping; using mojo::platform::ScopedPlatformHandle; using mojo::util::RefPtr; namespace mojo { namespace system { namespace { struct MOJO_ALIGNAS(8) SerializedSharedBufferDispatcher { uint64_t num_bytes; uint32_t platform_handle_index; }; } // namespace // static constexpr MojoHandleRights SharedBufferDispatcher::kDefaultHandleRights; // static const MojoCreateSharedBufferOptions SharedBufferDispatcher::kDefaultCreateOptions = { static_cast(sizeof(MojoCreateSharedBufferOptions)), MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE}; // static MojoResult SharedBufferDispatcher::ValidateCreateOptions( UserPointer in_options, MojoCreateSharedBufferOptions* out_options) { const MojoCreateSharedBufferOptionsFlags kKnownFlags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE; *out_options = kDefaultCreateOptions; if (in_options.IsNull()) return MOJO_RESULT_OK; UserOptionsReader 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 RefPtr SharedBufferDispatcher::Create( embedder::PlatformSupport* platform_support, const MojoCreateSharedBufferOptions& /*validated_options*/, uint64_t num_bytes, MojoResult* result) { if (!num_bytes) { *result = MOJO_RESULT_INVALID_ARGUMENT; return nullptr; } if (num_bytes > GetConfiguration().max_shared_memory_num_bytes) { *result = MOJO_RESULT_RESOURCE_EXHAUSTED; return nullptr; } auto shared_buffer = platform_support->CreateSharedBuffer(static_cast(num_bytes)); if (!shared_buffer) { *result = MOJO_RESULT_RESOURCE_EXHAUSTED; return nullptr; } *result = MOJO_RESULT_OK; return CreateInternal(std::move(shared_buffer)); } Dispatcher::Type SharedBufferDispatcher::GetType() const { return Type::SHARED_BUFFER; } bool SharedBufferDispatcher::SupportsEntrypointClass( EntrypointClass entrypoint_class) const { return (entrypoint_class == EntrypointClass::NONE || entrypoint_class == EntrypointClass::BUFFER); } // static RefPtr SharedBufferDispatcher::Deserialize( Channel* channel, const void* source, size_t size, std::vector* platform_handles) { DCHECK(channel); if (size != sizeof(SerializedSharedBufferDispatcher)) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)"; return nullptr; } const SerializedSharedBufferDispatcher* serialization = static_cast(source); if (serialization->num_bytes > static_cast(std::numeric_limits::max())) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (num_bytes too large)"; return nullptr; } if (static_cast(serialization->platform_handle_index) > std::numeric_limits::max()) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher" " (platform_handle_index too large)"; return nullptr; } size_t num_bytes = static_cast(serialization->num_bytes); size_t platform_handle_index = static_cast(serialization->platform_handle_index); if (!num_bytes) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (invalid num_bytes)"; return nullptr; } if (!platform_handles || platform_handle_index >= platform_handles->size()) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (missing handles)"; return nullptr; } // Starts off invalid, which is what we want. ScopedPlatformHandle platform_handle; // We take ownership of the handle, so we have to invalidate the one in // |platform_handles|. std::swap(platform_handle, (*platform_handles)[platform_handle_index]); // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be // closed even if creation fails. auto shared_buffer = channel->platform_support()->CreateSharedBufferFromHandle( num_bytes, std::move(platform_handle)); if (!shared_buffer) { LOG(ERROR) << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)"; return nullptr; } return CreateInternal(std::move(shared_buffer)); } SharedBufferDispatcher::SharedBufferDispatcher( RefPtr&& shared_buffer) : shared_buffer_(std::move(shared_buffer)) { DCHECK(shared_buffer_); } SharedBufferDispatcher::~SharedBufferDispatcher() { } // static MojoResult SharedBufferDispatcher::ValidateDuplicateOptions( UserPointer in_options, MojoDuplicateBufferHandleOptions* out_options) { const MojoDuplicateBufferHandleOptionsFlags kKnownFlags = MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE; static const MojoDuplicateBufferHandleOptions kDefaultOptions = { static_cast(sizeof(MojoDuplicateBufferHandleOptions)), MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE}; *out_options = kDefaultOptions; if (in_options.IsNull()) return MOJO_RESULT_OK; UserOptionsReader 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; } void SharedBufferDispatcher::CloseImplNoLock() { mutex().AssertHeld(); DCHECK(shared_buffer_); shared_buffer_ = nullptr; } MojoResult SharedBufferDispatcher::DuplicateDispatcherImplNoLock( util::RefPtr* new_dispatcher) { mutex().AssertHeld(); // Note: Since this is "duplicate", we keep our ref to |shared_buffer_|. *new_dispatcher = CreateInternal(shared_buffer_.Clone()); return MOJO_RESULT_OK; } RefPtr SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock( MessagePipe* /*message_pipe*/, unsigned /*port*/) { mutex().AssertHeld(); CancelAllAwakablesNoLock(); DCHECK(shared_buffer_); return CreateInternal(std::move(shared_buffer_)); } MojoResult SharedBufferDispatcher::DuplicateBufferHandleImplNoLock( UserPointer options, RefPtr* new_dispatcher) { mutex().AssertHeld(); 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_|. *new_dispatcher = CreateInternal(shared_buffer_.Clone()); return MOJO_RESULT_OK; } MojoResult SharedBufferDispatcher::GetBufferInformationImplNoLock( UserPointer info, uint32_t info_num_bytes) { mutex().AssertHeld(); // Note: If/when |MojoBufferInformation| is extended beyond its initial // definition, more work will be necessary. (See the definition of // |MojoGetBufferInformation()| in mojo/public/c/system/buffer.h.) static_assert(sizeof(MojoBufferInformation) == 16u, "MojoBufferInformation has been extended!"); if (info_num_bytes < sizeof(MojoBufferInformation)) return MOJO_RESULT_INVALID_ARGUMENT; MojoBufferInformation model_info = { sizeof(MojoBufferInformation), // |struct_size|. MOJO_BUFFER_INFORMATION_FLAG_NONE, // |flags|. static_cast(shared_buffer_->GetNumBytes()), // |num_bytes|. }; info.Put(model_info); return MOJO_RESULT_OK; } MojoResult SharedBufferDispatcher::MapBufferImplNoLock( uint64_t offset, uint64_t num_bytes, MojoMapBufferFlags flags, std::unique_ptr* mapping) { mutex().AssertHeld(); DCHECK(shared_buffer_); if (offset > static_cast(std::numeric_limits::max())) return MOJO_RESULT_INVALID_ARGUMENT; if (num_bytes > static_cast(std::numeric_limits::max())) return MOJO_RESULT_INVALID_ARGUMENT; if (!shared_buffer_->IsValidMap(static_cast(offset), static_cast(num_bytes))) return MOJO_RESULT_INVALID_ARGUMENT; DCHECK(mapping); *mapping = shared_buffer_->MapNoCheck(static_cast(offset), static_cast(num_bytes)); if (!*mapping) return MOJO_RESULT_RESOURCE_EXHAUSTED; return MOJO_RESULT_OK; } void SharedBufferDispatcher::StartSerializeImplNoLock( Channel* /*channel*/, size_t* max_size, size_t* max_platform_handles) { AssertHasOneRef(); // Only one ref => no need to take the lock. *max_size = sizeof(SerializedSharedBufferDispatcher); *max_platform_handles = 1; } bool SharedBufferDispatcher::EndSerializeAndCloseImplNoLock( Channel* /*channel*/, void* destination, size_t* actual_size, std::vector* platform_handles) { AssertHasOneRef(); // Only one ref => no need to take the lock. DCHECK(shared_buffer_); SerializedSharedBufferDispatcher* serialization = static_cast(destination); // If there's only one reference to |shared_buffer_|, then it's ours (and no // one else can make any more references to it), so we can just take its // handle. ScopedPlatformHandle platform_handle( shared_buffer_->HasOneRef() ? shared_buffer_->PassPlatformHandle() : shared_buffer_->DuplicatePlatformHandle()); if (!platform_handle.is_valid()) { shared_buffer_ = nullptr; return false; } serialization->num_bytes = shared_buffer_->GetNumBytes(); serialization->platform_handle_index = platform_handles->size(); platform_handles->push_back(std::move(platform_handle)); *actual_size = sizeof(SerializedSharedBufferDispatcher); shared_buffer_ = nullptr; return true; } } // namespace system } // namespace mojo