forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mach_port_rendezvous.h
220 lines (177 loc) · 8.6 KB
/
mach_port_rendezvous.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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/macros.h"
#include "base/synchronization/lock.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();
// 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);
// 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() { 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 that protects |client_data_|.
Lock lock_;
// Association of pid-to-ports.
std::map<pid_t, ClientData> client_data_;
DISALLOW_COPY_AND_ASSIGN(MachPortRendezvousServer);
};
// 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.
static MachPortRendezvousClient* GetInstance();
// 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);
// 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);
bool did_acquire_ports() { return did_acquire_ports_; }
// Lock for the below data members.
Lock lock_;
// Flag for if the client has attempted to acquire ports. If the client
// experienced an error in doing so, this will still be true.
bool did_acquire_ports_ = false;
// The collection of ports that was acquired.
MachPortsForRendezvous ports_;
DISALLOW_COPY_AND_ASSIGN(MachPortRendezvousClient);
};
} // namespace base
#endif // BASE_MAC_MACH_PORT_RENDEZVOUS_H_