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