• 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/containers/buffer_iterator.h"
13 #include "base/logging.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/mac/mach_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(mac::ScopedMachSendRight send_right)62 MachRendezvousPort::MachRendezvousPort(mac::ScopedMachSendRight send_right)
63     : name_(send_right.release()), disposition_(MACH_MSG_TYPE_MOVE_SEND) {}
64 
MachRendezvousPort(mac::ScopedMachReceiveRight receive_right)65 MachRendezvousPort::MachRendezvousPort(
66     mac::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   ScopedDispatchObject<dispatch_source_t> exit_watcher(dispatch_source_create(
120       DISPATCH_SOURCE_TYPE_PROC, static_cast<uintptr_t>(pid),
121       DISPATCH_PROC_EXIT, dispatch_source_->queue()));
122   dispatch_source_set_event_handler(exit_watcher, ^{
123     OnClientExited(pid);
124   });
125   dispatch_resume(exit_watcher);
126 
127   auto it =
128       client_data_.emplace(pid, ClientData{std::move(exit_watcher), ports});
129   DCHECK(it.second);
130 }
131 
ClientData(ScopedDispatchObject<dispatch_source_t> exit_watcher,MachPortsForRendezvous ports)132 MachPortRendezvousServer::ClientData::ClientData(
133     ScopedDispatchObject<dispatch_source_t> exit_watcher,
134     MachPortsForRendezvous ports)
135     : exit_watcher(exit_watcher), ports(ports) {}
136 
137 MachPortRendezvousServer::ClientData::ClientData(ClientData&&) = default;
138 
139 MachPortRendezvousServer::ClientData::~ClientData() = default;
140 
MachPortRendezvousServer()141 MachPortRendezvousServer::MachPortRendezvousServer() {
142   std::string bootstrap_name =
143       StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getpid());
144   kern_return_t kr = bootstrap_check_in(
145       bootstrap_port, bootstrap_name.c_str(),
146       mac::ScopedMachReceiveRight::Receiver(server_port_).get());
147   BOOTSTRAP_CHECK(kr == KERN_SUCCESS, kr)
148       << "bootstrap_check_in " << bootstrap_name;
149 
150   dispatch_source_ = std::make_unique<DispatchSourceMach>(
151       bootstrap_name.c_str(), server_port_.get(), ^{
152         HandleRequest();
153       });
154   dispatch_source_->Resume();
155 }
156 
~MachPortRendezvousServer()157 MachPortRendezvousServer::~MachPortRendezvousServer() {}
158 
HandleRequest()159 void MachPortRendezvousServer::HandleRequest() {
160   // Receive the request message, using the kernel audit token to ascertain the
161   // PID of the sender.
162   struct : mach_msg_header_t {
163     mach_msg_audit_trailer_t trailer;
164   } request{};
165   request.msgh_size = sizeof(request);
166   request.msgh_local_port = server_port_.get();
167 
168   const mach_msg_option_t options =
169       MACH_RCV_MSG | MACH_RCV_TIMEOUT |
170       MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
171       MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
172 
173   mach_msg_return_t mr = mach_msg(&request, options, 0, sizeof(request),
174                                   server_port_.get(), 0, MACH_PORT_NULL);
175   if (mr != KERN_SUCCESS) {
176     MACH_LOG(ERROR, mr) << "mach_msg receive";
177     return;
178   }
179 
180   // Destroy the message in case of an early return, which will release
181   // any rights from a bad message. In the case of a disallowed sender,
182   // the destruction of the reply port will break them out of a mach_msg.
183   ScopedMachMsgDestroy scoped_message(&request);
184 
185   if (request.msgh_id != kMachRendezvousMsgIdRequest ||
186       request.msgh_size != sizeof(mach_msg_header_t)) {
187     // Do not reply to messages that are unexpected.
188     return;
189   }
190 
191   pid_t sender_pid = audit_token_to_pid(request.trailer.msgh_audit);
192   MachPortsForRendezvous ports_to_send = PortsForPid(sender_pid);
193   if (ports_to_send.empty()) {
194     return;
195   }
196 
197   std::unique_ptr<uint8_t[]> response =
198       CreateReplyMessage(request.msgh_remote_port, ports_to_send);
199   auto* header = reinterpret_cast<mach_msg_header_t*>(response.get());
200 
201   mr = mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL,
202                 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
203 
204   if (mr == KERN_SUCCESS) {
205     scoped_message.Disarm();
206   } else {
207     MACH_LOG(ERROR, mr) << "mach_msg send";
208   }
209 }
210 
PortsForPid(pid_t pid)211 MachPortsForRendezvous MachPortRendezvousServer::PortsForPid(pid_t pid) {
212   MachPortsForRendezvous ports_to_send;
213   AutoLock lock(lock_);
214   auto it = client_data_.find(pid);
215   if (it != client_data_.end()) {
216     ports_to_send = std::move(it->second.ports);
217     client_data_.erase(it);
218   }
219   return ports_to_send;
220 }
221 
CreateReplyMessage(mach_port_t reply_port,const MachPortsForRendezvous & ports)222 std::unique_ptr<uint8_t[]> MachPortRendezvousServer::CreateReplyMessage(
223     mach_port_t reply_port,
224     const MachPortsForRendezvous& ports) {
225   const size_t port_count = ports.size();
226   const size_t buffer_size = CalculateResponseSize(port_count);
227   auto buffer = std::make_unique<uint8_t[]>(buffer_size);
228   BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
229 
230   auto* message = iterator.MutableObject<mach_msg_base_t>();
231   message->header.msgh_bits =
232       MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
233       MACH_MSGH_BITS_COMPLEX;
234   message->header.msgh_size = checked_cast<mach_msg_size_t>(buffer_size);
235   message->header.msgh_remote_port = reply_port;
236   message->header.msgh_id = kMachRendezvousMsgIdResponse;
237   message->body.msgh_descriptor_count =
238       checked_cast<mach_msg_size_t>(port_count);
239 
240   auto descriptors =
241       iterator.MutableSpan<mach_msg_port_descriptor_t>(port_count);
242   auto port_identifiers =
243       iterator.MutableSpan<MachPortsForRendezvous::key_type>(port_count);
244 
245   auto port_it = ports.begin();
246   for (size_t i = 0; i < port_count; ++i, ++port_it) {
247     const MachRendezvousPort& port_for_rendezvous = port_it->second;
248     mach_msg_port_descriptor_t* descriptor = &descriptors[i];
249     descriptor->name = port_for_rendezvous.name();
250     descriptor->disposition = port_for_rendezvous.disposition();
251     descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
252 
253     port_identifiers[i] = port_it->first;
254   }
255 
256   return buffer;
257 }
258 
OnClientExited(pid_t pid)259 void MachPortRendezvousServer::OnClientExited(pid_t pid) {
260   MachPortsForRendezvous ports = PortsForPid(pid);
261   for (auto& pair : ports) {
262     pair.second.Destroy();
263   }
264 }
265 
266 // static
GetInstance()267 MachPortRendezvousClient* MachPortRendezvousClient::GetInstance() {
268   static MachPortRendezvousClient* client = []() -> auto* {
269     auto* client = new MachPortRendezvousClient();
270     if (!client->AcquirePorts()) {
271       delete client;
272       client = nullptr;
273     }
274     return client;
275   }
276   ();
277   return client;
278 }
279 
TakeSendRight(MachPortsForRendezvous::key_type key)280 mac::ScopedMachSendRight MachPortRendezvousClient::TakeSendRight(
281     MachPortsForRendezvous::key_type key) {
282   MachRendezvousPort port = PortForKey(key);
283   DCHECK(port.disposition() == 0 ||
284          port.disposition() == MACH_MSG_TYPE_PORT_SEND ||
285          port.disposition() == MACH_MSG_TYPE_PORT_SEND_ONCE);
286   return mac::ScopedMachSendRight(port.name());
287 }
288 
TakeReceiveRight(MachPortsForRendezvous::key_type key)289 mac::ScopedMachReceiveRight MachPortRendezvousClient::TakeReceiveRight(
290     MachPortsForRendezvous::key_type key) {
291   MachRendezvousPort port = PortForKey(key);
292   DCHECK(port.disposition() == 0 ||
293          port.disposition() == MACH_MSG_TYPE_PORT_RECEIVE);
294   return mac::ScopedMachReceiveRight(port.name());
295 }
296 
GetPortCount()297 size_t MachPortRendezvousClient::GetPortCount() {
298   AutoLock lock(lock_);
299   return ports_.size();
300 }
301 
302 // static
GetBootstrapName()303 std::string MachPortRendezvousClient::GetBootstrapName() {
304   return StringPrintf(kBootstrapNameFormat, mac::BaseBundleID(), getppid());
305 }
306 
AcquirePorts()307 bool MachPortRendezvousClient::AcquirePorts() {
308   AutoLock lock(lock_);
309 
310   mac::ScopedMachSendRight server_port;
311   std::string bootstrap_name = GetBootstrapName();
312   kern_return_t kr = bootstrap_look_up(
313       bootstrap_port, const_cast<char*>(bootstrap_name.c_str()),
314       mac::ScopedMachSendRight::Receiver(server_port).get());
315   if (kr != KERN_SUCCESS) {
316     BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << bootstrap_name;
317     return false;
318   }
319 
320   return SendRequest(std::move(server_port));
321 }
322 
SendRequest(mac::ScopedMachSendRight server_port)323 bool MachPortRendezvousClient::SendRequest(
324     mac::ScopedMachSendRight server_port) {
325   const size_t buffer_size = CalculateResponseSize(kMaximumRendezvousPorts) +
326                              sizeof(mach_msg_trailer_t);
327   auto buffer = std::make_unique<uint8_t[]>(buffer_size);
328   BufferIterator<uint8_t> iterator(buffer.get(), buffer_size);
329 
330   // Perform a send and receive mach_msg.
331   auto* message = iterator.MutableObject<mach_msg_base_t>();
332   message->header.msgh_bits =
333       MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
334   // The |buffer_size| is used for receiving, since it includes space for the
335   // the entire reply and receiving trailer. But for the request being sent,
336   // the size is just an empty message.
337   message->header.msgh_size = sizeof(mach_msg_header_t);
338   message->header.msgh_remote_port = server_port.release();
339   message->header.msgh_local_port = mig_get_reply_port();
340   message->header.msgh_id = kMachRendezvousMsgIdRequest;
341 
342   kern_return_t mr = mach_msg(
343       &message->header, MACH_SEND_MSG | MACH_RCV_MSG, message->header.msgh_size,
344       checked_cast<mach_msg_size_t>(buffer_size),
345       message->header.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
346   if (mr != KERN_SUCCESS) {
347     MACH_LOG(ERROR, mr) << "mach_msg";
348     return false;
349   }
350 
351   if (message->header.msgh_id != kMachRendezvousMsgIdResponse) {
352     // Check if the response contains a rendezvous reply. If there were no
353     // ports for this client, then the send right would have been destroyed.
354     if (message->header.msgh_id == MACH_NOTIFY_SEND_ONCE) {
355       return true;
356     }
357     return false;
358   }
359 
360   const size_t port_count = message->body.msgh_descriptor_count;
361 
362   auto descriptors = iterator.Span<mach_msg_port_descriptor_t>(port_count);
363   auto port_identifiers =
364       iterator.Span<MachPortsForRendezvous::key_type>(port_count);
365 
366   if (descriptors.size() != port_identifiers.size()) {
367     // Ensure that the descriptors and keys are of the same size.
368     return false;
369   }
370 
371   for (size_t i = 0; i < port_count; ++i) {
372     MachRendezvousPort rendezvous_port(descriptors[i].name,
373                                        descriptors[i].disposition);
374     ports_.emplace(port_identifiers[i], rendezvous_port);
375   }
376 
377   return true;
378 }
379 
PortForKey(MachPortsForRendezvous::key_type key)380 MachRendezvousPort MachPortRendezvousClient::PortForKey(
381     MachPortsForRendezvous::key_type key) {
382   AutoLock lock(lock_);
383   auto it = ports_.find(key);
384   MachRendezvousPort port;
385   if (it != ports_.end()) {
386     port = it->second;
387     ports_.erase(it);
388   }
389   return port;
390 }
391 
392 MachPortRendezvousClient::MachPortRendezvousClient() = default;
393 
394 MachPortRendezvousClient::~MachPortRendezvousClient() = default;
395 
396 }  // namespace base
397