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