• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_MAC_MACH_PORT_RENDEZVOUS_H_
6 #define BASE_MAC_MACH_PORT_RENDEZVOUS_H_
7 
8 #include <dispatch/dispatch.h>
9 #include <mach/mach.h>
10 #include <stdint.h>
11 #include <sys/types.h>
12 
13 #include <map>
14 #include <memory>
15 #include <string>
16 
17 #include "base/base_export.h"
18 #include "base/mac/dispatch_source_mach.h"
19 #include "base/mac/scoped_dispatch_object.h"
20 #include "base/mac/scoped_mach_port.h"
21 #include "base/synchronization/lock.h"
22 #include "base/thread_annotations.h"
23 
24 namespace base {
25 
26 // Mach Port Rendezvous is a technique to exchange Mach port rights across
27 // child process creation. macOS does not provide a way to inherit Mach port
28 // rights, unlike what is possible with file descriptors. Port rendezvous
29 // enables a parent process to register Mach port rights for a nascent child,
30 // which the child can then retrieve using Mach IPC by looking up the endpoint
31 // in launchd's bootstrap namespace.
32 //
33 // When launching a child process, the parent process' rendezvous server lets
34 // calling code register a collection of ports for the new child. In order to
35 // acquire the ports, a child looks up the rendezvous server in the bootstrap
36 // namespace, and it sends an IPC message to the server, the reply to which
37 // contains the registered ports.
38 //
39 // Port rendezvous is only permitted between a parent and its direct child
40 // process descendants.
41 
42 // A MachRendezvousPort contains a single Mach port to pass to the child
43 // process. The associated disposition controls how the reference count will
44 // be manipulated.
45 class BASE_EXPORT MachRendezvousPort {
46  public:
47   MachRendezvousPort() = default;
48   // Creates a rendezvous port that allows specifying the specific disposition.
49   MachRendezvousPort(mach_port_t name, mach_msg_type_name_t disposition);
50   // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_SEND.
51   explicit MachRendezvousPort(mac::ScopedMachSendRight send_right);
52   // Creates a rendezvous port for MACH_MSG_TYPE_MOVE_RECEIVE.
53   explicit MachRendezvousPort(mac::ScopedMachReceiveRight receive_right);
54 
55   // Note that the destructor does not call Destroy() explicitly.
56   // To avoid leaking ports, either use dispositions that create rights during
57   // transit (MAKE or COPY), or use base::LaunchProcess, which will destroy
58   // rights on failure.
59   ~MachRendezvousPort();
60 
61   // Destroys the Mach port right type conveyed |disposition| named by |name|.
62   void Destroy();
63 
name()64   mach_port_t name() const { return name_; }
65 
disposition()66   mach_msg_type_name_t disposition() const { return disposition_; }
67 
68  private:
69   mach_port_t name_ = MACH_PORT_NULL;
70   mach_msg_type_name_t disposition_ = 0;
71   // Copy and assign allowed.
72 };
73 
74 // The collection of ports to pass to a child process. There are no restrictions
75 // regarding the keys of the map. Clients are responsible for avoiding
76 // collisions with other clients.
77 using MachPortsForRendezvous = std::map<uint32_t, MachRendezvousPort>;
78 
79 // Class that runs a Mach message server, from which client processes can
80 // acquire Mach port rights registered for them.
81 class BASE_EXPORT MachPortRendezvousServer {
82  public:
83   // Returns the instance of the server. Upon the first call to this method,
84   // the server is created, which registers an endpoint in the Mach bootstrap
85   // namespace.
86   static MachPortRendezvousServer* GetInstance();
87 
88   MachPortRendezvousServer(const MachPortRendezvousServer&) = delete;
89   MachPortRendezvousServer& operator=(const MachPortRendezvousServer&) = delete;
90 
91   // Registers a collection of Mach ports |ports| to be acquirable by the
92   // process known by |pid|. This cannot be called again for the same |pid|
93   // until the process known by |pid| has either acquired the ports or died.
94   //
95   // This must be called with the lock from GetLock() held.
96   void RegisterPortsForPid(pid_t pid, const MachPortsForRendezvous& ports)
97       EXCLUSIVE_LOCKS_REQUIRED(GetLock());
98 
99   // Returns a lock on the internal port registration map. The parent process
100   // should hold this lock for the duration of launching a process, including
101   // after calling RegisterPortsForPid(). This ensures that a child process
102   // cannot race acquiring ports before they are registered. The lock should
103   // be released after the child process is launched and the ports are
104   // registered.
GetLock()105   Lock& GetLock() LOCK_RETURNED(lock_) { return lock_; }
106 
107  private:
108   friend class MachPortRendezvousServerTest;
109   friend struct MachPortRendezvousFuzzer;
110 
111   struct ClientData {
112     ClientData(ScopedDispatchObject<dispatch_source_t> exit_watcher,
113                MachPortsForRendezvous ports);
114     ClientData(ClientData&&);
115     ~ClientData();
116 
117     // A DISPATCH_SOURCE_TYPE_PROC / DISPATCH_PROC_EXIT dispatch source. When
118     // the source is triggered, it calls OnClientExited().
119     ScopedDispatchObject<dispatch_source_t> exit_watcher;
120 
121     MachPortsForRendezvous ports;
122   };
123 
124   MachPortRendezvousServer();
125   ~MachPortRendezvousServer();
126 
127   // The server-side Mach message handler. Called by |dispatch_source_| when a
128   // message is received.
129   void HandleRequest();
130 
131   // Returns the registered collection of ports for the specified |pid|. An
132   // empty collection indicates no ports were found, as it is invalid to
133   // register with an empty collection. This claims the collection of ports
134   // and removes the entry from |client_data_|.
135   MachPortsForRendezvous PortsForPid(pid_t pid);
136 
137   // Returns a buffer containing a well-formed Mach message, destined for
138   // |reply_port| containing descriptors for the specified |ports|.
139   std::unique_ptr<uint8_t[]> CreateReplyMessage(
140       mach_port_t reply_port,
141       const MachPortsForRendezvous& ports);
142 
143   // Called by the ClientData::exit_watcher dispatch sources when a process
144   // for which ports have been registered exits. This releases port rights
145   // that are strongly owned, in the event that the child has not claimed them.
146   void OnClientExited(pid_t pid);
147 
148   // The Mach receive right for the server. A send right to this is port is
149   // registered in the bootstrap server.
150   mac::ScopedMachReceiveRight server_port_;
151 
152   // Mach message dispatch source for |server_port_|.
153   std::unique_ptr<DispatchSourceMach> dispatch_source_;
154 
155   Lock lock_;
156   // Association of pid-to-ports.
157   std::map<pid_t, ClientData> client_data_ GUARDED_BY(lock_);
158 };
159 
160 // Client class for accessing the memory object exposed by the
161 // MachPortRendezvousServer.
162 class BASE_EXPORT MachPortRendezvousClient {
163  public:
164   // Connects to the MachPortRendezvousServer and requests any registered Mach
165   // ports. This only performs the rendezvous once. Subsequent calls to this
166   // method return the same instance. If the rendezvous fails, which can happen
167   // if the server is not available, this returns null. Acquiring zero ports
168   // from the exchange is not considered a failure.
169   static MachPortRendezvousClient* GetInstance();
170 
171   MachPortRendezvousClient(const MachPortRendezvousClient&) = delete;
172   MachPortRendezvousClient& operator=(const MachPortRendezvousClient&) = delete;
173 
174   // Returns the Mach send right that was registered with |key|. If no such
175   // right exists, or it was already taken, returns an invalid right. Safe to
176   // call from any thread. DCHECKs if the right referenced by |key| is not a
177   // send or send-once right.
178   mac::ScopedMachSendRight TakeSendRight(MachPortsForRendezvous::key_type key);
179 
180   // Returns the Mach receive right that was registered with |key|. If no such
181   // right exists, or it was already taken, returns an invalid right. Safe to
182   // call from any thread. DCHECKs if the right referenced by |key| is not a
183   // receive right.
184   mac::ScopedMachReceiveRight TakeReceiveRight(
185       MachPortsForRendezvous::key_type key);
186 
187   // Returns the number of ports in the client. After PerformRendezvous(), this
188   // reflects the number of ports acquired. But as rights are taken, this
189   // only reflects the number of remaining rights.
190   size_t GetPortCount();
191 
192   // Returns the name of the server to look up in the bootstrap namespace.
193   static std::string GetBootstrapName();
194 
195  private:
196   MachPortRendezvousClient();
197   ~MachPortRendezvousClient();
198 
199   // Helper method to look up the server in the bootstrap namespace and send
200   // the acquisition request message.
201   bool AcquirePorts();
202 
203   // Sends the actual IPC message to |server_port| and parses the reply.
204   bool SendRequest(mac::ScopedMachSendRight server_port)
205       EXCLUSIVE_LOCKS_REQUIRED(lock_);
206 
207   // Returns a MachRendezvousPort for a given key and removes it from the
208   // |ports_| map. If an entry does not exist for that key, then a
209   // MachRendezvousPort with MACH_PORT_NULL is returned.
210   MachRendezvousPort PortForKey(MachPortsForRendezvous::key_type key);
211 
212   Lock lock_;
213   // The collection of ports that was acquired.
214   MachPortsForRendezvous ports_ GUARDED_BY(lock_);
215 };
216 
217 }  // namespace base
218 
219 #endif  // BASE_MAC_MACH_PORT_RENDEZVOUS_H_
220