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