220 lines
8.8 KiB
C++
220 lines
8.8 KiB
C++
// Copyright 2019 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef BASE_MAC_MACH_PORT_RENDEZVOUS_H_
|
|
#define BASE_MAC_MACH_PORT_RENDEZVOUS_H_
|
|
|
|
#include <dispatch/dispatch.h>
|
|
#include <mach/mach.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "base/base_export.h"
|
|
#include "base/mac/dispatch_source_mach.h"
|
|
#include "base/mac/scoped_dispatch_object.h"
|
|
#include "base/mac/scoped_mach_port.h"
|
|
#include "base/synchronization/lock.h"
|
|
#include "base/thread_annotations.h"
|
|
|
|
namespace base {
|
|
|
|
// Mach Port Rendezvous is a technique to exchange Mach port rights across
|
|
// child process creation. macOS does not provide a way to inherit Mach port
|
|
// rights, unlike what is possible with file descriptors. Port rendezvous
|
|
// enables a parent process to register Mach port rights for a nascent child,
|
|
// which the child can then retrieve using Mach IPC by looking up the endpoint
|
|
// in launchd's bootstrap namespace.
|
|
//
|
|
// When launching a child process, the parent process' rendezvous server lets
|
|
// calling code register a collection of ports for the new child. In order to
|
|
// acquire the ports, a child looks up the rendezvous server in the bootstrap
|
|
// namespace, and it sends an IPC message to the server, the reply to which
|
|
// contains the registered ports.
|
|
//
|
|
// Port rendezvous is only permitted between a parent and its direct child
|
|
// process descendants.
|
|
|
|
// A MachRendezvousPort contains a single Mach port to pass to the child
|
|
// process. The associated disposition controls how the reference count will
|
|
// be manipulated.
|
|
class BASE_EXPORT MachRendezvousPort {
|
|
public:
|
|
MachRendezvousPort() = default;
|
|
// Creates a rendezvous port that allows specifying the specific disposition.
|
|
MachRendezvousPort(mach_port_t name, mach_msg_type_name_t disposition);
|
|
// Creates a rendezvous port for MACH_MSG_TYPE_MOVE_SEND.
|
|
explicit MachRendezvousPort(mac::ScopedMachSendRight send_right);
|
|
// Creates a rendezvous port for MACH_MSG_TYPE_MOVE_RECEIVE.
|
|
explicit MachRendezvousPort(mac::ScopedMachReceiveRight receive_right);
|
|
|
|
// Note that the destructor does not call Destroy() explicitly.
|
|
// To avoid leaking ports, either use dispositions that create rights during
|
|
// transit (MAKE or COPY), or use base::LaunchProcess, which will destroy
|
|
// rights on failure.
|
|
~MachRendezvousPort();
|
|
|
|
// Destroys the Mach port right type conveyed |disposition| named by |name|.
|
|
void Destroy();
|
|
|
|
mach_port_t name() const { return name_; }
|
|
|
|
mach_msg_type_name_t disposition() const { return disposition_; }
|
|
|
|
private:
|
|
mach_port_t name_ = MACH_PORT_NULL;
|
|
mach_msg_type_name_t disposition_ = 0;
|
|
// Copy and assign allowed.
|
|
};
|
|
|
|
// The collection of ports to pass to a child process. There are no restrictions
|
|
// regarding the keys of the map. Clients are responsible for avoiding
|
|
// collisions with other clients.
|
|
using MachPortsForRendezvous = std::map<uint32_t, MachRendezvousPort>;
|
|
|
|
// Class that runs a Mach message server, from which client processes can
|
|
// acquire Mach port rights registered for them.
|
|
class BASE_EXPORT MachPortRendezvousServer {
|
|
public:
|
|
// Returns the instance of the server. Upon the first call to this method,
|
|
// the server is created, which registers an endpoint in the Mach bootstrap
|
|
// namespace.
|
|
static MachPortRendezvousServer* GetInstance();
|
|
|
|
MachPortRendezvousServer(const MachPortRendezvousServer&) = delete;
|
|
MachPortRendezvousServer& operator=(const MachPortRendezvousServer&) = delete;
|
|
|
|
// Registers a collection of Mach ports |ports| to be acquirable by the
|
|
// process known by |pid|. This cannot be called again for the same |pid|
|
|
// until the process known by |pid| has either acquired the ports or died.
|
|
//
|
|
// This must be called with the lock from GetLock() held.
|
|
void RegisterPortsForPid(pid_t pid, const MachPortsForRendezvous& ports)
|
|
EXCLUSIVE_LOCKS_REQUIRED(GetLock());
|
|
|
|
// Returns a lock on the internal port registration map. The parent process
|
|
// should hold this lock for the duration of launching a process, including
|
|
// after calling RegisterPortsForPid(). This ensures that a child process
|
|
// cannot race acquiring ports before they are registered. The lock should
|
|
// be released after the child process is launched and the ports are
|
|
// registered.
|
|
Lock& GetLock() LOCK_RETURNED(lock_) { return lock_; }
|
|
|
|
private:
|
|
friend class MachPortRendezvousServerTest;
|
|
friend struct MachPortRendezvousFuzzer;
|
|
|
|
struct ClientData {
|
|
ClientData(ScopedDispatchObject<dispatch_source_t> exit_watcher,
|
|
MachPortsForRendezvous ports);
|
|
ClientData(ClientData&&);
|
|
~ClientData();
|
|
|
|
// A DISPATCH_SOURCE_TYPE_PROC / DISPATCH_PROC_EXIT dispatch source. When
|
|
// the source is triggered, it calls OnClientExited().
|
|
ScopedDispatchObject<dispatch_source_t> exit_watcher;
|
|
|
|
MachPortsForRendezvous ports;
|
|
};
|
|
|
|
MachPortRendezvousServer();
|
|
~MachPortRendezvousServer();
|
|
|
|
// The server-side Mach message handler. Called by |dispatch_source_| when a
|
|
// message is received.
|
|
void HandleRequest();
|
|
|
|
// Returns the registered collection of ports for the specified |pid|. An
|
|
// empty collection indicates no ports were found, as it is invalid to
|
|
// register with an empty collection. This claims the collection of ports
|
|
// and removes the entry from |client_data_|.
|
|
MachPortsForRendezvous PortsForPid(pid_t pid);
|
|
|
|
// Returns a buffer containing a well-formed Mach message, destined for
|
|
// |reply_port| containing descriptors for the specified |ports|.
|
|
std::unique_ptr<uint8_t[]> CreateReplyMessage(
|
|
mach_port_t reply_port,
|
|
const MachPortsForRendezvous& ports);
|
|
|
|
// Called by the ClientData::exit_watcher dispatch sources when a process
|
|
// for which ports have been registered exits. This releases port rights
|
|
// that are strongly owned, in the event that the child has not claimed them.
|
|
void OnClientExited(pid_t pid);
|
|
|
|
// The Mach receive right for the server. A send right to this is port is
|
|
// registered in the bootstrap server.
|
|
mac::ScopedMachReceiveRight server_port_;
|
|
|
|
// Mach message dispatch source for |server_port_|.
|
|
std::unique_ptr<DispatchSourceMach> dispatch_source_;
|
|
|
|
Lock lock_;
|
|
// Association of pid-to-ports.
|
|
std::map<pid_t, ClientData> client_data_ GUARDED_BY(lock_);
|
|
};
|
|
|
|
// Client class for accessing the memory object exposed by the
|
|
// MachPortRendezvousServer.
|
|
class BASE_EXPORT MachPortRendezvousClient {
|
|
public:
|
|
// Connects to the MachPortRendezvousServer and requests any registered Mach
|
|
// ports. This only performs the rendezvous once. Subsequent calls to this
|
|
// method return the same instance. If the rendezvous fails, which can happen
|
|
// if the server is not available, this returns null. Acquiring zero ports
|
|
// from the exchange is not considered a failure.
|
|
static MachPortRendezvousClient* GetInstance();
|
|
|
|
MachPortRendezvousClient(const MachPortRendezvousClient&) = delete;
|
|
MachPortRendezvousClient& operator=(const MachPortRendezvousClient&) = delete;
|
|
|
|
// Returns the Mach send right that was registered with |key|. If no such
|
|
// right exists, or it was already taken, returns an invalid right. Safe to
|
|
// call from any thread. DCHECKs if the right referenced by |key| is not a
|
|
// send or send-once right.
|
|
mac::ScopedMachSendRight TakeSendRight(MachPortsForRendezvous::key_type key);
|
|
|
|
// Returns the Mach receive right that was registered with |key|. If no such
|
|
// right exists, or it was already taken, returns an invalid right. Safe to
|
|
// call from any thread. DCHECKs if the right referenced by |key| is not a
|
|
// receive right.
|
|
mac::ScopedMachReceiveRight TakeReceiveRight(
|
|
MachPortsForRendezvous::key_type key);
|
|
|
|
// Returns the number of ports in the client. After PerformRendezvous(), this
|
|
// reflects the number of ports acquired. But as rights are taken, this
|
|
// only reflects the number of remaining rights.
|
|
size_t GetPortCount();
|
|
|
|
// Returns the name of the server to look up in the bootstrap namespace.
|
|
static std::string GetBootstrapName();
|
|
|
|
private:
|
|
MachPortRendezvousClient();
|
|
~MachPortRendezvousClient();
|
|
|
|
// Helper method to look up the server in the bootstrap namespace and send
|
|
// the acquisition request message.
|
|
bool AcquirePorts();
|
|
|
|
// Sends the actual IPC message to |server_port| and parses the reply.
|
|
bool SendRequest(mac::ScopedMachSendRight server_port)
|
|
EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
|
|
|
// Returns a MachRendezvousPort for a given key and removes it from the
|
|
// |ports_| map. If an entry does not exist for that key, then a
|
|
// MachRendezvousPort with MACH_PORT_NULL is returned.
|
|
MachRendezvousPort PortForKey(MachPortsForRendezvous::key_type key);
|
|
|
|
Lock lock_;
|
|
// The collection of ports that was acquired.
|
|
MachPortsForRendezvous ports_ GUARDED_BY(lock_);
|
|
};
|
|
|
|
} // namespace base
|
|
|
|
#endif // BASE_MAC_MACH_PORT_RENDEZVOUS_H_
|