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