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