| // Copyright 2022 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_ipcz.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <cstdint> |
| #include <map> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check_op.h" |
| #include "base/containers/span.h" |
| #include "base/memory/platform_shared_memory_region.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/writable_shared_memory_region.h" |
| #include "base/no_destructor.h" |
| #include "base/notreached.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/strings/strcat.h" |
| #include "base/synchronization/lock.h" |
| #include "base/time/time.h" |
| #include "mojo/core/ipcz_api.h" |
| #include "mojo/core/ipcz_driver/data_pipe.h" |
| #include "mojo/core/ipcz_driver/invitation.h" |
| #include "mojo/core/ipcz_driver/mojo_message.h" |
| #include "mojo/core/ipcz_driver/mojo_trap.h" |
| #include "mojo/core/ipcz_driver/shared_buffer.h" |
| #include "mojo/core/ipcz_driver/shared_buffer_mapping.h" |
| #include "mojo/core/ipcz_driver/wrapped_platform_handle.h" |
| #include "mojo/public/cpp/platform/platform_handle.h" |
| #include "third_party/ipcz/include/ipcz/ipcz.h" |
| |
| namespace mojo::core { |
| |
| namespace { |
| |
| // Tracks active Mojo memory mappings by base address, since that's how the Mojo |
| // API identifies them for unmapping. |
| class MappingTable { |
| public: |
| MappingTable() = default; |
| ~MappingTable() = default; |
| |
| void Add(scoped_refptr<ipcz_driver::SharedBufferMapping> mapping) { |
| base::AutoLock lock(lock_); |
| void* address = mapping->memory(); |
| mappings_.emplace(address, std::move(mapping)); |
| } |
| |
| MojoResult Remove(void* address) { |
| base::AutoLock lock(lock_); |
| if (!mappings_.erase(address)) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return MOJO_RESULT_OK; |
| } |
| |
| private: |
| base::Lock lock_; |
| std::map<void*, scoped_refptr<ipcz_driver::SharedBufferMapping>> mappings_ |
| GUARDED_BY(lock_); |
| }; |
| |
| MappingTable& GetMappingTable() { |
| static base::NoDestructor<MappingTable> table; |
| return *table; |
| } |
| |
| // ipcz get and put operations differ slightly in their return code semantics as |
| // compared to Mojo read and write operations. These helpers perform the |
| // translation. |
| MojoResult GetMojoReadResultForIpczGet(IpczResult result) { |
| if (result == IPCZ_RESULT_UNAVAILABLE) { |
| // The peer is still open but there are not currently any parcels to read. |
| return MOJO_RESULT_SHOULD_WAIT; |
| } |
| if (result == IPCZ_RESULT_NOT_FOUND) { |
| // There are no more parcels to read and the peer is closed. |
| return MOJO_RESULT_FAILED_PRECONDITION; |
| } |
| return result; |
| } |
| |
| MojoResult GetMojoWriteResultForIpczPut(IpczResult result) { |
| if (result == IPCZ_RESULT_RESOURCE_EXHAUSTED) { |
| // For put operations with limits, which are used to emulate data pipe |
| // producer writes, this indicates that the caller needs to try again later |
| // due to the pipe being at capacity. |
| return MOJO_RESULT_SHOULD_WAIT; |
| } |
| if (result == IPCZ_RESULT_NOT_FOUND) { |
| // The peer is closed. |
| return MOJO_RESULT_FAILED_PRECONDITION; |
| } |
| return result; |
| } |
| |
| extern "C" { |
| |
| MojoResult MojoInitializeIpcz(const struct MojoInitializeOptions* options) { |
| NOTREACHED(); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoTimeTicks MojoGetTimeTicksNowIpcz() { |
| return base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds(); |
| } |
| |
| MojoResult MojoCloseIpcz(MojoHandle handle) { |
| if (handle == MOJO_HANDLE_INVALID) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return GetIpczAPI().Close(handle, IPCZ_NO_FLAGS, nullptr); |
| } |
| |
| MojoResult MojoQueryHandleSignalsStateIpcz( |
| MojoHandle handle, |
| MojoHandleSignalsState* signals_state) { |
| if (handle == MOJO_HANDLE_INVALID || !signals_state) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| auto* data_pipe = ipcz_driver::DataPipe::FromBox(handle); |
| if (data_pipe) { |
| data_pipe->FlushUpdatesFromPeer(); |
| if (!data_pipe->GetSignals(*signals_state)) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return MOJO_RESULT_OK; |
| } |
| |
| if (ipcz_driver::ObjectBase::FromBox(handle)) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| IpczPortalStatus status = {sizeof(status)}; |
| IpczResult result = |
| GetIpczAPI().QueryPortalStatus(handle, IPCZ_NO_FLAGS, nullptr, &status); |
| if (result != IPCZ_RESULT_OK) { |
| return result; |
| } |
| |
| signals_state->satisfiable_signals = |
| MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_QUOTA_EXCEEDED; |
| signals_state->satisfied_signals = 0; |
| if (status.flags & IPCZ_PORTAL_STATUS_PEER_CLOSED) { |
| signals_state->satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED; |
| } else { |
| signals_state->satisfiable_signals |= |
| MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_REMOTE; |
| signals_state->satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE; |
| } |
| if ((status.flags & IPCZ_PORTAL_STATUS_DEAD) == 0) { |
| signals_state->satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE; |
| } |
| if (status.num_local_parcels > 0) { |
| signals_state->satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE; |
| } |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoCreateMessagePipeIpcz( |
| const MojoCreateMessagePipeOptions* options, |
| MojoHandle* message_pipe_handle0, |
| MojoHandle* message_pipe_handle1) { |
| return GetIpczAPI().OpenPortals(GetIpczNode(), IPCZ_NO_FLAGS, nullptr, |
| message_pipe_handle0, message_pipe_handle1); |
| } |
| |
| MojoResult MojoWriteMessageIpcz(MojoHandle message_pipe_handle, |
| MojoMessageHandle message, |
| const MojoWriteMessageOptions* options) { |
| auto m = ipcz_driver::MojoMessage::TakeFromHandle(message); |
| if (!m || !message_pipe_handle) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| if (m->context()) { |
| // Wrap unserialized messages in boxes to be serialized lazily. |
| ScopedIpczHandle box = ipcz_driver::MojoMessage::Box(std::move(m)); |
| const IpczResult result = GetIpczAPI().Put( |
| message_pipe_handle, nullptr, 0, &box.get(), 1, IPCZ_NO_FLAGS, nullptr); |
| if (result == IPCZ_RESULT_OK) { |
| // On success, ownership of the box is passed into the portal. |
| std::ignore = box.release(); |
| } |
| return result; |
| } |
| |
| m->AttachDataPipePortals(); |
| const IpczResult result = GetIpczAPI().Put( |
| message_pipe_handle, m->data().data(), m->data().size(), |
| m->handles().data(), m->handles().size(), IPCZ_NO_FLAGS, nullptr); |
| if (result == IPCZ_RESULT_NOT_FOUND) { |
| return MOJO_RESULT_FAILED_PRECONDITION; |
| } |
| |
| if (result == IPCZ_RESULT_OK) { |
| // Ensure the handles don't get freed on MojoMessage destruction, as their |
| // ownership was relinquished in Put() above. |
| m->handles().clear(); |
| } |
| |
| return GetMojoWriteResultForIpczPut(result); |
| } |
| |
| MojoResult MojoReadMessageIpcz(MojoHandle message_pipe_handle, |
| const MojoReadMessageOptions* options, |
| MojoMessageHandle* message) { |
| ScopedIpczHandle parcel; |
| IpczResult result = GetIpczAPI().Get( |
| message_pipe_handle, IPCZ_GET_PARTIAL, nullptr, nullptr, nullptr, nullptr, |
| nullptr, ScopedIpczHandle::Receiver(parcel)); |
| if (result != IPCZ_RESULT_OK) { |
| return GetMojoReadResultForIpczGet(result); |
| } |
| |
| auto new_message = std::make_unique<ipcz_driver::MojoMessage>(); |
| new_message->SetParcel(std::move(parcel)); |
| if (auto wrapped = ipcz_driver::MojoMessage::UnwrapFrom(*new_message)) { |
| *message = wrapped.release()->handle(); |
| return IPCZ_RESULT_OK; |
| } |
| |
| *message = new_message.release()->handle(); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoFuseMessagePipesIpcz( |
| MojoHandle handle0, |
| MojoHandle handle1, |
| const MojoFuseMessagePipesOptions* options) { |
| if (handle0 == MOJO_HANDLE_INVALID || handle1 == MOJO_HANDLE_INVALID) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| const IpczResult result = |
| GetIpczAPI().MergePortals(handle0, handle1, IPCZ_NO_FLAGS, nullptr); |
| if (result != IPCZ_RESULT_OK) { |
| // On failure, MojoFuseMessagePipes is expected to close the message pipe |
| // endpoints it was given. |
| MojoCloseIpcz(handle0); |
| MojoCloseIpcz(handle1); |
| return MOJO_RESULT_FAILED_PRECONDITION; |
| } |
| return result; |
| } |
| |
| MojoResult MojoCreateMessageIpcz(const MojoCreateMessageOptions* options, |
| MojoMessageHandle* message) { |
| if (!message) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| auto new_message = std::make_unique<ipcz_driver::MojoMessage>(); |
| *message = new_message.release()->handle(); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoDestroyMessageIpcz(MojoMessageHandle message) { |
| if (message == MOJO_MESSAGE_HANDLE_INVALID) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| std::unique_ptr<ipcz_driver::MojoMessage> scoped_message( |
| ipcz_driver::MojoMessage::FromHandle(message)); |
| return scoped_message ? MOJO_RESULT_OK : MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| MojoResult MojoSerializeMessageIpcz( |
| MojoMessageHandle message, |
| const MojoSerializeMessageOptions* options) { |
| if (auto* m = ipcz_driver::MojoMessage::FromHandle(message)) { |
| return m->Serialize(); |
| } |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| MojoResult MojoAppendMessageDataIpcz( |
| MojoMessageHandle message, |
| uint32_t additional_payload_size, |
| const MojoHandle* handles, |
| uint32_t num_handles, |
| const MojoAppendMessageDataOptions* options, |
| void** buffer, |
| uint32_t* buffer_size) { |
| if (auto* m = ipcz_driver::MojoMessage::FromHandle(message)) { |
| const bool commit_size = |
| options && (options->flags & MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE); |
| return m->AppendData(additional_payload_size, handles, num_handles, buffer, |
| buffer_size, commit_size); |
| } |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| MojoResult MojoGetMessageDataIpcz(MojoMessageHandle message, |
| const MojoGetMessageDataOptions* options, |
| void** buffer, |
| uint32_t* num_bytes, |
| MojoHandle* handles, |
| uint32_t* num_handles) { |
| if (auto* m = ipcz_driver::MojoMessage::FromHandle(message)) { |
| const bool consume_handles = |
| !options || |
| ((options->flags & MOJO_GET_MESSAGE_DATA_FLAG_IGNORE_HANDLES) == 0); |
| return m->GetData(buffer, num_bytes, handles, num_handles, consume_handles); |
| } |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| MojoResult MojoSetMessageContextIpcz( |
| MojoMessageHandle message_handle, |
| uintptr_t context, |
| MojoMessageContextSerializer serializer, |
| MojoMessageContextDestructor destructor, |
| const MojoSetMessageContextOptions* options) { |
| auto* message = ipcz_driver::MojoMessage::FromHandle(message_handle); |
| if (!message) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| return message->SetContext(context, serializer, destructor); |
| } |
| |
| MojoResult MojoGetMessageContextIpcz( |
| MojoMessageHandle message_handle, |
| const MojoGetMessageContextOptions* options, |
| uintptr_t* context) { |
| auto* message = ipcz_driver::MojoMessage::FromHandle(message_handle); |
| if (!message) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| if (!message->context()) { |
| return MOJO_RESULT_NOT_FOUND; |
| } |
| *context = message->context(); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoNotifyBadMessageIpcz( |
| MojoMessageHandle message_handle, |
| const char* error, |
| uint32_t error_num_bytes, |
| const MojoNotifyBadMessageOptions* options) { |
| auto* message = ipcz_driver::MojoMessage::FromHandle(message_handle); |
| if (!message) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| const std::string error_string(error, error_num_bytes); |
| if (message->parcel() != IPCZ_INVALID_HANDLE) { |
| // Mojo prefixes bad message reports with this string if they're for |
| // messages from a remote node. We duplicate it here since many tests expect |
| // observation of prefixed error messages. |
| const char kPrefix[] = "Received bad user message: "; |
| auto prefixed_error_message = |
| std::make_unique<std::string>(base::StrCat({kPrefix, error_string})); |
| const IpczResult result = GetIpczAPI().Reject( |
| message->parcel(), |
| reinterpret_cast<uintptr_t>(prefixed_error_message.get()), |
| IPCZ_NO_FLAGS, nullptr); |
| if (result == IPCZ_RESULT_OK) { |
| // Ownership taken by driver. |
| std::ignore = prefixed_error_message.release(); |
| return IPCZ_RESULT_OK; |
| } |
| DCHECK_EQ(result, IPCZ_RESULT_FAILED_PRECONDITION); |
| } |
| |
| // The parcel was not from a remote node in this case. |
| ipcz_driver::Invitation::InvokeDefaultProcessErrorHandler(error_string); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoCreateDataPipeIpcz(const MojoCreateDataPipeOptions* options, |
| MojoHandle* data_pipe_producer_handle, |
| MojoHandle* data_pipe_consumer_handle) { |
| using DataPipe = ipcz_driver::DataPipe; |
| |
| // Mojo Core defaults to 64 kB capacity and 1-byte elements, and many callers |
| // assume these defaults. |
| constexpr uint32_t kDefaultCapacity = 64 * 1024; |
| DataPipe::Config config = {.element_size = 1, |
| .byte_capacity = kDefaultCapacity, |
| .is_peer_closed = false}; |
| if (options) { |
| config.element_size = options->element_num_bytes; |
| if (options->capacity_num_bytes) { |
| config.byte_capacity = options->capacity_num_bytes; |
| } |
| } |
| if (!config.byte_capacity || !config.element_size || |
| config.byte_capacity < config.element_size) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| absl::optional<DataPipe::Pair> pipe = DataPipe::CreatePair(config); |
| if (!pipe) { |
| // This result implies that we failed to allocate or map a new shared memory |
| // region and therefore have no transfer buffer for the pipe. |
| return MOJO_RESULT_RESOURCE_EXHAUSTED; |
| } |
| |
| *data_pipe_producer_handle = DataPipe::Box(std::move(pipe->producer)); |
| *data_pipe_consumer_handle = DataPipe::Box(std::move(pipe->consumer)); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoWriteDataIpcz(MojoHandle data_pipe_producer_handle, |
| const void* elements, |
| uint32_t* num_bytes, |
| const MojoWriteDataOptions* options) { |
| auto* pipe = ipcz_driver::DataPipe::FromBox(data_pipe_producer_handle); |
| if (!pipe || !num_bytes || !pipe->is_producer()) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return pipe->WriteData(elements, *num_bytes, |
| options ? options->flags : MOJO_WRITE_DATA_FLAG_NONE); |
| } |
| |
| MojoResult MojoBeginWriteDataIpcz(MojoHandle data_pipe_producer_handle, |
| const MojoBeginWriteDataOptions* options, |
| void** buffer, |
| uint32_t* buffer_num_bytes) { |
| auto* pipe = ipcz_driver::DataPipe::FromBox(data_pipe_producer_handle); |
| if (!pipe || !buffer || !buffer_num_bytes || !pipe->is_producer()) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| if (options && options->struct_size < sizeof(*options)) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| const MojoBeginWriteDataFlags flags = |
| options ? options->flags : MOJO_BEGIN_WRITE_DATA_FLAG_NONE; |
| return pipe->BeginWriteData(*buffer, *buffer_num_bytes, flags); |
| } |
| |
| MojoResult MojoEndWriteDataIpcz(MojoHandle data_pipe_producer_handle, |
| uint32_t num_bytes_produced, |
| const MojoEndWriteDataOptions* options) { |
| auto* pipe = ipcz_driver::DataPipe::FromBox(data_pipe_producer_handle); |
| if (!pipe || !pipe->is_producer()) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return pipe->EndWriteData(num_bytes_produced); |
| } |
| |
| MojoResult MojoReadDataIpcz(MojoHandle data_pipe_consumer_handle, |
| const MojoReadDataOptions* options, |
| void* elements, |
| uint32_t* num_bytes) { |
| const MojoReadDataFlags flags = |
| options ? options->flags : MOJO_READ_DATA_FLAG_NONE; |
| auto* pipe = ipcz_driver::DataPipe::FromBox(data_pipe_consumer_handle); |
| if (!pipe || !num_bytes || !pipe->is_consumer()) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return pipe->ReadData(elements, *num_bytes, flags); |
| } |
| |
| MojoResult MojoBeginReadDataIpcz(MojoHandle data_pipe_consumer_handle, |
| const MojoBeginReadDataOptions* options, |
| const void** buffer, |
| uint32_t* buffer_num_bytes) { |
| auto* pipe = ipcz_driver::DataPipe::FromBox(data_pipe_consumer_handle); |
| if (!pipe || !buffer || !buffer_num_bytes || !pipe->is_consumer()) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return pipe->BeginReadData(*buffer, *buffer_num_bytes); |
| } |
| |
| MojoResult MojoEndReadDataIpcz(MojoHandle data_pipe_consumer_handle, |
| uint32_t num_bytes_consumed, |
| const MojoEndReadDataOptions* options) { |
| auto* pipe = ipcz_driver::DataPipe::FromBox(data_pipe_consumer_handle); |
| if (!pipe || !pipe->is_consumer()) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return pipe->EndReadData(num_bytes_consumed); |
| } |
| |
| MojoResult MojoCreateSharedBufferIpcz( |
| uint64_t num_bytes, |
| const MojoCreateSharedBufferOptions* options, |
| MojoHandle* shared_buffer_handle) { |
| auto region = base::WritableSharedMemoryRegion::Create(num_bytes); |
| if (!region.IsValid()) { |
| return MOJO_RESULT_RESOURCE_EXHAUSTED; |
| } |
| |
| *shared_buffer_handle = ipcz_driver::SharedBuffer::MakeBoxed( |
| base::WritableSharedMemoryRegion::TakeHandleForSerialization( |
| std::move(region))); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoDuplicateBufferHandleIpcz( |
| MojoHandle buffer_handle, |
| const MojoDuplicateBufferHandleOptions* options, |
| MojoHandle* new_buffer_handle) { |
| auto* buffer = ipcz_driver::SharedBuffer::FromBox(buffer_handle); |
| if (!buffer || !new_buffer_handle || |
| (options && options->struct_size < sizeof(*options))) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| const bool read_only = |
| options && |
| (options->flags & MOJO_DUPLICATE_BUFFER_HANDLE_FLAG_READ_ONLY) != 0; |
| auto [new_buffer, result] = buffer->Duplicate(read_only); |
| if (result != IPCZ_RESULT_OK) { |
| return result; |
| } |
| |
| *new_buffer_handle = ipcz_driver::SharedBuffer::Box(std::move(new_buffer)); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoMapBufferIpcz(MojoHandle buffer_handle, |
| uint64_t offset, |
| uint64_t num_bytes, |
| const MojoMapBufferOptions* options, |
| void** address) { |
| auto* buffer = ipcz_driver::SharedBuffer::FromBox(buffer_handle); |
| if (!buffer) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| auto mapping = ipcz_driver::SharedBufferMapping::Create( |
| buffer->region(), static_cast<size_t>(offset), |
| static_cast<size_t>(num_bytes)); |
| if (!mapping) { |
| return MOJO_RESULT_RESOURCE_EXHAUSTED; |
| } |
| *address = mapping->memory(); |
| GetMappingTable().Add(std::move(mapping)); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoUnmapBufferIpcz(void* address) { |
| return GetMappingTable().Remove(address); |
| } |
| |
| MojoResult MojoGetBufferInfoIpcz(MojoHandle buffer_handle, |
| const MojoGetBufferInfoOptions* options, |
| MojoSharedBufferInfo* info) { |
| auto* buffer = ipcz_driver::SharedBuffer::FromBox(buffer_handle); |
| if (!buffer || !info || info->struct_size < sizeof(*info)) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| info->size = buffer->region().GetSize(); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoCreateTrapIpcz(MojoTrapEventHandler handler, |
| const MojoCreateTrapOptions* options, |
| MojoHandle* trap_handle) { |
| if (!handler || !trap_handle) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| *trap_handle = ipcz_driver::MojoTrap::MakeBoxed(handler); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoAddTriggerIpcz(MojoHandle trap_handle, |
| MojoHandle handle, |
| MojoHandleSignals signals, |
| MojoTriggerCondition condition, |
| uintptr_t context, |
| const MojoAddTriggerOptions* options) { |
| auto* trap = ipcz_driver::MojoTrap::FromBox(trap_handle); |
| if (!trap) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return trap->AddTrigger(handle, signals, condition, context); |
| } |
| |
| MojoResult MojoRemoveTriggerIpcz(MojoHandle trap_handle, |
| uintptr_t context, |
| const MojoRemoveTriggerOptions* options) { |
| auto* trap = ipcz_driver::MojoTrap::FromBox(trap_handle); |
| if (!trap) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return trap->RemoveTrigger(context); |
| } |
| |
| MojoResult MojoArmTrapIpcz(MojoHandle trap_handle, |
| const MojoArmTrapOptions* options, |
| uint32_t* num_blocking_events, |
| MojoTrapEvent* blocking_events) { |
| auto* trap = ipcz_driver::MojoTrap::FromBox(trap_handle); |
| if (!trap) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return trap->Arm(blocking_events, num_blocking_events); |
| } |
| |
| MojoResult MojoWrapPlatformHandleIpcz( |
| const MojoPlatformHandle* platform_handle, |
| const MojoWrapPlatformHandleOptions* options, |
| MojoHandle* mojo_handle) { |
| if (!platform_handle || !mojo_handle || |
| platform_handle->struct_size < sizeof(*platform_handle)) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| auto handle = PlatformHandle::FromMojoPlatformHandle(platform_handle); |
| *mojo_handle = |
| ipcz_driver::WrappedPlatformHandle::MakeBoxed(std::move(handle)); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoUnwrapPlatformHandleIpcz( |
| MojoHandle mojo_handle, |
| const MojoUnwrapPlatformHandleOptions* options, |
| MojoPlatformHandle* platform_handle) { |
| if (!mojo_handle || !platform_handle || |
| platform_handle->struct_size < sizeof(*platform_handle)) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| auto wrapper = ipcz_driver::WrappedPlatformHandle::Unbox(mojo_handle); |
| if (!wrapper) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| PlatformHandle::ToMojoPlatformHandle(std::move(wrapper->handle()), |
| platform_handle); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoWrapPlatformSharedMemoryRegionIpcz( |
| const MojoPlatformHandle* platform_handles, |
| uint32_t num_platform_handles, |
| uint64_t num_bytes, |
| const MojoSharedBufferGuid* guid, |
| MojoPlatformSharedMemoryRegionAccessMode access_mode, |
| const MojoWrapPlatformSharedMemoryRegionOptions* options, |
| MojoHandle* mojo_handle) { |
| if (!platform_handles || !num_bytes || !guid || !mojo_handle) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| auto buffer = ipcz_driver::SharedBuffer::CreateForMojoWrapper( |
| base::make_span(platform_handles, num_platform_handles), num_bytes, *guid, |
| access_mode); |
| if (!buffer) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| *mojo_handle = ipcz_driver::SharedBuffer::Box(std::move(buffer)); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoUnwrapPlatformSharedMemoryRegionIpcz( |
| MojoHandle mojo_handle, |
| const MojoUnwrapPlatformSharedMemoryRegionOptions* options, |
| MojoPlatformHandle* platform_handles, |
| uint32_t* num_platform_handles, |
| uint64_t* num_bytes, |
| MojoSharedBufferGuid* mojo_guid, |
| MojoPlatformSharedMemoryRegionAccessMode* access_mode) { |
| if (!mojo_handle || !platform_handles || !num_platform_handles || |
| !mojo_guid) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| auto* buffer = ipcz_driver::SharedBuffer::FromBox(mojo_handle); |
| if (!buffer) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| using Mode = base::subtle::PlatformSharedMemoryRegion::Mode; |
| const Mode mode = buffer->region().GetMode(); |
| const base::UnguessableToken guid = buffer->region().GetGUID(); |
| const uint32_t size = static_cast<uint32_t>(buffer->region().GetSize()); |
| |
| uint32_t capacity = *num_platform_handles; |
| uint32_t required_handles = 1; |
| #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) |
| if (buffer->region().GetMode() == |
| base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { |
| required_handles = 2; |
| } |
| #endif |
| *num_platform_handles = required_handles; |
| if (capacity < required_handles) { |
| return MOJO_RESULT_RESOURCE_EXHAUSTED; |
| } |
| |
| PlatformHandle handles[2]; |
| base::subtle::ScopedPlatformSharedMemoryHandle region_handle = |
| buffer->region().PassPlatformHandle(); |
| #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) |
| handles[0] = PlatformHandle(std::move(region_handle.fd)); |
| handles[1] = PlatformHandle(std::move(region_handle.readonly_fd)); |
| #else |
| handles[0] = PlatformHandle(std::move(region_handle)); |
| #endif |
| |
| for (size_t i = 0; i < required_handles; ++i) { |
| PlatformHandle::ToMojoPlatformHandle(std::move(handles[i]), |
| &platform_handles[i]); |
| } |
| |
| *num_bytes = size; |
| mojo_guid->high = guid.GetHighForSerialization(); |
| mojo_guid->low = guid.GetLowForSerialization(); |
| |
| switch (mode) { |
| case Mode::kReadOnly: |
| *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY; |
| break; |
| case Mode::kWritable: |
| *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE; |
| break; |
| case Mode::kUnsafe: |
| *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE; |
| break; |
| default: |
| *access_mode = MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY; |
| NOTREACHED(); |
| } |
| |
| std::ignore = ipcz_driver::SharedBuffer::Unbox(mojo_handle); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoCreateInvitationIpcz(const MojoCreateInvitationOptions* options, |
| MojoHandle* invitation_handle) { |
| if (!invitation_handle || |
| (options && options->struct_size < sizeof(*options))) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| *invitation_handle = ipcz_driver::Invitation::MakeBoxed(); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoAttachMessagePipeToInvitationIpcz( |
| MojoHandle invitation_handle, |
| const void* name, |
| uint32_t name_num_bytes, |
| const MojoAttachMessagePipeToInvitationOptions* options, |
| MojoHandle* message_pipe_handle) { |
| auto* invitation = ipcz_driver::Invitation::FromBox(invitation_handle); |
| if (!invitation || !message_pipe_handle || |
| (options && options->struct_size < sizeof(*options))) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return invitation->Attach( |
| base::make_span(static_cast<const uint8_t*>(name), name_num_bytes), |
| message_pipe_handle); |
| } |
| |
| MojoResult MojoExtractMessagePipeFromInvitationIpcz( |
| MojoHandle invitation_handle, |
| const void* name, |
| uint32_t name_num_bytes, |
| const MojoExtractMessagePipeFromInvitationOptions* options, |
| MojoHandle* message_pipe_handle) { |
| auto* invitation = ipcz_driver::Invitation::FromBox(invitation_handle); |
| if (!invitation || !message_pipe_handle || |
| (options && options->struct_size < sizeof(*options))) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| return invitation->Extract( |
| base::make_span(static_cast<const uint8_t*>(name), name_num_bytes), |
| message_pipe_handle); |
| } |
| |
| MojoResult MojoSendInvitationIpcz( |
| MojoHandle invitation_handle, |
| const MojoPlatformProcessHandle* process_handle, |
| const MojoInvitationTransportEndpoint* transport_endpoint, |
| MojoProcessErrorHandler error_handler, |
| uintptr_t error_handler_context, |
| const MojoSendInvitationOptions* options) { |
| auto* invitation = ipcz_driver::Invitation::FromBox(invitation_handle); |
| if (!invitation) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| |
| const MojoResult result = |
| invitation->Send(process_handle, transport_endpoint, error_handler, |
| error_handler_context, options); |
| if (result == MOJO_RESULT_OK) { |
| // On success, the invitation is consumed. |
| GetIpczAPI().Close(invitation_handle, IPCZ_NO_FLAGS, nullptr); |
| } |
| return result; |
| } |
| |
| MojoResult MojoAcceptInvitationIpcz( |
| const MojoInvitationTransportEndpoint* transport_endpoint, |
| const MojoAcceptInvitationOptions* options, |
| MojoHandle* invitation_handle) { |
| if (!transport_endpoint || !invitation_handle || |
| (options && options->struct_size < sizeof(*options))) { |
| return MOJO_RESULT_INVALID_ARGUMENT; |
| } |
| *invitation_handle = |
| ipcz_driver::Invitation::Accept(transport_endpoint, options); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoSetQuotaIpcz(MojoHandle handle, |
| MojoQuotaType type, |
| uint64_t limit, |
| const MojoSetQuotaOptions* options) { |
| return MOJO_RESULT_UNIMPLEMENTED; |
| } |
| |
| MojoResult MojoQueryQuotaIpcz(MojoHandle handle, |
| MojoQuotaType type, |
| const MojoQueryQuotaOptions* options, |
| uint64_t* current_limit, |
| uint64_t* current_usage) { |
| IpczPortalStatus status = {.size = sizeof(status)}; |
| const IpczResult result = |
| GetIpczAPI().QueryPortalStatus(handle, IPCZ_NO_FLAGS, nullptr, &status); |
| DCHECK_EQ(result, IPCZ_RESULT_OK); |
| if (type == MOJO_QUOTA_TYPE_RECEIVE_QUEUE_LENGTH && current_usage) { |
| *current_usage = status.num_local_parcels; |
| } else if (type == MOJO_QUOTA_TYPE_RECEIVE_QUEUE_MEMORY_SIZE && |
| current_usage) { |
| *current_usage = status.num_local_bytes; |
| } else if (current_usage) { |
| *current_usage = 0; |
| } |
| if (current_limit) { |
| *current_limit = 0; |
| } |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoShutdownIpcz(const MojoShutdownOptions* options) { |
| NOTREACHED(); |
| return MOJO_RESULT_OK; |
| } |
| |
| MojoResult MojoSetDefaultProcessErrorHandlerIpcz( |
| MojoDefaultProcessErrorHandler handler, |
| const MojoSetDefaultProcessErrorHandlerOptions* options) { |
| ipcz_driver::Invitation::SetDefaultProcessErrorHandler(handler); |
| return MOJO_RESULT_OK; |
| } |
| |
| } // extern "C" |
| |
| MojoSystemThunks2 g_mojo_ipcz_thunks = { |
| sizeof(MojoSystemThunks2), |
| MojoInitializeIpcz, |
| MojoGetTimeTicksNowIpcz, |
| MojoCloseIpcz, |
| MojoQueryHandleSignalsStateIpcz, |
| MojoCreateMessagePipeIpcz, |
| MojoWriteMessageIpcz, |
| MojoReadMessageIpcz, |
| MojoFuseMessagePipesIpcz, |
| MojoCreateMessageIpcz, |
| MojoDestroyMessageIpcz, |
| MojoSerializeMessageIpcz, |
| MojoAppendMessageDataIpcz, |
| MojoGetMessageDataIpcz, |
| MojoSetMessageContextIpcz, |
| MojoGetMessageContextIpcz, |
| MojoNotifyBadMessageIpcz, |
| MojoCreateDataPipeIpcz, |
| MojoWriteDataIpcz, |
| MojoBeginWriteDataIpcz, |
| MojoEndWriteDataIpcz, |
| MojoReadDataIpcz, |
| MojoBeginReadDataIpcz, |
| MojoEndReadDataIpcz, |
| MojoCreateSharedBufferIpcz, |
| MojoDuplicateBufferHandleIpcz, |
| MojoMapBufferIpcz, |
| MojoUnmapBufferIpcz, |
| MojoGetBufferInfoIpcz, |
| MojoCreateTrapIpcz, |
| MojoAddTriggerIpcz, |
| MojoRemoveTriggerIpcz, |
| MojoArmTrapIpcz, |
| MojoWrapPlatformHandleIpcz, |
| MojoUnwrapPlatformHandleIpcz, |
| MojoWrapPlatformSharedMemoryRegionIpcz, |
| MojoUnwrapPlatformSharedMemoryRegionIpcz, |
| MojoCreateInvitationIpcz, |
| MojoAttachMessagePipeToInvitationIpcz, |
| MojoExtractMessagePipeFromInvitationIpcz, |
| MojoSendInvitationIpcz, |
| MojoAcceptInvitationIpcz, |
| MojoSetQuotaIpcz, |
| MojoQueryQuotaIpcz, |
| MojoShutdownIpcz, |
| MojoSetDefaultProcessErrorHandlerIpcz}; |
| |
| } // namespace |
| |
| const MojoSystemThunks2* GetMojoIpczImpl() { |
| return &g_mojo_ipcz_thunks; |
| } |
| |
| } // namespace mojo::core |