mach_port_rendezvous.h 8.6 KB

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