blob: af3ed3161d33acf6daa449ec86e727887a780c26 [file] [log] [blame]
// 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 <limits>
#include <utility>
#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<uint32_t>(sizeof(MojoCreateSharedBufferOptions)),
MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE};
// static
MojoResult SharedBufferDispatcher::ValidateCreateOptions(
UserPointer<const MojoCreateSharedBufferOptions> 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<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
RefPtr<SharedBufferDispatcher> 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<size_t>(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> SharedBufferDispatcher::Deserialize(
Channel* channel,
const void* source,
size_t size,
std::vector<ScopedPlatformHandle>* platform_handles) {
DCHECK(channel);
if (size != sizeof(SerializedSharedBufferDispatcher)) {
LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)";
return nullptr;
}
const SerializedSharedBufferDispatcher* serialization =
static_cast<const SerializedSharedBufferDispatcher*>(source);
if (serialization->num_bytes >
static_cast<uint64_t>(std::numeric_limits<size_t>::max())) {
LOG(ERROR)
<< "Invalid serialized shared buffer dispatcher (num_bytes too large)";
return nullptr;
}
if (static_cast<size_t>(serialization->platform_handle_index) >
std::numeric_limits<size_t>::max()) {
LOG(ERROR) << "Invalid serialized shared buffer dispatcher"
" (platform_handle_index too large)";
return nullptr;
}
size_t num_bytes = static_cast<size_t>(serialization->num_bytes);
size_t platform_handle_index =
static_cast<size_t>(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<PlatformSharedBuffer>&& shared_buffer)
: shared_buffer_(std::move(shared_buffer)) {
DCHECK(shared_buffer_);
}
SharedBufferDispatcher::~SharedBufferDispatcher() {
}
// static
MojoResult SharedBufferDispatcher::ValidateDuplicateOptions(
UserPointer<const MojoDuplicateBufferHandleOptions> in_options,
MojoDuplicateBufferHandleOptions* out_options) {
const MojoDuplicateBufferHandleOptionsFlags kKnownFlags =
MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE;
static const MojoDuplicateBufferHandleOptions kDefaultOptions = {
static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)),
MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE};
*out_options = kDefaultOptions;
if (in_options.IsNull())
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;
}
void SharedBufferDispatcher::CloseImplNoLock() {
mutex().AssertHeld();
DCHECK(shared_buffer_);
shared_buffer_ = nullptr;
}
MojoResult SharedBufferDispatcher::DuplicateDispatcherImplNoLock(
util::RefPtr<Dispatcher>* 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<Dispatcher>
SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock(
MessagePipe* /*message_pipe*/,
unsigned /*port*/) {
mutex().AssertHeld();
CancelAllAwakablesNoLock();
DCHECK(shared_buffer_);
return CreateInternal(std::move(shared_buffer_));
}
MojoResult SharedBufferDispatcher::DuplicateBufferHandleImplNoLock(
UserPointer<const MojoDuplicateBufferHandleOptions> options,
RefPtr<Dispatcher>* 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<MojoBufferInformation> 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<uint64_t>(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<PlatformSharedBufferMapping>* mapping) {
mutex().AssertHeld();
DCHECK(shared_buffer_);
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;
if (!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)
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<ScopedPlatformHandle>* platform_handles) {
AssertHasOneRef(); // Only one ref => no need to take the lock.
DCHECK(shared_buffer_);
SerializedSharedBufferDispatcher* serialization =
static_cast<SerializedSharedBufferDispatcher*>(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