Skip to content

Commit

Permalink
Allow NamedMojoIpcServer to run without an isolated connection
Browse files Browse the repository at this point in the history
IsolatedConnection is deprecated and will change its behavior after
MojoIpcz adoption. This CL updates NamedMojoIpcServer so that it can run
with or without an isolated connection. If the caller provides a message
pipe ID, the non-isolated standard invitation flow will be used;
otherwise an isolated connection will be used. Existing callers will
still use IsolatedConnection after this CL is checked in, but they
should eventually be switched to use non-isolated connections.

This CL also parameterizes NamedMojoIpcServer tests so they test both
isolated and non-isolated connections. It also updates the example usage
to reflect the non-isolated usage.

Bug: 1378803
Change-Id: I55f65fc969e66a93803809c2d258dba395200265
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4054401
Reviewed-by: Joshua Pawlicki <waffles@chromium.org>
Reviewed-by: Noah Rose Ledesma <noahrose@google.com>
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1076580}
  • Loading branch information
ywh233 authored and Chromium LUCI CQ committed Nov 29, 2022
1 parent 6029d52 commit 96a68d1
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 135 deletions.
10 changes: 7 additions & 3 deletions chrome/updater/app/server/linux/update_service_stub.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "chrome/updater/linux/ipc_constants.h"
#include "chrome/updater/registration_data.h"
#include "chrome/updater/updater_version.h"
#include "components/named_mojo_ipc_server/named_mojo_ipc_server.h"
#include "mojo/public/cpp/bindings/remote.h"

namespace updater {
Expand Down Expand Up @@ -103,9 +104,11 @@ static bool IsTrustedIPCEndpoint(base::ProcessId /*caller_pid*/) {

UpdateServiceStub::UpdateServiceStub(scoped_refptr<updater::UpdateService> impl,
UpdaterScope scope)
: server_(GetActiveDutySocketPath(scope)->MaybeAsASCII(),
this,
base::BindRepeating(&IsTrustedIPCEndpoint)),
: server_(
GetActiveDutySocketPath(scope)->MaybeAsASCII(),
named_mojo_ipc_server::NamedMojoIpcServerBase::kUseIsolatedConnection,
this,
base::BindRepeating(&IsTrustedIPCEndpoint)),
impl_(impl) {
server_.set_disconnect_handler(base::BindRepeating(
&UpdateServiceStub::OnClientDisconnected, base::Unretained(this)));
Expand Down Expand Up @@ -227,6 +230,7 @@ UpdateServiceInternalStub::UpdateServiceInternalStub(
: server_(
GetActiveDutyInternalSocketPath(scope, base::Version(kUpdaterVersion))
->MaybeAsASCII(),
named_mojo_ipc_server::NamedMojoIpcServerBase::kUseIsolatedConnection,
this,
base::BindRepeating(&IsTrustedIPCEndpoint)),
impl_(impl) {
Expand Down
54 changes: 27 additions & 27 deletions components/named_mojo_ipc_server/README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
# Named Mojo IPC Server

This component provides a helper that uses a mojo::NamedPlatformChannel to
manage multiple concurrent IPCs. Clients that connect to the
NamedPlatformChannel are sent invitations to join an isolated IPC graph
suitable only for direct IPC between the two processes.

## Caveats

The isolated connections managed by NamedMojoIpcServer can only be used to
connect two nodes which have been initialized as Mojo brokers. That is,
the `is_broker_process` field in the configuration passed to `mojo::core::Init`
must be set to true. This assumes that these isolated connections effectively
only go between two standalone (i.e. non-Chrome) processes, or between some
standalone process and the browser process; but never between two existing
Chrome processes, or between a standalone process and one of Chrome's child
processes.

A restriction of isolated connections is that Mojo remotes, receivers, or
message pipes cannot be passed outside of the boundary of the process pairs.
For example, the server cannot be used broker connections between two clients.
This component provides a helper that allows a server process to handle multiple
concurrent IPCs coming through a `NamedPlatformChannel`.

## Example usage

In the server process:
```cpp
static const uint64_t kMessagePipeId = 0u;

class MyInterfaceImpl: public mojom::MyInterface {
void Start() {
server_.set_disconnect_handler(
Expand All @@ -43,21 +26,38 @@ class MyInterfaceImpl: public mojom::MyInterface {
server_.Close(server_.current_receiver());
}

MojoIpcServer<mojom::MyInterface> server_{"my_server_name", this};
static bool IsTrustedMojoEndpoint(base::ProcessId caller_pid) {
// Verify the calling process...
return true;
}

NamedMojoIpcServer<mojom::MyInterface> server_{"my_server_name",
kMessagePipeId, this,
base::BindRepeating(&MyInterfaceImpl::IsTrustedMojoEndpoint)};
};
```
Note: In unittests `base::test:TaskEnvironment` should run until idle after
`NamedMojoIpcServer` is shutdown. Otherwise, memory may leak. E.g:
```cpp
void MyTestFixture::TearDown() {
ipc_server_->StopServer();
task_environment_.RunUntilIdle();
}
```

In the client:
```cpp
void ConnectToServer() {
mojo::PlatformChannelEndpoint endpoint =
mojo::NamedPlatformChannel::ConnectToServer("my_server_name");
std::unique_ptr<mojo::IsolatedConnection> connection =
std::make_unique<mojo::IsolatedConnection>();
auto invitation = mojo::IncomingInvitation::Accept(std::move(endpoint));
mojo::Remote<mojom::MyInterface> remote(
mojo::PendingRemote<mojom::MyInterface>(
connection->Connect(std::move(endpoint)), 0));
invitation.ExtractMessagePipe(kMessagePipeId), 0));
}
```

On Windows, the server needs to have the following access rights on the client
process: `PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION`.
85 changes: 67 additions & 18 deletions components/named_mojo_ipc_server/named_mojo_ipc_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "components/named_mojo_ipc_server/named_mojo_ipc_server.h"

#include <stdint.h>

#include <memory>

#include "base/bind.h"
Expand All @@ -12,13 +14,22 @@
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/notreached.h"
#include "base/process/process.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/named_mojo_ipc_server/named_mojo_server_endpoint_connector.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/isolated_connection.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include "base/strings/stringprintf.h"
#include "base/win/win_util.h"
#endif // BUILDFLAG(IS_WIN)
Expand Down Expand Up @@ -64,12 +75,10 @@ NamedMojoIpcServerBase::DelegateProxy::DelegateProxy(
NamedMojoIpcServerBase::DelegateProxy::~DelegateProxy() = default;

void NamedMojoIpcServerBase::DelegateProxy::OnServerEndpointConnected(
std::unique_ptr<mojo::IsolatedConnection> connection,
mojo::ScopedMessagePipeHandle message_pipe,
mojo::PlatformChannelEndpoint endpoint,
base::ProcessId peer_pid) {
if (server_)
server_->OnServerEndpointConnected(std::move(connection),
std::move(message_pipe), peer_pid);
server_->OnServerEndpointConnected(std::move(endpoint), peer_pid);
}

void NamedMojoIpcServerBase::DelegateProxy::OnServerEndpointConnectionFailed() {
Expand All @@ -79,8 +88,10 @@ void NamedMojoIpcServerBase::DelegateProxy::OnServerEndpointConnectionFailed() {

NamedMojoIpcServerBase::NamedMojoIpcServerBase(
const mojo::NamedPlatformChannel::ServerName& server_name,
absl::optional<uint64_t> message_pipe_id,
IsTrustedMojoEndpointCallback is_trusted_endpoint_callback)
: server_name_(server_name),
message_pipe_id_(message_pipe_id),
is_trusted_endpoint_callback_(std::move(is_trusted_endpoint_callback)) {
io_sequence_ =
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
Expand All @@ -103,7 +114,7 @@ void NamedMojoIpcServerBase::StartServer() {
weak_factory_.GetWeakPtr()),
io_sequence_);
server_started_ = true;
SendInvitation();
CreateServerEndpoint();
}

void NamedMojoIpcServerBase::StopServer() {
Expand All @@ -126,7 +137,7 @@ void NamedMojoIpcServerBase::Close(mojo::ReceiverId id) {
}
}

void NamedMojoIpcServerBase::SendInvitation() {
void NamedMojoIpcServerBase::CreateServerEndpoint() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

io_sequence_->PostTaskAndReplyWithResult(
Expand Down Expand Up @@ -163,24 +174,62 @@ void NamedMojoIpcServerBase::OnServerEndpointCreated(
}

void NamedMojoIpcServerBase::OnServerEndpointConnected(
std::unique_ptr<mojo::IsolatedConnection> connection,
mojo::ScopedMessagePipeHandle message_pipe,
mojo::PlatformChannelEndpoint endpoint,
base::ProcessId peer_pid) {
if (is_trusted_endpoint_callback_.Run(peer_pid)) {
auto receiver_id = TrackMessagePipe(std::move(message_pipe), peer_pid);
active_connections_[receiver_id] = std::move(connection);
} else {
LOG(ERROR) << "Process " << peer_pid
<< " is not a trusted mojo endpoint. Connection refused.";
}

SendInvitation();
PassAndTrackMessagePipe(std::move(endpoint), peer_pid);
CreateServerEndpoint();
}

void NamedMojoIpcServerBase::OnServerEndpointConnectionFailed() {
resend_invitation_on_error_timer_.Start(
FROM_HERE, kResentInvitationOnErrorDelay, this,
&NamedMojoIpcServerBase::SendInvitation);
&NamedMojoIpcServerBase::CreateServerEndpoint);
}

void NamedMojoIpcServerBase::PassAndTrackMessagePipe(
mojo::PlatformChannelEndpoint endpoint,
base::ProcessId peer_pid) {
if (!is_trusted_endpoint_callback_.Run(peer_pid)) {
LOG(ERROR) << "Process " << peer_pid
<< " is not a trusted mojo endpoint. Connection refused.";
return;
}

if (!message_pipe_id_.has_value()) {
// Create isolated connection.
auto connection = std::make_unique<mojo::IsolatedConnection>();
mojo::ScopedMessagePipeHandle message_pipe =
connection->Connect(std::move(endpoint));
mojo::ReceiverId receiver_id =
TrackMessagePipe(std::move(message_pipe), peer_pid);
active_connections_[receiver_id] = std::move(connection);
return;
}

// Create non-isolated connection.
mojo::OutgoingInvitation invitation;
mojo::ScopedMessagePipeHandle message_pipe =
invitation.AttachMessagePipe(*message_pipe_id_);
#if BUILDFLAG(IS_WIN)
// Open process with minimum permissions since the client process might have
// restricted its access with DACL.
base::Process peer_process =
base::Process::OpenWithAccess(peer_pid, PROCESS_DUP_HANDLE);
// Windows opens the process with a system call so we use PLOG to extract more
// info. Other OSes (i.e. POSIX) don't do that.
#define INVALID_PROCESS_LOG PLOG
#else
base::Process peer_process = base::Process::Open(peer_pid);
#define INVALID_PROCESS_LOG LOG
#endif
if (!peer_process.IsValid()) {
INVALID_PROCESS_LOG(ERROR) << "Failed to open peer process";
return;
}
#undef INVALID_PROCESS_LOG
mojo::OutgoingInvitation::Send(std::move(invitation), peer_process.Handle(),
std::move(endpoint));
TrackMessagePipe(std::move(message_pipe), peer_pid);
}

} // namespace named_mojo_ipc_server

0 comments on commit 96a68d1

Please sign in to comment.