// Copyright 2018 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/public/cpp/system/invitation.h" #include #include #include #include "base/numerics/safe_conversions.h" #include "build/build_config.h" #include "mojo/core/embedder/embedder.h" #include "mojo/public/c/system/invitation.h" #include "mojo/public/c/system/platform_handle.h" #include "mojo/public/cpp/system/platform_handle.h" #if BUILDFLAG(IS_WIN) #include #endif #if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) #include "mojo/public/cpp/platform/platform_channel_server.h" #endif namespace mojo { namespace { static constexpr base::StringPiece kIsolatedPipeName = {"\0\0\0\0", 4}; void ProcessHandleToMojoProcessHandle(base::ProcessHandle target_process, MojoPlatformProcessHandle* handle) { handle->struct_size = sizeof(*handle); #if BUILDFLAG(IS_WIN) handle->value = static_cast(reinterpret_cast(target_process)); #else handle->value = static_cast(target_process); #endif } void PlatformHandleToTransportEndpoint( PlatformHandle platform_handle, MojoPlatformHandle* endpoint_handle, MojoInvitationTransportEndpoint* endpoint) { PlatformHandle::ToMojoPlatformHandle(std::move(platform_handle), endpoint_handle); CHECK_NE(endpoint_handle->type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); endpoint->struct_size = sizeof(*endpoint); endpoint->num_platform_handles = 1; endpoint->platform_handles = endpoint_handle; } void RunErrorCallback(uintptr_t context, const MojoProcessErrorDetails* details) { auto* callback = reinterpret_cast(context); std::string error_message; if (details->error_message) { error_message = std::string(details->error_message, details->error_message_length); callback->Run(error_message); } else if (details->flags & MOJO_PROCESS_ERROR_FLAG_DISCONNECTED) { delete callback; } } void SendInvitation(ScopedInvitationHandle invitation, base::ProcessHandle target_process, PlatformHandle endpoint_handle, MojoInvitationTransportType transport_type, MojoSendInvitationFlags flags, const ProcessErrorCallback& error_callback, base::StringPiece isolated_connection_name) { std::unique_ptr process_handle; if (target_process != base::kNullProcessHandle) { process_handle = std::make_unique(); ProcessHandleToMojoProcessHandle(target_process, process_handle.get()); } MojoPlatformHandle platform_handle; MojoInvitationTransportEndpoint endpoint; PlatformHandleToTransportEndpoint(std::move(endpoint_handle), &platform_handle, &endpoint); endpoint.type = transport_type; MojoProcessErrorHandler error_handler = nullptr; uintptr_t error_handler_context = 0; if (error_callback) { error_handler = &RunErrorCallback; // NOTE: The allocated callback is effectively owned by the error handler, // which will delete it on the final invocation for this context (i.e. // process disconnection). error_handler_context = reinterpret_cast(new ProcessErrorCallback(error_callback)); } MojoSendInvitationOptions options; options.struct_size = sizeof(options); options.flags = flags; if (flags & MOJO_SEND_INVITATION_FLAG_ISOLATED) { options.isolated_connection_name = isolated_connection_name.data(); options.isolated_connection_name_length = static_cast(isolated_connection_name.size()); } MojoResult result = MojoSendInvitation( invitation.get().value(), process_handle.get(), &endpoint, error_handler, error_handler_context, &options); // If successful, the invitation handle is already closed for us. if (result == MOJO_RESULT_OK) std::ignore = invitation.release(); } #if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) void WaitForServerConnection( PlatformChannelServerEndpoint server_endpoint, PlatformChannelServer::ConnectionCallback callback) { core::GetIOTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&PlatformChannelServer::WaitForConnection, std::move(server_endpoint), std::move(callback))); } base::Process CloneProcessFromHandle(base::ProcessHandle handle) { if (handle == base::kNullProcessHandle) { return base::Process{}; } #if BUILDFLAG(IS_WIN) // We can't use the hack below on Windows, because handle verification will // explode when a new Process instance tries to own the already-owned // `handle`. HANDLE new_handle; BOOL ok = ::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), &new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS); CHECK(ok); return base::Process(new_handle); #else base::Process temporary_owner(handle); base::Process clone = temporary_owner.Duplicate(); std::ignore = temporary_owner.Release(); return clone; #endif } #endif // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) } // namespace OutgoingInvitation::OutgoingInvitation() { MojoHandle invitation_handle; MojoResult result = MojoCreateInvitation(nullptr, &invitation_handle); DCHECK_EQ(result, MOJO_RESULT_OK); handle_.reset(InvitationHandle(invitation_handle)); } OutgoingInvitation::OutgoingInvitation(OutgoingInvitation&& other) = default; OutgoingInvitation::~OutgoingInvitation() = default; OutgoingInvitation& OutgoingInvitation::operator=(OutgoingInvitation&& other) = default; ScopedMessagePipeHandle OutgoingInvitation::AttachMessagePipe( base::StringPiece name) { DCHECK(!name.empty()); DCHECK(base::IsValueInRangeForNumericType(name.size())); MojoHandle message_pipe_handle; MojoResult result = MojoAttachMessagePipeToInvitation( handle_.get().value(), name.data(), static_cast(name.size()), nullptr, &message_pipe_handle); DCHECK_EQ(MOJO_RESULT_OK, result); return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle)); } ScopedMessagePipeHandle OutgoingInvitation::AttachMessagePipe(uint64_t name) { return AttachMessagePipe( base::StringPiece(reinterpret_cast(&name), sizeof(name))); } ScopedMessagePipeHandle OutgoingInvitation::ExtractMessagePipe( base::StringPiece name) { DCHECK(!name.empty()); DCHECK(base::IsValueInRangeForNumericType(name.size())); MojoHandle message_pipe_handle; MojoResult result = MojoExtractMessagePipeFromInvitation( handle_.get().value(), name.data(), static_cast(name.size()), nullptr, &message_pipe_handle); DCHECK_EQ(MOJO_RESULT_OK, result); return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle)); } ScopedMessagePipeHandle OutgoingInvitation::ExtractMessagePipe(uint64_t name) { return ExtractMessagePipe( base::StringPiece(reinterpret_cast(&name), sizeof(name))); } // static void OutgoingInvitation::Send(OutgoingInvitation invitation, base::ProcessHandle target_process, PlatformChannelEndpoint channel_endpoint, const ProcessErrorCallback& error_callback) { SendInvitation(std::move(invitation.handle_), target_process, channel_endpoint.TakePlatformHandle(), MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, invitation.extra_flags_, error_callback, ""); } // static void OutgoingInvitation::Send(OutgoingInvitation invitation, base::ProcessHandle target_process, PlatformChannelServerEndpoint server_endpoint, const ProcessErrorCallback& error_callback) { #if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) WaitForServerConnection( std::move(server_endpoint), base::BindOnce( [](OutgoingInvitation invitation, base::Process target_process, const ProcessErrorCallback& error_callback, PlatformChannelEndpoint endpoint) { SendInvitation(std::move(invitation.handle_), target_process.Handle(), endpoint.TakePlatformHandle(), MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, invitation.extra_flags_, error_callback, ""); }, std::move(invitation), CloneProcessFromHandle(target_process), error_callback)); #endif // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) } // static void OutgoingInvitation::SendAsync(OutgoingInvitation invitation, base::ProcessHandle target_process, PlatformChannelEndpoint channel_endpoint, const ProcessErrorCallback& error_callback) { SendInvitation(std::move(invitation.handle_), target_process, channel_endpoint.TakePlatformHandle(), MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC, invitation.extra_flags_, error_callback, ""); } // static ScopedMessagePipeHandle OutgoingInvitation::SendIsolated( PlatformChannelEndpoint channel_endpoint, base::StringPiece connection_name, base::ProcessHandle target_process) { OutgoingInvitation invitation; ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(kIsolatedPipeName); SendInvitation(std::move(invitation.handle_), target_process, channel_endpoint.TakePlatformHandle(), MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, MOJO_SEND_INVITATION_FLAG_ISOLATED | invitation.extra_flags_, ProcessErrorCallback(), connection_name); return pipe; } // static ScopedMessagePipeHandle OutgoingInvitation::SendIsolated( PlatformChannelServerEndpoint server_endpoint, base::StringPiece connection_name, base::ProcessHandle target_process) { OutgoingInvitation invitation; ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(kIsolatedPipeName); #if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) WaitForServerConnection( std::move(server_endpoint), base::BindOnce( [](OutgoingInvitation invitation, base::Process target_process, const std::string& connection_name, PlatformChannelEndpoint endpoint) { SendInvitation( std::move(invitation.handle_), target_process.Handle(), endpoint.TakePlatformHandle(), MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL, MOJO_SEND_INVITATION_FLAG_ISOLATED | invitation.extra_flags_, ProcessErrorCallback(), connection_name); }, std::move(invitation), CloneProcessFromHandle(target_process), std::string(connection_name))); #endif // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS) return pipe; } IncomingInvitation::IncomingInvitation() = default; IncomingInvitation::IncomingInvitation(IncomingInvitation&& other) = default; IncomingInvitation::IncomingInvitation(ScopedInvitationHandle handle) : handle_(std::move(handle)) {} IncomingInvitation::~IncomingInvitation() = default; IncomingInvitation& IncomingInvitation::operator=(IncomingInvitation&& other) = default; // static IncomingInvitation IncomingInvitation::Accept( PlatformChannelEndpoint channel_endpoint, MojoAcceptInvitationFlags flags) { MojoPlatformHandle endpoint_handle; PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), &endpoint_handle); CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); MojoInvitationTransportEndpoint transport_endpoint; transport_endpoint.struct_size = sizeof(transport_endpoint); transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL; transport_endpoint.num_platform_handles = 1; transport_endpoint.platform_handles = &endpoint_handle; MojoAcceptInvitationOptions options; options.struct_size = sizeof(options); options.flags = flags; MojoHandle invitation_handle; MojoResult result = MojoAcceptInvitation(&transport_endpoint, &options, &invitation_handle); if (result != MOJO_RESULT_OK) return IncomingInvitation(); return IncomingInvitation( ScopedInvitationHandle(InvitationHandle(invitation_handle))); } // static IncomingInvitation IncomingInvitation::AcceptAsync( PlatformChannelEndpoint channel_endpoint) { MojoPlatformHandle endpoint_handle; PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), &endpoint_handle); CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); MojoInvitationTransportEndpoint transport_endpoint; transport_endpoint.struct_size = sizeof(transport_endpoint); transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC; transport_endpoint.num_platform_handles = 1; transport_endpoint.platform_handles = &endpoint_handle; MojoHandle invitation_handle; MojoResult result = MojoAcceptInvitation(&transport_endpoint, nullptr, &invitation_handle); if (result != MOJO_RESULT_OK) return IncomingInvitation(); return IncomingInvitation( ScopedInvitationHandle(InvitationHandle(invitation_handle))); } // static ScopedMessagePipeHandle IncomingInvitation::AcceptIsolated( PlatformChannelEndpoint channel_endpoint) { MojoPlatformHandle endpoint_handle; PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(), &endpoint_handle); CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID); MojoInvitationTransportEndpoint transport_endpoint; transport_endpoint.struct_size = sizeof(transport_endpoint); transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL; transport_endpoint.num_platform_handles = 1; transport_endpoint.platform_handles = &endpoint_handle; MojoAcceptInvitationOptions options; options.struct_size = sizeof(options); options.flags = MOJO_ACCEPT_INVITATION_FLAG_ISOLATED; MojoHandle invitation_handle; MojoResult result = MojoAcceptInvitation(&transport_endpoint, &options, &invitation_handle); if (result != MOJO_RESULT_OK) return ScopedMessagePipeHandle(); IncomingInvitation invitation{ ScopedInvitationHandle(InvitationHandle(invitation_handle))}; return invitation.ExtractMessagePipe(kIsolatedPipeName); } ScopedMessagePipeHandle IncomingInvitation::ExtractMessagePipe( base::StringPiece name) { DCHECK(!name.empty()); DCHECK(base::IsValueInRangeForNumericType(name.size())); DCHECK(handle_.is_valid()); MojoHandle message_pipe_handle; MojoResult result = MojoExtractMessagePipeFromInvitation( handle_.get().value(), name.data(), static_cast(name.size()), nullptr, &message_pipe_handle); DCHECK_EQ(MOJO_RESULT_OK, result); return ScopedMessagePipeHandle(MessagePipeHandle(message_pipe_handle)); } ScopedMessagePipeHandle IncomingInvitation::ExtractMessagePipe(uint64_t name) { return ExtractMessagePipe( base::StringPiece(reinterpret_cast(&name), sizeof(name))); } } // namespace mojo