mach_port_rendezvous.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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. #include "base/mac/mach_port_rendezvous.h"
  5. #include <bsm/libbsm.h>
  6. #include <mach/mig.h>
  7. #include <servers/bootstrap.h>
  8. #include <unistd.h>
  9. #include <utility>
  10. #include "base/containers/buffer_iterator.h"
  11. #include "base/logging.h"
  12. #include "base/mac/foundation_util.h"
  13. #include "base/mac/mach_logging.h"
  14. #include "base/mac/scoped_mach_msg_destroy.h"
  15. #include "base/notreached.h"
  16. #include "base/strings/stringprintf.h"
  17. namespace base {
  18. namespace {
  19. // The name to use in the bootstrap server, formatted with the BaseBundleID and
  20. // PID of the server.
  21. constexpr char kBootstrapNameFormat[] = "%s.MachPortRendezvousServer.%d";
  22. // This limit is arbitrary and can be safely increased in the future.
  23. constexpr size_t kMaximumRendezvousPorts = 5;
  24. enum MachRendezvousMsgId : mach_msg_id_t {
  25. kMachRendezvousMsgIdRequest = 'mrzv',
  26. kMachRendezvousMsgIdResponse = 'MRZV',
  27. };
  28. size_t CalculateResponseSize(size_t num_ports) {
  29. return sizeof(mach_msg_base_t) +
  30. (num_ports * sizeof(mach_msg_port_descriptor_t)) +
  31. (num_ports * sizeof(MachPortsForRendezvous::key_type));
  32. }
  33. } // namespace
  34. MachRendezvousPort::MachRendezvousPort(mach_port_t name,
  35. mach_msg_type_name_t disposition)
  36. : name_(name), disposition_(disposition) {
  37. DCHECK(disposition == MACH_MSG_TYPE_MOVE_RECEIVE ||
  38. disposition == MACH_MSG_TYPE_MOVE_SEND ||
  39. disposition == MACH_MSG_TYPE_MOVE_SEND_ONCE ||
  40. disposition == MACH_MSG_TYPE_COPY_SEND ||
  41. disposition == MACH_MSG_TYPE_MAKE_SEND ||
  42. disposition == MACH_MSG_TYPE_MAKE_SEND_ONCE);
  43. }
  44. MachRendezvousPort::MachRendezvousPort(mac::ScopedMachSendRight send_right)
  45. : name_(send_right.release()), disposition_(MACH_MSG_TYPE_MOVE_SEND) {}
  46. MachRendezvousPort::MachRendezvousPort(
  47. mac::ScopedMachReceiveRight receive_right)
  48. : name_(receive_right.release()),
  49. disposition_(MACH_MSG_TYPE_MOVE_RECEIVE) {}
  50. MachRendezvousPort::~MachRendezvousPort() = default;
  51. void MachRendezvousPort::Destroy() {
  52. // Map the disposition to the type of right to deallocate.
  53. mach_port_right_t right = 0;
  54. switch (disposition_) {
  55. case 0:
  56. DCHECK(name_ == MACH_PORT_NULL);
  57. return;
  58. case MACH_MSG_TYPE_COPY_SEND:
  59. case MACH_MSG_TYPE_MAKE_SEND:
  60. case MACH_MSG_TYPE_MAKE_SEND_ONCE:
  61. // Right is not owned, would be created by transit.
  62. return;
  63. case MACH_MSG_TYPE_MOVE_RECEIVE:
  64. right = MACH_PORT_RIGHT_RECEIVE;
  65. break;
  66. case MACH_MSG_TYPE_MOVE_SEND:
  67. right = MACH_PORT_RIGHT_SEND;
  68. break;
  69. case MACH_MSG_TYPE_MOVE_SEND_ONCE:
  70. right = MACH_PORT_RIGHT_SEND_ONCE;
  71. break;
  72. default:
  73. NOTREACHED() << "Leaking port name " << name_ << " with disposition "
  74. << disposition_;
  75. return;
  76. }
  77. kern_return_t kr = mach_port_mod_refs(mach_task_self(), name_, right, -1);
  78. MACH_DCHECK(kr == KERN_SUCCESS, kr)
  79. << "Failed to drop ref on port name " << name_;
  80. name_ = MACH_PORT_NULL;
  81. disposition_ = 0;
  82. }
  83. // static
  84. MachPortRendezvousServer* MachPortRendezvousServer::GetInstance() {
  85. static auto* instance = new MachPortRendezvousServer();
  86. return instance;
  87. }
  88. void MachPortRendezvousServer::RegisterPortsForPid(
  89. pid_t pid,
  90. const MachPortsForRendezvous& ports) {
  91. lock_.AssertAcquired();
  92. DCHECK_LT(ports.size(), kMaximumRendezvousPorts);
  93. DCHECK(!ports.empty());
  94. ScopedDispatchObject<dispatch_source_t> exit_watcher(dispatch_source_create(
  95. DISPATCH_SOURCE_TYPE_PROC, static_cast<uintptr_t>(pid),
  96. DISPATCH_PROC_EXIT, dispatch_source_->queue()));
  97. dispatch_source_set_event_handler(exit_watcher, ^{
  98. OnClientExited(pid);
  99. });
  100. dispatch_resume(exit_watcher);
  101. auto it =
  102. client_data_.emplace(pid, ClientData{std::move(exit_watcher), ports});
  103. DCHECK(it.second);
  104. }
  105. MachPortRendezvousServer::ClientData::ClientData(
  106. ScopedDispatchObject<dispatch_source_t> exit_watcher,
  107. MachPortsForRendezvous ports)
  108. : exit_watcher(exit_watcher), ports(ports) {}
  109. MachPortRendezvousServer::ClientData::ClientData(ClientData&&) = default;
  110. MachPortRendezvousServer::ClientData::~ClientData() = default;
  111. MachPortRendezvousServer::MachPortRendezvousServer() {
  112. std::string bootstrap_name =
  113. StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getpid());
  114. kern_return_t kr = bootstrap_check_in(
  115. bootstrap_port, bootstrap_name.c_str(),
  116. mac::ScopedMachReceiveRight::Receiver(server_port_).get());
  117. BOOTSTRAP_CHECK(kr == KERN_SUCCESS, kr)
  118. << "bootstrap_check_in " << bootstrap_name;
  119. dispatch_source_ = std::make_unique<DispatchSourceMach>(
  120. bootstrap_name.c_str(), server_port_.get(), ^{
  121. HandleRequest();
  122. });
  123. dispatch_source_->Resume();
  124. }
  125. MachPortRendezvousServer::~MachPortRendezvousServer() {}
  126. void MachPortRendezvousServer::HandleRequest() {
  127. // Receive the request message, using the kernel audit token to ascertain the
  128. // PID of the sender.
  129. struct : mach_msg_header_t {
  130. mach_msg_audit_trailer_t trailer;
  131. } request{};
  132. request.msgh_size = sizeof(request);
  133. request.msgh_local_port = server_port_.get();
  134. const mach_msg_option_t options =
  135. MACH_RCV_MSG | MACH_RCV_TIMEOUT |
  136. MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
  137. MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
  138. mach_msg_return_t mr = mach_msg(&request, options, 0, sizeof(request),
  139. server_port_.get(), 0, MACH_PORT_NULL);
  140. if (mr != KERN_SUCCESS) {
  141. MACH_LOG(ERROR, mr) << "mach_msg receive";
  142. return;
  143. }
  144. // Destroy the message in case of an early return, which will release
  145. // any rights from a bad message. In the case of a disallowed sender,
  146. // the destruction of the reply port will break them out of a mach_msg.
  147. ScopedMachMsgDestroy scoped_message(&request);
  148. if (request.msgh_id != kMachRendezvousMsgIdRequest ||
  149. request.msgh_size != sizeof(mach_msg_header_t)) {
  150. // Do not reply to messages that are unexpected.
  151. return;
  152. }
  153. pid_t sender_pid = audit_token_to_pid(request.trailer.msgh_audit);
  154. MachPortsForRendezvous ports_to_send = PortsForPid(sender_pid);
  155. if (ports_to_send.empty()) {
  156. return;
  157. }
  158. std::unique_ptr<uint8_t[]> response =
  159. CreateReplyMessage(request.msgh_remote_port, ports_to_send);
  160. auto* header = reinterpret_cast<mach_msg_header_t*>(response.get());
  161. mr = mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL,
  162. MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  163. if (mr == KERN_SUCCESS) {
  164. scoped_message.Disarm();
  165. } else {
  166. MACH_LOG(ERROR, mr) << "mach_msg send";
  167. }
  168. }
  169. MachPortsForRendezvous MachPortRendezvousServer::PortsForPid(pid_t pid) {
  170. MachPortsForRendezvous ports_to_send;
  171. AutoLock lock(lock_);
  172. auto it = client_data_.find(pid);
  173. if (it != client_data_.end()) {
  174. ports_to_send = std::move(it->second.ports);
  175. client_data_.erase(it);
  176. }
  177. return ports_to_send;
  178. }
  179. std::unique_ptr<uint8_t[]> MachPortRendezvousServer::CreateReplyMessage(
  180. mach_port_t reply_port,
  181. const MachPortsForRendezvous& ports) {
  182. const size_t port_count = ports.size();
  183. const size_t buffer_size = CalculateResponseSize(port_count);
  184. auto buffer = std::make_unique<uint8_t[]>(buffer_size);
  185. BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
  186. auto* message = iterator.MutableObject<mach_msg_base_t>();
  187. message->header.msgh_bits =
  188. MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
  189. MACH_MSGH_BITS_COMPLEX;
  190. message->header.msgh_size = checked_cast<mach_msg_size_t>(buffer_size);
  191. message->header.msgh_remote_port = reply_port;
  192. message->header.msgh_id = kMachRendezvousMsgIdResponse;
  193. message->body.msgh_descriptor_count =
  194. checked_cast<mach_msg_size_t>(port_count);
  195. auto descriptors =
  196. iterator.MutableSpan<mach_msg_port_descriptor_t>(port_count);
  197. auto port_identifiers =
  198. iterator.MutableSpan<MachPortsForRendezvous::key_type>(port_count);
  199. auto port_it = ports.begin();
  200. for (size_t i = 0; i < port_count; ++i, ++port_it) {
  201. const MachRendezvousPort& port_for_rendezvous = port_it->second;
  202. mach_msg_port_descriptor_t* descriptor = &descriptors[i];
  203. descriptor->name = port_for_rendezvous.name();
  204. descriptor->disposition = port_for_rendezvous.disposition();
  205. descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
  206. port_identifiers[i] = port_it->first;
  207. }
  208. return buffer;
  209. }
  210. void MachPortRendezvousServer::OnClientExited(pid_t pid) {
  211. MachPortsForRendezvous ports = PortsForPid(pid);
  212. for (auto& pair : ports) {
  213. pair.second.Destroy();
  214. }
  215. }
  216. // static
  217. MachPortRendezvousClient* MachPortRendezvousClient::GetInstance() {
  218. static MachPortRendezvousClient* client = []() -> auto* {
  219. auto* client = new MachPortRendezvousClient();
  220. if (!client->AcquirePorts()) {
  221. delete client;
  222. client = nullptr;
  223. }
  224. return client;
  225. }
  226. ();
  227. return client;
  228. }
  229. mac::ScopedMachSendRight MachPortRendezvousClient::TakeSendRight(
  230. MachPortsForRendezvous::key_type key) {
  231. MachRendezvousPort port = PortForKey(key);
  232. DCHECK(port.disposition() == 0 ||
  233. port.disposition() == MACH_MSG_TYPE_PORT_SEND ||
  234. port.disposition() == MACH_MSG_TYPE_PORT_SEND_ONCE);
  235. return mac::ScopedMachSendRight(port.name());
  236. }
  237. mac::ScopedMachReceiveRight MachPortRendezvousClient::TakeReceiveRight(
  238. MachPortsForRendezvous::key_type key) {
  239. MachRendezvousPort port = PortForKey(key);
  240. DCHECK(port.disposition() == 0 ||
  241. port.disposition() == MACH_MSG_TYPE_PORT_RECEIVE);
  242. return mac::ScopedMachReceiveRight(port.name());
  243. }
  244. size_t MachPortRendezvousClient::GetPortCount() {
  245. AutoLock lock(lock_);
  246. return ports_.size();
  247. }
  248. // static
  249. std::string MachPortRendezvousClient::GetBootstrapName() {
  250. return StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getppid());
  251. }
  252. bool MachPortRendezvousClient::AcquirePorts() {
  253. AutoLock lock(lock_);
  254. mac::ScopedMachSendRight server_port;
  255. std::string bootstrap_name = GetBootstrapName();
  256. kern_return_t kr = bootstrap_look_up(
  257. bootstrap_port, const_cast<char*>(bootstrap_name.c_str()),
  258. mac::ScopedMachSendRight::Receiver(server_port).get());
  259. if (kr != KERN_SUCCESS) {
  260. BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << bootstrap_name;
  261. return false;
  262. }
  263. return SendRequest(std::move(server_port));
  264. }
  265. bool MachPortRendezvousClient::SendRequest(
  266. mac::ScopedMachSendRight server_port) {
  267. const size_t buffer_size = CalculateResponseSize(kMaximumRendezvousPorts) +
  268. sizeof(mach_msg_trailer_t);
  269. auto buffer = std::make_unique<uint8_t[]>(buffer_size);
  270. BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
  271. // Perform a send and receive mach_msg.
  272. auto* message = iterator.MutableObject<mach_msg_base_t>();
  273. message->header.msgh_bits =
  274. MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
  275. // The |buffer_size| is used for receiving, since it includes space for the
  276. // the entire reply and receiving trailer. But for the request being sent,
  277. // the size is just an empty message.
  278. message->header.msgh_size = sizeof(mach_msg_header_t);
  279. message->header.msgh_remote_port = server_port.release();
  280. message->header.msgh_local_port = mig_get_reply_port();
  281. message->header.msgh_id = kMachRendezvousMsgIdRequest;
  282. kern_return_t mr = mach_msg(
  283. &message->header, MACH_SEND_MSG | MACH_RCV_MSG, message->header.msgh_size,
  284. checked_cast<mach_msg_size_t>(buffer_size),
  285. message->header.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  286. if (mr != KERN_SUCCESS) {
  287. MACH_LOG(ERROR, mr) << "mach_msg";
  288. return false;
  289. }
  290. if (message->header.msgh_id != kMachRendezvousMsgIdResponse) {
  291. // Check if the response contains a rendezvous reply. If there were no
  292. // ports for this client, then the send right would have been destroyed.
  293. if (message->header.msgh_id == MACH_NOTIFY_SEND_ONCE) {
  294. return true;
  295. }
  296. return false;
  297. }
  298. const size_t port_count = message->body.msgh_descriptor_count;
  299. auto descriptors = iterator.Span<mach_msg_port_descriptor_t>(port_count);
  300. auto port_identifiers =
  301. iterator.Span<MachPortsForRendezvous::key_type>(port_count);
  302. if (descriptors.size() != port_identifiers.size()) {
  303. // Ensure that the descriptors and keys are of the same size.
  304. return false;
  305. }
  306. for (size_t i = 0; i < port_count; ++i) {
  307. MachRendezvousPort rendezvous_port(descriptors[i].name,
  308. descriptors[i].disposition);
  309. ports_.emplace(port_identifiers[i], rendezvous_port);
  310. }
  311. return true;
  312. }
  313. MachRendezvousPort MachPortRendezvousClient::PortForKey(
  314. MachPortsForRendezvous::key_type key) {
  315. AutoLock lock(lock_);
  316. auto it = ports_.find(key);
  317. MachRendezvousPort port;
  318. if (it != ports_.end()) {
  319. port = it->second;
  320. ports_.erase(it);
  321. }
  322. return port;
  323. }
  324. MachPortRendezvousClient::MachPortRendezvousClient() = default;
  325. MachPortRendezvousClient::~MachPortRendezvousClient() = default;
  326. } // namespace base