// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "mojo/core/core.h"
#include <string.h>
#include <algorithm>
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/writable_shared_memory_region.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_id_helper.h"
#include "build/build_config.h"
#include "mojo/buildflags.h"
#include "mojo/core/channel.h"
#include "mojo/core/configuration.h"
#include "mojo/core/data_pipe_consumer_dispatcher.h"
#include "mojo/core/data_pipe_producer_dispatcher.h"
#include "mojo/core/embedder/process_error_callback.h"
#include "mojo/core/handle_signals_state.h"
#include "mojo/core/invitation_dispatcher.h"
#include "mojo/core/message_pipe_dispatcher.h"
#include "mojo/core/platform_handle_dispatcher.h"
#include "mojo/core/platform_handle_utils.h"
#include "mojo/core/platform_shared_memory_mapping.h"
#include "mojo/core/ports/event.h"
#include "mojo/core/ports/name.h"
#include "mojo/core/ports/node.h"
#include "mojo/core/request_context.h"
#include "mojo/core/shared_buffer_dispatcher.h"
#include "mojo/core/user_message_impl.h"
#include "mojo/core/watcher_dispatcher.h"
#include "mojo/public/cpp/bindings/mojo_buildflags.h"
#include "mojo/public/cpp/platform/platform_handle_internal.h"
namespace mojo {
namespace core {
namespace {
// This is an unnecessarily large limit that is relatively easy to enforce.
const uint32_t kMaxHandlesPerMessage = 1024 * 1024;
// TODO(rockot): Maybe we could negotiate a debugging pipe ID for cross-process
// pipes too; for now we just use a constant. This only affects bootstrap pipes.
const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL;
// The pipe name which must be used for the sole pipe attachment on any isolated
// invitation.
constexpr std::string_view kIsolatedInvitationPipeName = {"\0\0\0\0", 4};
void InvokeProcessErrorCallback(MojoProcessErrorHandler handler,
uintptr_t context,
const std::string& error,
MojoProcessErrorFlags flags) {
MojoProcessErrorDetails details;
details.struct_size = sizeof(details);
DCHECK(base::IsValueInRangeForNumericType<uint32_t>(error.size()));
details.error_message_length = static_cast<uint32_t>(error.size());
if (!error.empty())
details.error_message = error.data();
else
details.error_message = nullptr;
details.flags = flags;
handler(context, &details);
}
// Helper class which is bound to the lifetime of a
// ProcessErrorCallback generated by the |MojoSendInvitation()|
// API. When the last reference to the error callback is lost within the EDK,
// which will happen shortly after a connection to the process is lost, that
// obviously guarantees that no more invocations of the callback will occur. At
// that point, the corresponding instance of this object (owned by the callback
// -- see Core::SendInvitation) will be destroyed.
class ProcessDisconnectHandler {
public:
ProcessDisconnectHandler(MojoProcessErrorHandler handler, uintptr_t context)
: handler_(handler), context_(context) {}
ProcessDisconnectHandler(const ProcessDisconnectHandler&) = delete;
ProcessDisconnectHandler& operator=(const ProcessDisconnectHandler&) = delete;
~ProcessDisconnectHandler() {
InvokeProcessErrorCallback(handler_, context_, std::string(),
MOJO_PROCESS_ERROR_FLAG_DISCONNECTED);
}
private:
const MojoProcessErrorHandler handler_;
const uintptr_t context_;
};
void RunMojoProcessErrorHandler(
ProcessDisconnectHandler* disconnect_handler,
MojoProcessErrorHandler handler,
uintptr_t context,
const std::string& error) {
InvokeProcessErrorCallback(handler, context, error,
MOJO_PROCESS_ERROR_FLAG_NONE);
}
uint64_t MakePipeId() {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
return base::trace_event::GetNextGlobalTraceId();
#else
return 0;
#endif
}
} // namespace
Core::Core() {
handles_ = std::make_unique<HandleTable>();
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
handles_.get(), "MojoHandleTable", nullptr);
}
Core::~Core() {
if (node_controller_ && node_controller_->io_task_runner()) {
// If this races with IO thread shutdown the callback will be dropped and
// the NodeController will be shutdown on this thread anyway, which is also
// just fine.
auto io_task_runner = node_controller_->io_task_runner();
io_task_runner->PostTask(FROM_HERE,
base::BindOnce(&Core::PassNodeControllerToIOThread,
std::move(node_controller_)));
}
base::trace_event::MemoryDumpManager::GetInstance()
->UnregisterAndDeleteDumpProviderSoon(std::move(handles_));
}
void Core::SetIOTaskRunner(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
GetNodeController()->SetIOTaskRunner(std::move(io_task_runner));
}
NodeController* Core::GetNodeController() {
base::AutoLock lock(node_controller_lock_);
if (!node_controller_)
node_controller_ = std::make_unique<NodeController>();
return node_controller_.get();
}
scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) {
base::AutoLock lock(handles_->GetLock());
return handles_->GetDispatcher(handle);
}
scoped_refptr<Dispatcher> Core::GetAndRemoveDispatcher(MojoHandle handle) {
scoped_refptr<Dispatcher> dispatcher;
base::AutoLock lock(handles_->GetLock());
handles_->GetAndRemoveDispatcher(handle, &dispatcher);
return dispatcher;
}
MojoHandle Core::CreatePartialMessagePipe(ports::PortRef* peer) {
RequestContext request_context;
ports::PortRef local_port;
GetNodeController()->node()->CreatePortPair(&local_port, peer);
return AddDispatcher(new MessagePipeDispatcher(
GetNodeController(), local_port, kUnknownPipeIdForDebug, 0));
}
MojoHandle Core::CreatePartialMessagePipe(const ports::PortRef& port) {
RequestContext request_context;
return AddDispatcher(new MessagePipeDispatcher(GetNodeController(), port,
kUnknownPipeIdForDebug, 1));
}
void Core::SendBrokerClientInvitation(
base::Process target_process,
ConnectionParams connection_params,
const std::vector<std::pair<std::string, ports::PortRef>>& attached_ports,
const ProcessErrorCallback& process_error_callback) {
RequestContext request_context;
GetNodeController()->SendBrokerClientInvitation(
std::move(target_process), std::move(connection_params), attached_ports,
process_error_callback);
}
void Core::ConnectIsolated(ConnectionParams connection_params,
const ports::PortRef& port,
std::string_view connection_name) {
RequestContext request_context;
GetNodeController()->ConnectIsolated(std::move(connection_params), port,
connection_name);
}
MojoHandle Core::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) {
base::AutoLock lock(handles_->GetLock());
return handles_->AddDispatcher(dispatcher);
}
bool Core::AddDispatchersFromTransit(
const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
MojoHandle* handles) {
bool failed = false;
{
base::AutoLock lock(handles_->GetLock());
if (!handles_->AddDispatchersFromTransit(dispatchers, handles))
failed = true;
}
if (failed) {
for (auto d : dispatchers) {
if (d.dispatcher)
d.dispatcher->Close();
}
return false;
}
return true;
}
MojoResult Core::AcquireDispatchersForTransit(
const MojoHandle* handles,
size_t num_handles,
std::vector<Dispatcher::DispatcherInTransit>* dispatchers) {
base::AutoLock lock(handles_->GetLock());
MojoResult rv = handles_->BeginTransit(handles, num_handles, dispatchers);
if (rv != MOJO_RESULT_OK)
handles_->CancelTransit(*dispatchers);
return rv;
}
void Core::ReleaseDispatchersForTransit(
const std::vector<Dispatcher::DispatcherInTransit>& dispatchers,
bool in_transit) {
base::AutoLock lock(handles_->GetLock());
if (in_transit)
handles_->CompleteTransitAndClose(dispatchers);
else
handles_->CancelTransit(dispatchers);
}
void Core::RequestShutdown(base::OnceClosure callback) {
GetNodeController()->RequestShutdown(std::move(callback));
}
MojoHandle Core::ExtractMessagePipeFromInvitation(const std::string& name) {
RequestContext request_context;
ports::PortRef port0, port1;
GetNodeController()->node()->CreatePortPair(&port0, &port1);
MojoHandle handle = AddDispatcher(new MessagePipeDispatcher(
GetNodeController(), port0, kUnknownPipeIdForDebug, 1));
GetNodeController()->MergePortIntoInviter(name, port1);
return handle;
}
MojoTimeTicks Core::GetTimeTicksNow() {
return base::TimeTicks::Now().ToInternalValue();
}
MojoResult Core::Close(MojoHandle handle) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher;
{
base::AutoLock lock(handles_->GetLock());
MojoResult rv = handles_->GetAndRemoveDispatcher(handle, &dispatcher);
if (rv != MOJO_RESULT_OK)
return rv;
}
dispatcher->Close();
return MOJO_RESULT_OK;
}
MojoResult Core::QueryHandleSignalsState(
MojoHandle handle,
MojoHandleSignalsState* signals_state) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle);
if (!dispatcher || !signals_state)
return MOJO_RESULT_INVALID_ARGUMENT;
*signals_state = dispatcher->GetHandleSignalsState();
return MOJO_RESULT_OK;
}
MojoResult Core::CreateTrap(MojoTrapEventHandler handler,
const MojoCreateTrapOptions* options,
MojoHandle* trap_handle) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
if (!trap_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
*trap_handle = AddDispatcher(new WatcherDispatcher(handler));
if (*trap_handle == MOJO_HANDLE_INVALID)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
return MOJO_RESULT_OK;
}
MojoResult Core::AddTrigger(MojoHandle trap_handle,
MojoHandle handle,
MojoHandleSignals signals,
MojoTriggerCondition condition,
uintptr_t context,
const MojoAddTriggerOptions* options) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
scoped_refptr<Dispatcher> watcher = GetDispatcher(trap_handle);
if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER)
return MOJO_RESULT_INVALID_ARGUMENT;
scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handle);
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
return watcher->WatchDispatcher(std::move(dispatcher), signals, condition,
context);
}
MojoResult Core::RemoveTrigger(MojoHandle trap_handle,
uintptr_t context,
const MojoRemoveTriggerOptions* options) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
scoped_refptr<Dispatcher> watcher = GetDispatcher(trap_handle);
if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER)
return MOJO_RESULT_INVALID_ARGUMENT;
return watcher->CancelWatch(context);
}
MojoResult Core::ArmTrap(MojoHandle trap_handle,
const MojoArmTrapOptions* options,
uint32_t* num_blocking_events,
MojoTrapEvent* blocking_events) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
scoped_refptr<Dispatcher> watcher = GetDispatcher(trap_handle);
if (!watcher || watcher->GetType() != Dispatcher::Type::WATCHER)
return MOJO_RESULT_INVALID_ARGUMENT;
return watcher->Arm(num_blocking_events, blocking_events);
}
MojoResult Core::CreateMessage(const MojoCreateMessageOptions* options,
MojoMessageHandle* message_handle) {
if (!message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
const MojoCreateMessageFlags flags =
options ? options->flags : MOJO_CREATE_MESSAGE_FLAG_NONE;
*message_handle = reinterpret_cast<MojoMessageHandle>(
UserMessageImpl::CreateEventForNewMessage(flags).release());
return MOJO_RESULT_OK;
}
MojoResult Core::DestroyMessage(MojoMessageHandle message_handle) {
if (!message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
delete reinterpret_cast<ports::UserMessageEvent*>(message_handle);
return MOJO_RESULT_OK;
}
MojoResult Core::SerializeMessage(MojoMessageHandle message_handle,
const MojoSerializeMessageOptions* options) {
if (!message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
return reinterpret_cast<ports::UserMessageEvent*>(message_handle)
->GetMessage<UserMessageImpl>()
->SerializeIfNecessary();
}
MojoResult Core::ReserveMessageCapacity(MojoMessageHandle message_handle,
uint32_t payload_buffer_size,
uint32_t* buffer_size) {
if (!message_handle) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
RequestContext request_context;
auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
->GetMessage<UserMessageImpl>();
MojoResult rv = message->ReserveCapacity(payload_buffer_size);
if (rv != MOJO_RESULT_OK) {
return rv;
}
if (buffer_size) {
*buffer_size =
base::checked_cast<uint32_t>(message->user_payload_capacity());
}
return MOJO_RESULT_OK;
}
MojoResult Core::AppendMessageData(MojoMessageHandle message_handle,
uint32_t additional_payload_size,
const MojoHandle* handles,
uint32_t num_handles,
const MojoAppendMessageDataOptions* options,
void** buffer,
uint32_t* buffer_size) {
if (!message_handle || (num_handles && !handles))
return MOJO_RESULT_INVALID_ARGUMENT;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
->GetMessage<UserMessageImpl>();
MojoResult rv =
message->AppendData(additional_payload_size, handles, num_handles);
if (rv != MOJO_RESULT_OK)
return rv;
if (options && (options->flags & MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE))
message->CommitSize();
if (buffer)
*buffer = message->user_payload();
if (buffer_size) {
*buffer_size =
base::checked_cast<uint32_t>(message->user_payload_capacity());
}
return MOJO_RESULT_OK;
}
MojoResult Core::GetMessageData(MojoMessageHandle message_handle,
const MojoGetMessageDataOptions* options,
void** buffer,
uint32_t* num_bytes,
MojoHandle* handles,
uint32_t* num_handles) {
if (!message_handle || (num_handles && *num_handles && !handles))
return MOJO_RESULT_INVALID_ARGUMENT;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
->GetMessage<UserMessageImpl>();
if (!message->IsSerialized() || !message->IsTransmittable())
return MOJO_RESULT_FAILED_PRECONDITION;
if (num_bytes) {
base::CheckedNumeric<uint32_t> payload_size = message->user_payload_size();
*num_bytes = payload_size.ValueOrDie();
}
if (message->user_payload_size() > 0) {
if (!num_bytes || !buffer)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
*buffer = message->user_payload();
} else if (buffer) {
*buffer = nullptr;
}
if (options && (options->flags & MOJO_GET_MESSAGE_DATA_FLAG_IGNORE_HANDLES))
return MOJO_RESULT_OK;
uint32_t max_num_handles = 0;
if (num_handles) {
max_num_handles = *num_handles;
*num_handles = static_cast<uint32_t>(message->num_handles());
}
if (message->num_handles() > max_num_handles ||
message->num_handles() > kMaxHandlesPerMessage) {
return MOJO_RESULT_RESOURCE_EXHAUSTED;
}
RequestContext request_context;
Dispatcher::SetExtractingHandlesFromMessage(true);
MojoResult result = message->ExtractSerializedHandles(
UserMessageImpl::ExtractBadHandlePolicy::kAbort, handles);
Dispatcher::SetExtractingHandlesFromMessage(false);
return result;
}
MojoResult Core::SetMessageContext(
MojoMessageHandle message_handle,
uintptr_t context,
MojoMessageContextSerializer serializer,
MojoMessageContextDestructor destructor,
const MojoSetMessageContextOptions* options) {
if (!message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
->GetMessage<UserMessageImpl>();
return message->SetContext(context, serializer, destructor);
}
MojoResult Core::GetMessageContext(MojoMessageHandle message_handle,
const MojoGetMessageContextOptions* options,
uintptr_t* context) {
if (!message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
auto* message = reinterpret_cast<ports::UserMessageEvent*>(message_handle)
->GetMessage<UserMessageImpl>();
if (!message->HasContext())
return MOJO_RESULT_NOT_FOUND;
*context = message->context();
return MOJO_RESULT_OK;
}
MojoResult Core::CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
MojoHandle* message_pipe_handle0,
MojoHandle* message_pipe_handle1) {
RequestContext request_context;
ports::PortRef port0, port1;
GetNodeController()->node()->CreatePortPair(&port0, &port1);
DCHECK(message_pipe_handle0);
DCHECK(message_pipe_handle1);
uint64_t pipe_id = MakePipeId();
*message_pipe_handle0 = AddDispatcher(new MessagePipeDispatcher(
GetNodeController(), port0, pipe_id, /*endpoint=*/0));
if (*message_pipe_handle0 == MOJO_HANDLE_INVALID)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
*message_pipe_handle1 = AddDispatcher(new MessagePipeDispatcher(
GetNodeController(), port1, pipe_id, /*endpoint=*/1));
if (*message_pipe_handle1 == MOJO_HANDLE_INVALID) {
scoped_refptr<Dispatcher> dispatcher0;
{
base::AutoLock lock(handles_->GetLock());
handles_->GetAndRemoveDispatcher(*message_pipe_handle0, &dispatcher0);
}
dispatcher0->Close();
return MOJO_RESULT_RESOURCE_EXHAUSTED;
}
return MOJO_RESULT_OK;
}
MojoResult Core::WriteMessage(MojoHandle message_pipe_handle,
MojoMessageHandle message_handle,
const MojoWriteMessageOptions* options) {
RequestContext request_context;
if (!message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
auto message_event = base::WrapUnique(
reinterpret_cast<ports::UserMessageEvent*>(message_handle));
auto* message = message_event->GetMessage<UserMessageImpl>();
if (!message || !message->IsTransmittable())
return MOJO_RESULT_INVALID_ARGUMENT;
auto dispatcher = GetDispatcher(message_pipe_handle);
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
return dispatcher->WriteMessage(std::move(message_event));
}
MojoResult Core::ReadMessage(MojoHandle message_pipe_handle,
const MojoReadMessageOptions* options,
MojoMessageHandle* message_handle) {
RequestContext request_context;
auto dispatcher = GetDispatcher(message_pipe_handle);
if (!dispatcher || !message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
std::unique_ptr<ports::UserMessageEvent> message_event;
MojoResult rv = dispatcher->ReadMessage(&message_event);
if (rv != MOJO_RESULT_OK)
return rv;
*message_handle =
reinterpret_cast<MojoMessageHandle>(message_event.release());
return MOJO_RESULT_OK;
}
MojoResult Core::FuseMessagePipes(MojoHandle handle0,
MojoHandle handle1,
const MojoFuseMessagePipesOptions* options) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher0;
scoped_refptr<Dispatcher> dispatcher1;
bool valid_handles = true;
{
base::AutoLock lock(handles_->GetLock());
MojoResult result0 =
handles_->GetAndRemoveDispatcher(handle0, &dispatcher0);
MojoResult result1 =
handles_->GetAndRemoveDispatcher(handle1, &dispatcher1);
if (result0 != MOJO_RESULT_OK || result1 != MOJO_RESULT_OK ||
dispatcher0->GetType() != Dispatcher::Type::MESSAGE_PIPE ||
dispatcher1->GetType() != Dispatcher::Type::MESSAGE_PIPE)
valid_handles = false;
}
if (!valid_handles) {
if (dispatcher0)
dispatcher0->Close();
if (dispatcher1)
dispatcher1->Close();
return MOJO_RESULT_INVALID_ARGUMENT;
}
MessagePipeDispatcher* mpd0 =
static_cast<MessagePipeDispatcher*>(dispatcher0.get());
MessagePipeDispatcher* mpd1 =
static_cast<MessagePipeDispatcher*>(dispatcher1.get());
if (!mpd0->Fuse(mpd1))
return MOJO_RESULT_FAILED_PRECONDITION;
return MOJO_RESULT_OK;
}
MojoResult Core::NotifyBadMessage(MojoMessageHandle message_handle,
const char* error,
size_t error_num_bytes,
const MojoNotifyBadMessageOptions* options) {
if (!message_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
auto* message_event =
reinterpret_cast<ports::UserMessageEvent*>(message_handle);
auto* message = message_event->GetMessage<UserMessageImpl>();
NodeController* node_controller = GetNodeController();
if (!node_controller->HasBadMessageHandler(message->source_node())) {
if (message->source_node() == ports::kInvalidNodeName)
DVLOG(1) << "Received invalid message from unknown node.";
if (!default_process_error_callback_.is_null())
default_process_error_callback_.Run(std::string(error, error_num_bytes));
return MOJO_RESULT_OK;
}
node_controller->NotifyBadMessageFrom(message->source_node(),
std::string(error, error_num_bytes));
return MOJO_RESULT_OK;
}
MojoResult Core::CreateDataPipe(const MojoCreateDataPipeOptions* options,
MojoHandle* data_pipe_producer_handle,
MojoHandle* data_pipe_consumer_handle) {
RequestContext request_context;
if (options && options->struct_size < sizeof(MojoCreateDataPipeOptions))
return MOJO_RESULT_INVALID_ARGUMENT;
MojoCreateDataPipeOptions create_options;
create_options.struct_size = sizeof(MojoCreateDataPipeOptions);
create_options.flags = options ? options->flags : 0;
create_options.element_num_bytes = options ? options->element_num_bytes : 1;
// TODO(rockot): Use Configuration to get default data pipe capacity.
create_options.capacity_num_bytes = options && options->capacity_num_bytes
? options->capacity_num_bytes
: 64 * 1024;
if (!create_options.element_num_bytes || !create_options.capacity_num_bytes ||
create_options.capacity_num_bytes < create_options.element_num_bytes) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
base::subtle::PlatformSharedMemoryRegion ring_buffer_region =
base::WritableSharedMemoryRegion::TakeHandleForSerialization(
GetNodeController()->CreateSharedBuffer(
create_options.capacity_num_bytes));
// NOTE: We demote the writable region to an unsafe region so that the
// producer handle can be transferred freely. There is no compelling reason
// to restrict access rights of consumers since they are the exclusive
// consumer of this pipe, and it would be impossible to support such access
// control on Android anyway.
auto writable_region_handle = ring_buffer_region.PassPlatformHandle();
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_APPLE)
// This isn't strictly necessary, but it does make the handle configuration
// consistent with regular UnsafeSharedMemoryRegions.
writable_region_handle.readonly_fd.reset();
#endif
base::UnsafeSharedMemoryRegion producer_region =
base::UnsafeSharedMemoryRegion::Deserialize(
base::subtle::PlatformSharedMemoryRegion::Take(
std::move(writable_region_handle),
base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe,
create_options.capacity_num_bytes, ring_buffer_region.GetGUID()));
if (!producer_region.IsValid())
return MOJO_RESULT_RESOURCE_EXHAUSTED;
ports::PortRef port0, port1;
GetNodeController()->node()->CreatePortPair(&port0, &port1);
DCHECK(data_pipe_producer_handle);
DCHECK(data_pipe_consumer_handle);
base::UnsafeSharedMemoryRegion consumer_region = producer_region.Duplicate();
uint64_t pipe_id = MakePipeId();
scoped_refptr<Dispatcher> producer = DataPipeProducerDispatcher::Create(
GetNodeController(), port0, std::move(producer_region), create_options,
pipe_id);
if (!producer)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
scoped_refptr<Dispatcher> consumer = DataPipeConsumerDispatcher::Create(
GetNodeController(), port1, std::move(consumer_region), create_options,
pipe_id);
if (!consumer) {
producer->Close();
return MOJO_RESULT_RESOURCE_EXHAUSTED;
}
*data_pipe_producer_handle = AddDispatcher(producer);
*data_pipe_consumer_handle = AddDispatcher(consumer);
if (*data_pipe_producer_handle == MOJO_HANDLE_INVALID ||
*data_pipe_consumer_handle == MOJO_HANDLE_INVALID) {
if (*data_pipe_producer_handle != MOJO_HANDLE_INVALID) {
scoped_refptr<Dispatcher> unused;
base::AutoLock lock(handles_->GetLock());
handles_->GetAndRemoveDispatcher(*data_pipe_producer_handle, &unused);
}
producer->Close();
consumer->Close();
return MOJO_RESULT_RESOURCE_EXHAUSTED;
}
return MOJO_RESULT_OK;
}
MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle,
const void* elements,
uint32_t* num_bytes,
const MojoWriteDataOptions* options) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher(
GetDispatcher(data_pipe_producer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
MojoWriteDataOptions validated_options;
if (options) {
if (options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
constexpr MojoWriteDataFlags kSupportedFlags =
MOJO_WRITE_DATA_FLAG_NONE | MOJO_WRITE_DATA_FLAG_ALL_OR_NONE;
if (options->flags & ~kSupportedFlags)
return MOJO_RESULT_UNIMPLEMENTED;
validated_options.flags = options->flags;
} else {
validated_options.flags = MOJO_WRITE_DATA_FLAG_NONE;
}
return dispatcher->WriteData(elements, num_bytes, validated_options);
}
MojoResult Core::BeginWriteData(MojoHandle data_pipe_producer_handle,
const MojoBeginWriteDataOptions* options,
void** buffer,
uint32_t* buffer_num_bytes) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher(
GetDispatcher(data_pipe_producer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
MojoBeginWriteDataFlags flags = MOJO_BEGIN_WRITE_DATA_FLAG_NONE;
if (options) {
if (options->struct_size < sizeof(*options)) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
flags = options->flags;
}
return dispatcher->BeginWriteData(buffer, buffer_num_bytes, flags);
}
MojoResult Core::EndWriteData(MojoHandle data_pipe_producer_handle,
uint32_t num_bytes_written,
const MojoEndWriteDataOptions* options) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher(
GetDispatcher(data_pipe_producer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options) {
if (options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (options->flags != MOJO_END_WRITE_DATA_FLAG_NONE)
return MOJO_RESULT_UNIMPLEMENTED;
}
return dispatcher->EndWriteData(num_bytes_written);
}
MojoResult Core::ReadData(MojoHandle data_pipe_consumer_handle,
const MojoReadDataOptions* options,
void* elements,
uint32_t* num_bytes) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher(
GetDispatcher(data_pipe_consumer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
MojoReadDataOptions validated_options;
if (options) {
if (options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
constexpr MojoReadDataFlags kSupportedFlags =
MOJO_READ_DATA_FLAG_NONE | MOJO_READ_DATA_FLAG_ALL_OR_NONE |
MOJO_READ_DATA_FLAG_DISCARD | MOJO_READ_DATA_FLAG_QUERY |
MOJO_READ_DATA_FLAG_PEEK;
if (options->flags & ~kSupportedFlags)
return MOJO_RESULT_UNIMPLEMENTED;
validated_options.flags = options->flags;
} else {
validated_options.flags = MOJO_WRITE_DATA_FLAG_NONE;
}
return dispatcher->ReadData(validated_options, elements, num_bytes);
}
MojoResult Core::BeginReadData(MojoHandle data_pipe_consumer_handle,
const MojoBeginReadDataOptions* options,
const void** buffer,
uint32_t* buffer_num_bytes) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher(
GetDispatcher(data_pipe_consumer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options) {
if (options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (options->flags != MOJO_BEGIN_READ_DATA_FLAG_NONE)
return MOJO_RESULT_UNIMPLEMENTED;
}
return dispatcher->BeginReadData(buffer, buffer_num_bytes);
}
MojoResult Core::EndReadData(MojoHandle data_pipe_consumer_handle,
uint32_t num_bytes_read,
const MojoEndReadDataOptions* options) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher(
GetDispatcher(data_pipe_consumer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options) {
if (options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (options->flags != MOJO_END_READ_DATA_FLAG_NONE)
return MOJO_RESULT_UNIMPLEMENTED;
}
return dispatcher->EndReadData(num_bytes_read);
}
MojoResult Core::CreateSharedBuffer(
uint64_t num_bytes,
const MojoCreateSharedBufferOptions* options,
MojoHandle* shared_buffer_handle) {
RequestContext request_context;
MojoCreateSharedBufferOptions validated_options = {};
MojoResult result = SharedBufferDispatcher::ValidateCreateOptions(
options, &validated_options);
if (result != MOJO_RESULT_OK)
return result;
scoped_refptr<SharedBufferDispatcher> dispatcher;
result = SharedBufferDispatcher::Create(
validated_options, GetNodeController(), num_bytes, &dispatcher);
if (result != MOJO_RESULT_OK) {
DCHECK(!dispatcher);
return result;
}
*shared_buffer_handle = AddDispatcher(dispatcher);
if (*shared_buffer_handle == MOJO_HANDLE_INVALID) {
LOG(ERROR) << "Handle table full";
dispatcher->Close();
return MOJO_RESULT_RESOURCE_EXHAUSTED;
}
return MOJO_RESULT_OK;
}
MojoResult Core::DuplicateBufferHandle(
MojoHandle buffer_handle,
const MojoDuplicateBufferHandleOptions* options,
MojoHandle* new_buffer_handle) {
RequestContext request_context;
scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
// Don't verify |options| here; that's the dispatcher's job.
scoped_refptr<Dispatcher> new_dispatcher;
MojoResult result =
dispatcher->DuplicateBufferHandle(options, &new_dispatcher);
if (result != MOJO_RESULT_OK)
return result;
*new_buffer_handle = AddDispatcher(new_dispatcher);
if (*new_buffer_handle == MOJO_HANDLE_INVALID) {
LOG(ERROR) << "Handle table full";
new_dispatcher->Close();
return MOJO_RESULT_RESOURCE_EXHAUSTED;
}
return MOJO_RESULT_OK;
}
MojoResult Core::MapBuffer(MojoHandle buffer_handle,
uint64_t offset,
uint64_t num_bytes,
const MojoMapBufferOptions* options,
void** buffer) {
scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
if (options) {
if (options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (options->flags != MOJO_MAP_BUFFER_FLAG_NONE)
return MOJO_RESULT_UNIMPLEMENTED;
}
std::unique_ptr<PlatformSharedMemoryMapping> mapping;
MojoResult result = dispatcher->MapBuffer(offset, num_bytes, &mapping);
if (result != MOJO_RESULT_OK)
return result;
DCHECK(mapping);
void* address = mapping->GetBase();
{
base::AutoLock locker(mapping_table_lock_);
if (mapping_table_.size() >= GetConfiguration().max_mapping_table_size)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
auto emplace_result = mapping_table_.emplace(address, std::move(mapping));
DCHECK(emplace_result.second);
}
*buffer = address;
return MOJO_RESULT_OK;
}
MojoResult Core::UnmapBuffer(void* buffer) {
std::unique_ptr<PlatformSharedMemoryMapping> mapping;
// Destroy |mapping| while not holding the lock.
{
base::AutoLock lock(mapping_table_lock_);
auto iter = mapping_table_.find(buffer);
if (iter == mapping_table_.end())
return MOJO_RESULT_INVALID_ARGUMENT;
// Grab a reference so that it gets unmapped outside of this lock.
mapping = std::move(iter->second);
mapping_table_.erase(iter);
}
return MOJO_RESULT_OK;
}
MojoResult Core::GetBufferInfo(MojoHandle buffer_handle,
const MojoGetBufferInfoOptions* options,
MojoSharedBufferInfo* info) {
if (options) {
if (options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (options->flags != MOJO_GET_BUFFER_INFO_FLAG_NONE)
return MOJO_RESULT_UNIMPLEMENTED;
}
if (!info || info->struct_size < sizeof(MojoSharedBufferInfo))
return MOJO_RESULT_INVALID_ARGUMENT;
scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
return dispatcher->GetBufferInfo(info);
}
MojoResult Core::WrapPlatformHandle(
const MojoPlatformHandle* platform_handle,
const MojoWrapPlatformHandleOptions* options,
MojoHandle* mojo_handle) {
if (!platform_handle ||
platform_handle->struct_size < sizeof(*platform_handle)) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
auto handle = PlatformHandle::FromMojoPlatformHandle(platform_handle);
MojoHandle h =
AddDispatcher(PlatformHandleDispatcher::Create(std::move(handle)));
if (h == MOJO_HANDLE_INVALID)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
*mojo_handle = h;
return MOJO_RESULT_OK;
}
MojoResult Core::UnwrapPlatformHandle(
MojoHandle mojo_handle,
const MojoUnwrapPlatformHandleOptions* options,
MojoPlatformHandle* platform_handle) {
if (!platform_handle ||
platform_handle->struct_size < sizeof(*platform_handle)) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
scoped_refptr<Dispatcher> dispatcher;
{
base::AutoLock lock(handles_->GetLock());
dispatcher = handles_->GetDispatcher(mojo_handle);
if (!dispatcher ||
dispatcher->GetType() != Dispatcher::Type::PLATFORM_HANDLE)
return MOJO_RESULT_INVALID_ARGUMENT;
MojoResult result =
handles_->GetAndRemoveDispatcher(mojo_handle, &dispatcher);
if (result != MOJO_RESULT_OK)
return result;
}
PlatformHandleDispatcher* phd =
static_cast<PlatformHandleDispatcher*>(dispatcher.get());
PlatformHandle handle = phd->TakePlatformHandle();
phd->Close();
PlatformHandle::ToMojoPlatformHandle(std::move(handle), platform_handle);
return MOJO_RESULT_OK;
}
MojoResult Core::WrapPlatformSharedMemoryRegion(
const MojoPlatformHandle* platform_handles,
uint32_t num_platform_handles,
uint64_t size,
const MojoSharedBufferGuid* guid,
MojoPlatformSharedMemoryRegionAccessMode access_mode,
const MojoWrapPlatformSharedMemoryRegionOptions* options,
MojoHandle* mojo_handle) {
DCHECK(size);
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && \
!BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
if (access_mode == MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE) {
if (num_platform_handles != 2)
return MOJO_RESULT_INVALID_ARGUMENT;
}
#else
if (num_platform_handles != 1)
return MOJO_RESULT_INVALID_ARGUMENT;
#endif
PlatformHandle handles[2];
bool handles_ok = true;
for (size_t i = 0; i < num_platform_handles; ++i) {
handles[i] = PlatformHandle::FromMojoPlatformHandle(&platform_handles[i]);
if (!handles[i].is_valid())
handles_ok = false;
}
if (!handles_ok)
return MOJO_RESULT_INVALID_ARGUMENT;
std::optional<base::UnguessableToken> token =
mojo::internal::PlatformHandleInternal::UnmarshalUnguessableToken(guid);
if (!token.has_value()) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
base::subtle::PlatformSharedMemoryRegion::Mode mode;
switch (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:
return MOJO_RESULT_INVALID_ARGUMENT;
}
base::subtle::PlatformSharedMemoryRegion region =
base::subtle::PlatformSharedMemoryRegion::Take(
CreateSharedMemoryRegionHandleFromPlatformHandles(
std::move(handles[0]), std::move(handles[1])),
mode, size, token.value());
if (!region.IsValid())
return MOJO_RESULT_UNKNOWN;
scoped_refptr<SharedBufferDispatcher> dispatcher;
MojoResult result =
SharedBufferDispatcher::CreateFromPlatformSharedMemoryRegion(
std::move(region), &dispatcher);
if (result != MOJO_RESULT_OK)
return result;
MojoHandle h = AddDispatcher(dispatcher);
if (h == MOJO_HANDLE_INVALID) {
dispatcher->Close();
return MOJO_RESULT_RESOURCE_EXHAUSTED;
}
*mojo_handle = h;
return MOJO_RESULT_OK;
}
MojoResult Core::UnwrapPlatformSharedMemoryRegion(
MojoHandle mojo_handle,
const MojoUnwrapPlatformSharedMemoryRegionOptions* options,
MojoPlatformHandle* platform_handles,
uint32_t* num_platform_handles,
uint64_t* size,
MojoSharedBufferGuid* guid,
MojoPlatformSharedMemoryRegionAccessMode* access_mode) {
scoped_refptr<Dispatcher> dispatcher;
MojoResult result = MOJO_RESULT_OK;
{
base::AutoLock lock(handles_->GetLock());
result = handles_->GetAndRemoveDispatcher(mojo_handle, &dispatcher);
if (result != MOJO_RESULT_OK)
return result;
}
if (dispatcher->GetType() != Dispatcher::Type::SHARED_BUFFER) {
dispatcher->Close();
return MOJO_RESULT_INVALID_ARGUMENT;
}
SharedBufferDispatcher* shm_dispatcher =
static_cast<SharedBufferDispatcher*>(dispatcher.get());
base::subtle::PlatformSharedMemoryRegion region =
shm_dispatcher->PassPlatformSharedMemoryRegion();
DCHECK(region.IsValid());
DCHECK(size);
*size = region.GetSize();
*guid = mojo::internal::PlatformHandleInternal::MarshalUnguessableToken(
region.GetGUID());
DCHECK(access_mode);
switch (region.GetMode()) {
case base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly:
*access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY;
break;
case base::subtle::PlatformSharedMemoryRegion::Mode::kWritable:
*access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE;
break;
case base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe:
*access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE;
break;
default:
return MOJO_RESULT_INVALID_ARGUMENT;
}
PlatformHandle handle;
PlatformHandle read_only_handle;
ExtractPlatformHandlesFromSharedMemoryRegionHandle(
region.PassPlatformHandle(), &handle, &read_only_handle);
const uint32_t available_handle_storage_slots = *num_platform_handles;
if (available_handle_storage_slots < 1)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
*num_platform_handles = 1;
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && \
!BUILDFLAG(MOJO_USE_APPLE_CHANNEL)
if (region.GetMode() ==
base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) {
if (available_handle_storage_slots < 2)
return MOJO_RESULT_INVALID_ARGUMENT;
PlatformHandle::ToMojoPlatformHandle(std::move(read_only_handle),
&platform_handles[1]);
if (platform_handles[1].type == MOJO_PLATFORM_HANDLE_TYPE_INVALID)
return MOJO_RESULT_INVALID_ARGUMENT;
*num_platform_handles = 2;
}
#endif
PlatformHandle::ToMojoPlatformHandle(std::move(handle), &platform_handles[0]);
if (platform_handles[0].type == MOJO_PLATFORM_HANDLE_TYPE_INVALID)
return MOJO_RESULT_INVALID_ARGUMENT;
return MOJO_RESULT_OK;
}
MojoResult Core::CreateInvitation(const MojoCreateInvitationOptions* options,
MojoHandle* invitation_handle) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (!invitation_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
*invitation_handle = AddDispatcher(new InvitationDispatcher);
if (*invitation_handle == MOJO_HANDLE_INVALID)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
return MOJO_RESULT_OK;
}
MojoResult Core::AttachMessagePipeToInvitation(
MojoHandle invitation_handle,
const void* name,
uint32_t name_num_bytes,
const MojoAttachMessagePipeToInvitationOptions* options,
MojoHandle* message_pipe_handle) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (!message_pipe_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
if (name_num_bytes == 0)
return MOJO_RESULT_INVALID_ARGUMENT;
scoped_refptr<Dispatcher> dispatcher = GetDispatcher(invitation_handle);
if (!dispatcher || dispatcher->GetType() != Dispatcher::Type::INVITATION)
return MOJO_RESULT_INVALID_ARGUMENT;
auto* invitation_dispatcher =
static_cast<InvitationDispatcher*>(dispatcher.get());
RequestContext request_context;
ports::PortRef remote_peer_port;
MojoHandle local_handle = CreatePartialMessagePipe(&remote_peer_port);
if (local_handle == MOJO_HANDLE_INVALID)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
MojoResult result = invitation_dispatcher->AttachMessagePipe(
std::string_view(static_cast<const char*>(name), name_num_bytes),
std::move(remote_peer_port));
if (result != MOJO_RESULT_OK) {
Close(local_handle);
return result;
}
*message_pipe_handle = local_handle;
return MOJO_RESULT_OK;
}
MojoResult Core::ExtractMessagePipeFromInvitation(
MojoHandle invitation_handle,
const void* name,
uint32_t name_num_bytes,
const MojoExtractMessagePipeFromInvitationOptions* options,
MojoHandle* message_pipe_handle) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (!message_pipe_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
if (name_num_bytes == 0)
return MOJO_RESULT_INVALID_ARGUMENT;
RequestContext request_context;
std::string_view name_string(static_cast<const char*>(name), name_num_bytes);
scoped_refptr<Dispatcher> dispatcher = GetDispatcher(invitation_handle);
if (!dispatcher || dispatcher->GetType() != Dispatcher::Type::INVITATION)
return MOJO_RESULT_INVALID_ARGUMENT;
auto* invitation_dispatcher =
static_cast<InvitationDispatcher*>(dispatcher.get());
// First attempt to extract from the invitation object itself. This is for
// cases where this invitation was created in-process or is an accepted
// isolated invitation.
MojoResult extract_result = invitation_dispatcher->ExtractMessagePipe(
name_string, message_pipe_handle);
if (extract_result == MOJO_RESULT_OK ||
extract_result == MOJO_RESULT_RESOURCE_EXHAUSTED) {
return extract_result;
}
*message_pipe_handle =
ExtractMessagePipeFromInvitation(std::string(name_string));
if (*message_pipe_handle == MOJO_HANDLE_INVALID)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
return MOJO_RESULT_OK;
}
MojoResult Core::SendInvitation(
MojoHandle invitation_handle,
const MojoPlatformProcessHandle* process_handle,
const MojoInvitationTransportEndpoint* transport_endpoint,
MojoProcessErrorHandler error_handler,
uintptr_t error_handler_context,
const MojoSendInvitationOptions* options) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
base::Process target_process;
if (process_handle) {
MojoResult result =
UnwrapAndClonePlatformProcessHandle(process_handle, target_process);
if (result != MOJO_RESULT_OK)
return result;
}
ProcessErrorCallback process_error_callback;
if (error_handler) {
process_error_callback =
base::BindRepeating(&RunMojoProcessErrorHandler,
base::Owned(new ProcessDisconnectHandler(
error_handler, error_handler_context)),
error_handler, error_handler_context);
} else if (default_process_error_callback_) {
process_error_callback = default_process_error_callback_;
}
if (!transport_endpoint)
return MOJO_RESULT_INVALID_ARGUMENT;
if (transport_endpoint->struct_size < sizeof(*transport_endpoint))
return MOJO_RESULT_INVALID_ARGUMENT;
if (transport_endpoint->num_platform_handles == 0)
return MOJO_RESULT_INVALID_ARGUMENT;
if (!transport_endpoint->platform_handles)
return MOJO_RESULT_INVALID_ARGUMENT;
if (transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL &&
transport_endpoint->type !=
MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) {
return MOJO_RESULT_UNIMPLEMENTED;
}
scoped_refptr<Dispatcher> dispatcher = GetDispatcher(invitation_handle);
if (!dispatcher || dispatcher->GetType() != Dispatcher::Type::INVITATION)
return MOJO_RESULT_INVALID_ARGUMENT;
auto* invitation_dispatcher =
static_cast<InvitationDispatcher*>(dispatcher.get());
auto endpoint = PlatformHandle::FromMojoPlatformHandle(
&transport_endpoint->platform_handles[0]);
if (!endpoint.is_valid())
return MOJO_RESULT_INVALID_ARGUMENT;
ConnectionParams connection_params(
PlatformChannelEndpoint(std::move(endpoint)));
// At this point everything else has been validated, so we can take ownership
// of the dispatcher.
{
base::AutoLock lock(handles_->GetLock());
scoped_refptr<Dispatcher> removed_dispatcher;
MojoResult result = handles_->GetAndRemoveDispatcher(invitation_handle,
&removed_dispatcher);
if (result != MOJO_RESULT_OK) {
// Release ownership of the endpoint platform handle, per the API
// contract. The caller retains ownership on failure.
connection_params.TakeEndpoint().TakePlatformHandle().release();
return result;
}
DCHECK_EQ(removed_dispatcher.get(), invitation_dispatcher);
}
std::vector<std::pair<std::string, ports::PortRef>> attached_ports;
InvitationDispatcher::PortMapping attached_port_map =
invitation_dispatcher->TakeAttachedPorts();
invitation_dispatcher->Close();
for (auto& entry : attached_port_map)
attached_ports.emplace_back(entry.first, std::move(entry.second));
connection_params.set_is_untrusted_process(
options &&
(options->flags & MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS));
bool is_isolated =
options && (options->flags & MOJO_SEND_INVITATION_FLAG_ISOLATED);
RequestContext request_context;
if (is_isolated) {
DCHECK_EQ(attached_ports.size(), 1u);
DCHECK_EQ(attached_ports[0].first, kIsolatedInvitationPipeName);
std::string_view connection_name(options->isolated_connection_name,
options->isolated_connection_name_length);
GetNodeController()->ConnectIsolated(std::move(connection_params),
attached_ports[0].second,
connection_name);
} else {
if (transport_endpoint->type ==
MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) {
connection_params.set_is_async(true);
}
GetNodeController()->SendBrokerClientInvitation(
std::move(target_process), std::move(connection_params), attached_ports,
process_error_callback);
}
return MOJO_RESULT_OK;
}
MojoResult Core::AcceptInvitation(
const MojoInvitationTransportEndpoint* transport_endpoint,
const MojoAcceptInvitationOptions* options,
MojoHandle* invitation_handle) {
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
if (!transport_endpoint)
return MOJO_RESULT_INVALID_ARGUMENT;
if (transport_endpoint->struct_size < sizeof(*transport_endpoint))
return MOJO_RESULT_INVALID_ARGUMENT;
if (transport_endpoint->num_platform_handles == 0)
return MOJO_RESULT_INVALID_ARGUMENT;
if (!transport_endpoint->platform_handles)
return MOJO_RESULT_INVALID_ARGUMENT;
if (transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL &&
transport_endpoint->type !=
MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) {
return MOJO_RESULT_UNIMPLEMENTED;
}
if (!invitation_handle)
return MOJO_RESULT_INVALID_ARGUMENT;
auto dispatcher = base::MakeRefCounted<InvitationDispatcher>();
*invitation_handle = AddDispatcher(dispatcher);
if (*invitation_handle == MOJO_HANDLE_INVALID)
return MOJO_RESULT_RESOURCE_EXHAUSTED;
auto endpoint = PlatformHandle::FromMojoPlatformHandle(
&transport_endpoint->platform_handles[0]);
if (!endpoint.is_valid()) {
Close(*invitation_handle);
*invitation_handle = MOJO_HANDLE_INVALID;
return MOJO_RESULT_INVALID_ARGUMENT;
}
ConnectionParams connection_params(
PlatformChannelEndpoint(std::move(endpoint)));
if (options &&
options->flags & MOJO_ACCEPT_INVITATION_FLAG_LEAK_TRANSPORT_ENDPOINT) {
connection_params.set_leak_endpoint(true);
}
bool is_isolated =
options && (options->flags & MOJO_ACCEPT_INVITATION_FLAG_ISOLATED);
NodeController* const node_controller = GetNodeController();
RequestContext request_context;
if (is_isolated) {
// For an isolated invitation, we simply mint a new port pair here and send
// one name to the remote endpoint while stashing the other in the accepted
// invitation object for later extraction.
ports::PortRef local_port;
ports::PortRef remote_port;
node_controller->node()->CreatePortPair(&local_port, &remote_port);
node_controller->ConnectIsolated(std::move(connection_params), remote_port,
std::string_view());
MojoResult result =
dispatcher->AttachMessagePipe(kIsolatedInvitationPipeName, local_port);
DCHECK_EQ(MOJO_RESULT_OK, result);
} else {
if (transport_endpoint->type ==
MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) {
connection_params.set_is_async(true);
}
node_controller->AcceptBrokerClientInvitation(std::move(connection_params));
}
return MOJO_RESULT_OK;
}
MojoResult Core::SetQuota(MojoHandle handle,
MojoQuotaType type,
uint64_t limit,
const MojoSetQuotaOptions* options) {
RequestContext request_context;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
auto dispatcher = GetDispatcher(handle);
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
return dispatcher->SetQuota(type, limit);
}
MojoResult Core::QueryQuota(MojoHandle handle,
MojoQuotaType type,
const MojoQueryQuotaOptions* options,
uint64_t* limit,
uint64_t* usage) {
RequestContext request_context;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
auto dispatcher = GetDispatcher(handle);
if (!dispatcher)
return MOJO_RESULT_INVALID_ARGUMENT;
return dispatcher->QueryQuota(type, limit, usage);
}
MojoResult Core::SetDefaultProcessErrorHandler(
MojoDefaultProcessErrorHandler handler,
const MojoSetDefaultProcessErrorHandlerOptions* options) {
if (default_process_error_callback_ && handler)
return MOJO_RESULT_ALREADY_EXISTS;
if (!handler) {
default_process_error_callback_.Reset();
return MOJO_RESULT_OK;
}
default_process_error_callback_ = base::BindRepeating(
[](MojoDefaultProcessErrorHandler handler, const std::string& error) {
MojoProcessErrorDetails details = {0};
details.struct_size = sizeof(details);
details.error_message_length = static_cast<uint32_t>(error.size());
details.error_message = error.c_str();
handler(&details);
},
handler);
return MOJO_RESULT_OK;
}
void Core::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) {
base::AutoLock lock(handles_->GetLock());
handles_->GetActiveHandlesForTest(handles);
}
// static
void Core::PassNodeControllerToIOThread(
std::unique_ptr<NodeController> node_controller) {
// It's OK to leak this reference. At this point we know the IO loop is still
// running, and we know the NodeController will observe its eventual
// destruction. This tells the NodeController to delete itself when that
// happens.
node_controller.release()->DestroyOnIOThreadShutdown();
}
} // namespace core
} // namespace mojo