| // 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/platform/platform_channel.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/numerics/clamped_math.h" |
| #include "build/build_config.h" |
| #include "mojo/buildflags.h" |
| #include "mojo/core/embedder/features.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include <windows.h> |
| |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/win/scoped_handle.h" |
| #include "mojo/public/cpp/platform/named_platform_channel.h" |
| #elif BUILDFLAG(IS_FUCHSIA) |
| #include <lib/zx/channel.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| |
| #include "base/fuchsia/fuchsia_logging.h" |
| #elif BUILDFLAG(IS_POSIX) |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "base/files/scoped_file.h" |
| #include "base/posix/global_descriptors.h" |
| #endif |
| |
| #if BUILDFLAG(MOJO_USE_APPLE_CHANNEL) |
| #include <mach/port.h> |
| |
| #include "base/apple/mach_logging.h" |
| #include "base/apple/scoped_mach_port.h" |
| #endif |
| |
| #if BUILDFLAG(IS_POSIX) |
| #include <sys/socket.h> |
| #endif |
| |
| namespace mojo { |
| |
| namespace { |
| |
| #if BUILDFLAG(IS_WIN) |
| void CreateChannel(PlatformHandle* local_endpoint, |
| PlatformHandle* remote_endpoint) { |
| std::wstring pipe_name = NamedPlatformChannel::GetPipeNameFromServerName( |
| NamedPlatformChannel::GenerateRandomServerName(), /*is_local_pipe=*/true); |
| DWORD kOpenMode = |
| PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE; |
| const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE; |
| *local_endpoint = PlatformHandle(base::win::ScopedHandle( |
| ::CreateNamedPipeW(pipe_name.c_str(), kOpenMode, kPipeMode, |
| 1, // Max instances. |
| 4096, // Output buffer size. |
| 4096, // Input buffer size. |
| 5000, // Timeout in ms. |
| nullptr))); // Default security descriptor. |
| PCHECK(local_endpoint->is_valid()); |
| |
| const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE; |
| // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate |
| // the client. |
| DWORD kFlags = |
| SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED; |
| // Allow the handle to be inherited by child processes. |
| SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), |
| nullptr, TRUE}; |
| |
| // TODO(crbug.com/443055954): Sporadically, opening this named pipe fails with |
| // ERROR_PIPE_BUSY. This is unexpected as the pipe name is random. The |
| // leading hypothesis is that antivirus software is briefly interfering with |
| // the connection. To mitigate this as a transient issue, we wait for the |
| // pipe to become available and retry the connection once. If the retry |
| // succeeds, we dump the process state to help confirm the theory. |
| *remote_endpoint = PlatformHandle(base::win::ScopedHandle( |
| ::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, &security_attributes, |
| OPEN_EXISTING, kFlags, nullptr))); |
| if (!remote_endpoint->is_valid() && ::GetLastError() == ERROR_PIPE_BUSY) { |
| if (::WaitNamedPipe(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT)) { |
| *remote_endpoint = PlatformHandle(base::win::ScopedHandle( |
| ::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, |
| &security_attributes, OPEN_EXISTING, kFlags, nullptr))); |
| if (remote_endpoint->is_valid()) { |
| base::debug::DumpWithoutCrashing(); |
| } |
| } |
| } |
| |
| PCHECK(remote_endpoint->is_valid()); |
| |
| // Since a client has connected, ConnectNamedPipe() should return zero and |
| // GetLastError() should return ERROR_PIPE_CONNECTED. |
| CHECK(!::ConnectNamedPipe(local_endpoint->GetHandle().Get(), nullptr)); |
| PCHECK(::GetLastError() == ERROR_PIPE_CONNECTED); |
| } |
| #elif BUILDFLAG(IS_FUCHSIA) |
| void CreateChannel(PlatformHandle* local_endpoint, |
| PlatformHandle* remote_endpoint) { |
| zx::channel handles[2]; |
| zx_status_t result = zx::channel::create(0, &handles[0], &handles[1]); |
| ZX_CHECK(result == ZX_OK, result); |
| |
| *local_endpoint = PlatformHandle(std::move(handles[0])); |
| *remote_endpoint = PlatformHandle(std::move(handles[1])); |
| DCHECK(local_endpoint->is_valid()); |
| DCHECK(remote_endpoint->is_valid()); |
| } |
| #elif BUILDFLAG(MOJO_USE_APPLE_CHANNEL) |
| void CreateChannel(PlatformHandle* local_endpoint, |
| PlatformHandle* remote_endpoint) { |
| // Mach messaging is simplex; and in order to enable full-duplex |
| // communication, the Mojo channel implementation performs an internal |
| // handshake with its peer to establish two sets of Mach receive and send |
| // rights. The handshake process starts with the creation of one |
| // PlatformChannel endpoint. |
| base::apple::ScopedMachReceiveRight receive; |
| base::apple::ScopedMachSendRight send; |
| // The mpl_qlimit specified here should stay in sync with |
| // NamedPlatformChannel. |
| CHECK(base::apple::CreateMachPort(&receive, &send, MACH_PORT_QLIMIT_LARGE)); |
| |
| // In a reverse of Mach messaging semantics, in Mojo the "local" endpoint is |
| // the send right, while the "remote" end is the receive right. |
| *local_endpoint = PlatformHandle(std::move(send)); |
| *remote_endpoint = PlatformHandle(std::move(receive)); |
| } |
| #elif BUILDFLAG(IS_POSIX) |
| void CreateChannel(PlatformHandle* local_endpoint, |
| PlatformHandle* remote_endpoint) { |
| int fds[2]; |
| PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0); |
| |
| // Set non-blocking on both ends. |
| PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0); |
| PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0); |
| |
| *local_endpoint = PlatformHandle(base::ScopedFD(fds[0])); |
| *remote_endpoint = PlatformHandle(base::ScopedFD(fds[1])); |
| DCHECK(local_endpoint->is_valid()); |
| DCHECK(remote_endpoint->is_valid()); |
| } |
| #else |
| #error "Unsupported platform." |
| #endif |
| |
| } // namespace |
| |
| const char PlatformChannel::kHandleSwitch[] = "mojo-platform-channel-handle"; |
| |
| PlatformChannel::PlatformChannel() { |
| PlatformHandle local_handle; |
| PlatformHandle remote_handle; |
| CreateChannel(&local_handle, &remote_handle); |
| local_endpoint_ = PlatformChannelEndpoint(std::move(local_handle)); |
| remote_endpoint_ = PlatformChannelEndpoint(std::move(remote_handle)); |
| } |
| |
| PlatformChannel::PlatformChannel(PlatformChannelEndpoint local, |
| PlatformChannelEndpoint remote) |
| : local_endpoint_(std::move(local)), remote_endpoint_(std::move(remote)) {} |
| |
| PlatformChannel::PlatformChannel(PlatformChannel&& other) = default; |
| |
| PlatformChannel& PlatformChannel::operator=(PlatformChannel&& other) = default; |
| |
| PlatformChannel::~PlatformChannel() = default; |
| |
| void PlatformChannel::PrepareToPassRemoteEndpoint(HandlePassingInfo* info, |
| std::string* value) { |
| remote_endpoint_.PrepareToPass(*info, *value); |
| } |
| |
| std::string PlatformChannel::PrepareToPassRemoteEndpoint( |
| base::LaunchOptions& options) { |
| return remote_endpoint_.PrepareToPass(options); |
| } |
| |
| void PlatformChannel::PrepareToPassRemoteEndpoint( |
| HandlePassingInfo* info, |
| base::CommandLine* command_line) { |
| remote_endpoint_.PrepareToPass(*info, *command_line); |
| } |
| |
| void PlatformChannel::PrepareToPassRemoteEndpoint( |
| base::LaunchOptions* options, |
| base::CommandLine* command_line) { |
| remote_endpoint_.PrepareToPass(*options, *command_line); |
| } |
| |
| void PlatformChannel::RemoteProcessLaunchAttempted() { |
| remote_endpoint_.ProcessLaunchAttempted(); |
| } |
| |
| // static |
| PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromString( |
| std::string_view value) { |
| return PlatformChannelEndpoint::RecoverFromString(value); |
| } |
| |
| // static |
| PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromCommandLine( |
| const base::CommandLine& command_line) { |
| return RecoverPassedEndpointFromString( |
| command_line.GetSwitchValueASCII(kHandleSwitch)); |
| } |
| |
| // static |
| bool PlatformChannel::CommandLineHasPassedEndpoint( |
| const base::CommandLine& command_line) { |
| return command_line.HasSwitch(kHandleSwitch); |
| } |
| |
| } // namespace mojo |