| // 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. |
| |
| #include "mojo/core/core.h" |
| |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/containers/stack_container.h" |
| #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/strings/string_piece.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 base::StringPiece 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, |
| base::StringPiece 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::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; |
| |
| absl::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( |
| base::StringPiece(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; |
| |
| base::StringPiece 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_SERVER && |
| 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; |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX) |
| if (transport_endpoint->type == |
| MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) { |
| connection_params = |
| ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint))); |
| } |
| #endif |
| if (!connection_params.server_endpoint().is_valid()) { |
| connection_params = |
| ConnectionParams(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(); |
| connection_params.TakeServerEndpoint().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); |
| base::StringPiece 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_SERVER && |
| 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; |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX) |
| if (transport_endpoint->type == |
| MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) { |
| connection_params = |
| ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint))); |
| } |
| #endif |
| if (!connection_params.server_endpoint().is_valid()) { |
| connection_params = |
| ConnectionParams(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, |
| base::StringPiece()); |
| 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 |