1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "util/mach/child_port_handshake.h"
16 
17 #include <errno.h>
18 #include <pthread.h>
19 #include <stdint.h>
20 #include <sys/event.h>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 
26 #include <algorithm>
27 #include <utility>
28 
29 #include "base/check_op.h"
30 #include "base/logging.h"
31 #include "base/mac/mach_logging.h"
32 #include "base/mac/scoped_mach_port.h"
33 #include "base/notreached.h"
34 #include "base/posix/eintr_wrapper.h"
35 #include "base/rand_util.h"
36 #include "base/stl_util.h"
37 #include "base/strings/stringprintf.h"
38 #include "util/file/file_io.h"
39 #include "util/mach/bootstrap.h"
40 #include "util/mach/child_port.h"
41 #include "util/mach/child_port_server.h"
42 #include "util/mach/mach_extensions.h"
43 #include "util/mach/mach_message.h"
44 #include "util/mach/mach_message_server.h"
45 #include "util/misc/implicit_cast.h"
46 #include "util/misc/random_string.h"
47 
48 namespace crashpad {
49 namespace {
50 
51 class ChildPortHandshakeServer final : public ChildPortServer::Interface {
52  public:
53   ChildPortHandshakeServer();
54   ~ChildPortHandshakeServer();
55 
56   mach_port_t RunServer(base::ScopedFD server_write_fd,
57                         ChildPortHandshake::PortRightType port_right_type);
58 
59  private:
60   // ChildPortServer::Interface:
61   kern_return_t HandleChildPortCheckIn(child_port_server_t server,
62                                        child_port_token_t token,
63                                        mach_port_t port,
64                                        mach_msg_type_name_t right_type,
65                                        const mach_msg_trailer_t* trailer,
66                                        bool* destroy_request) override;
67 
68   child_port_token_t token_;
69   mach_port_t port_;
70   mach_msg_type_name_t right_type_;
71   bool checked_in_;
72 
73   DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeServer);
74 };
75 
ChildPortHandshakeServer()76 ChildPortHandshakeServer::ChildPortHandshakeServer()
77     : token_(0),
78       port_(MACH_PORT_NULL),
79       right_type_(MACH_MSG_TYPE_PORT_NONE),
80       checked_in_(false) {
81 }
82 
~ChildPortHandshakeServer()83 ChildPortHandshakeServer::~ChildPortHandshakeServer() {
84 }
85 
RunServer(base::ScopedFD server_write_fd,ChildPortHandshake::PortRightType port_right_type)86 mach_port_t ChildPortHandshakeServer::RunServer(
87     base::ScopedFD server_write_fd,
88     ChildPortHandshake::PortRightType port_right_type) {
89   DCHECK_EQ(port_, kMachPortNull);
90   DCHECK(!checked_in_);
91   DCHECK(server_write_fd.is_valid());
92 
93   // Initialize the token and share it with the client via the pipe.
94   token_ = base::RandUint64();
95   if (!LoggingWriteFile(server_write_fd.get(), &token_, sizeof(token_))) {
96     LOG(WARNING) << "no client check-in";
97     return MACH_PORT_NULL;
98   }
99 
100   // Create a unique name for the bootstrap service mapping. Make it unguessable
101   // to prevent outsiders from grabbing the name first, which would cause
102   // bootstrap_check_in() to fail.
103   uint64_t thread_id;
104   errno = pthread_threadid_np(pthread_self(), &thread_id);
105   PCHECK(errno == 0) << "pthread_threadid_np";
106   std::string service_name = base::StringPrintf(
107       "org.chromium.crashpad.child_port_handshake.%d.%llu.%s",
108       getpid(),
109       thread_id,
110       RandomString().c_str());
111 
112   // Check the new service in with the bootstrap server, obtaining a receive
113   // right for it.
114   base::mac::ScopedMachReceiveRight server_port(BootstrapCheckIn(service_name));
115   CHECK(server_port.is_valid());
116 
117   // Share the service name with the client via the pipe.
118   uint32_t service_name_length = service_name.size();
119   if (!LoggingWriteFile(server_write_fd.get(),
120                         &service_name_length,
121                         sizeof(service_name_length))) {
122     LOG(WARNING) << "no client check-in";
123     return MACH_PORT_NULL;
124   }
125 
126   if (!LoggingWriteFile(
127           server_write_fd.get(), service_name.c_str(), service_name_length)) {
128     LOG(WARNING) << "no client check-in";
129     return MACH_PORT_NULL;
130   }
131 
132   // Prior to macOS 10.12, a kqueue cannot monitor a raw Mach receive right with
133   // EVFILT_MACHPORT. It requires a port set. Compare 10.11.6
134   // xnu-3248.60.10/osfmk/ipc/ipc_pset.c filt_machportattach(), which requires
135   // MACH_PORT_RIGHT_PORT_SET, to 10.12.0 xnu-3789.1.32/osfmk/ipc/ipc_pset.c
136   // filt_machportattach(), which also handles MACH_PORT_TYPE_RECEIVE. Create a
137   // new port set and add the receive right to it.
138   base::mac::ScopedMachPortSet server_port_set(
139       NewMachPort(MACH_PORT_RIGHT_PORT_SET));
140   CHECK(server_port_set.is_valid());
141 
142   kern_return_t kr = mach_port_insert_member(
143       mach_task_self(), server_port.get(), server_port_set.get());
144   MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
145 
146   // Set up a kqueue to monitor both the server’s receive right and the write
147   // side of the pipe. Messages from the client will be received via the receive
148   // right, and the pipe will show EOF if the client closes its read side
149   // prematurely.
150   base::ScopedFD kq(kqueue());
151   PCHECK(kq != -1) << "kqueue";
152 
153   struct kevent changelist[2];
154   EV_SET(&changelist[0],
155          server_port_set.get(),
156          EVFILT_MACHPORT,
157          EV_ADD | EV_CLEAR,
158          0,
159          0,
160          nullptr);
161   EV_SET(&changelist[1],
162          server_write_fd.get(),
163          EVFILT_WRITE,
164          EV_ADD | EV_CLEAR,
165          0,
166          0,
167          nullptr);
168   int rv = HANDLE_EINTR(kevent(
169       kq.get(), changelist, base::size(changelist), nullptr, 0, nullptr));
170   PCHECK(rv != -1) << "kevent";
171 
172   ChildPortServer child_port_server(this);
173 
174   bool blocking = true;
175   DCHECK(!checked_in_);
176   while (!checked_in_) {
177     DCHECK_EQ(port_, kMachPortNull);
178 
179     // Get a kevent from the kqueue. Block while waiting for an event unless the
180     // write pipe has arrived at EOF, in which case the kevent() should be
181     // nonblocking. Although the client sends its check-in message before
182     // closing the read side of the pipe, this organization allows the events to
183     // be delivered out of order and the check-in message will still be
184     // processed.
185     struct kevent event;
186     constexpr timespec nonblocking_timeout = {};
187     const timespec* timeout = blocking ? nullptr : &nonblocking_timeout;
188     rv = HANDLE_EINTR(kevent(kq.get(), nullptr, 0, &event, 1, timeout));
189     PCHECK(rv != -1) << "kevent";
190 
191     if (rv == 0) {
192       // Non-blocking kevent() with no events to return.
193       DCHECK(!blocking);
194       LOG(WARNING) << "no client check-in";
195       return MACH_PORT_NULL;
196     }
197 
198     DCHECK_EQ(rv, 1);
199 
200     if (event.flags & EV_ERROR) {
201       // kevent() may have put its error here.
202       errno = event.data;
203       PLOG(FATAL) << "kevent";
204     }
205 
206     switch (event.filter) {
207       case EVFILT_MACHPORT: {
208         // There’s something to receive on the port set.
209         DCHECK_EQ(event.ident, server_port_set.get());
210 
211         // Run the message server in an inner loop instead of using
212         // MachMessageServer::kPersistent. This allows the loop to exit as soon
213         // as child_port_ is set, even if other messages are queued. This needs
214         // to drain all messages, because the use of edge triggering (EV_CLEAR)
215         // means that if more than one message is in the queue when kevent()
216         // returns, no more notifications will be generated.
217         while (!checked_in_) {
218           // If a proper message is received from child_port_check_in(),
219           // this will call HandleChildPortCheckIn().
220           mach_msg_return_t mr =
221               MachMessageServer::Run(&child_port_server,
222                                      server_port_set.get(),
223                                      MACH_MSG_OPTION_NONE,
224                                      MachMessageServer::kOneShot,
225                                      MachMessageServer::kReceiveLargeIgnore,
226                                      kMachMessageTimeoutNonblocking);
227           if (mr == MACH_RCV_TIMED_OUT) {
228             break;
229           } else if (mr != MACH_MSG_SUCCESS) {
230             MACH_LOG(ERROR, mr) << "MachMessageServer::Run";
231             return MACH_PORT_NULL;
232           }
233         }
234         break;
235       }
236 
237       case EVFILT_WRITE:
238         // The write pipe is ready to be written to, or it’s at EOF. The former
239         // case is uninteresting, but a notification for this may be presented
240         // because the write pipe will be ready to be written to, at the latest,
241         // when the client reads its messages from the read side of the same
242         // pipe. Ignore that case. Multiple notifications for that situation
243         // will not be generated because edge triggering (EV_CLEAR) is used
244         // above.
245         DCHECK_EQ(implicit_cast<int>(event.ident), server_write_fd.get());
246         if (event.flags & EV_EOF) {
247           // There are no readers attached to the write pipe. The client has
248           // closed its side of the pipe. There can be one last shot at
249           // receiving messages, in case the check-in message is delivered
250           // out of order, after the EOF notification.
251           blocking = false;
252         }
253         break;
254 
255       default:
256         NOTREACHED();
257         break;
258     }
259   }
260 
261   if (port_ == MACH_PORT_NULL) {
262     return MACH_PORT_NULL;
263   }
264 
265   bool mismatch = false;
266   switch (port_right_type) {
267     case ChildPortHandshake::PortRightType::kReceiveRight:
268       if (right_type_ != MACH_MSG_TYPE_PORT_RECEIVE) {
269         LOG(ERROR) << "expected receive right, observed " << right_type_;
270         mismatch = true;
271       }
272       break;
273     case ChildPortHandshake::PortRightType::kSendRight:
274       if (right_type_ != MACH_MSG_TYPE_PORT_SEND &&
275           right_type_ != MACH_MSG_TYPE_PORT_SEND_ONCE) {
276         LOG(ERROR) << "expected send or send-once right, observed "
277                    << right_type_;
278         mismatch = true;
279       }
280       break;
281   }
282 
283   if (mismatch) {
284     MachMessageDestroyReceivedPort(port_, right_type_);
285     port_ = MACH_PORT_NULL;
286     return MACH_PORT_NULL;
287   }
288 
289   mach_port_t port = MACH_PORT_NULL;
290   std::swap(port_, port);
291   return port;
292 }
293 
HandleChildPortCheckIn(child_port_server_t server,const child_port_token_t token,mach_port_t port,mach_msg_type_name_t right_type,const mach_msg_trailer_t * trailer,bool * destroy_request)294 kern_return_t ChildPortHandshakeServer::HandleChildPortCheckIn(
295     child_port_server_t server,
296     const child_port_token_t token,
297     mach_port_t port,
298     mach_msg_type_name_t right_type,
299     const mach_msg_trailer_t* trailer,
300     bool* destroy_request) {
301   DCHECK_EQ(port_, kMachPortNull);
302   DCHECK(!checked_in_);
303 
304   if (token != token_) {
305     // If the token’s not correct, someone’s attempting to spoof the legitimate
306     // client.
307     LOG(WARNING) << "ignoring incorrect token";
308     *destroy_request = true;
309   } else {
310     checked_in_ = true;
311 
312     if (right_type != MACH_MSG_TYPE_PORT_RECEIVE &&
313         right_type != MACH_MSG_TYPE_PORT_SEND &&
314         right_type != MACH_MSG_TYPE_PORT_SEND_ONCE) {
315       // The message needs to carry a receive, send, or send-once right.
316       LOG(ERROR) << "invalid right type " << right_type;
317       *destroy_request = true;
318     } else {
319       // Communicate the child port and right type back to the RunServer().
320       // *destroy_request is left at false, because RunServer() needs the right
321       // to remain intact. It gives ownership of the right to its caller.
322       port_ = port;
323       right_type_ = right_type;
324     }
325   }
326 
327   // This is a MIG simpleroutine, there is no reply message.
328   return MIG_NO_REPLY;
329 }
330 
331 }  // namespace
332 
ChildPortHandshake()333 ChildPortHandshake::ChildPortHandshake()
334     : client_read_fd_(),
335       server_write_fd_() {
336   // Use socketpair() instead of pipe(). There is no way to suppress SIGPIPE on
337   // pipes in Mac OS X 10.6, because the F_SETNOSIGPIPE fcntl() command was not
338   // introduced until 10.7.
339   int pipe_fds[2];
340   PCHECK(socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fds) == 0)
341       << "socketpair";
342 
343   client_read_fd_.reset(pipe_fds[0]);
344   server_write_fd_.reset(pipe_fds[1]);
345 
346   // Simulate pipe() semantics by shutting down the “wrong” sides of the socket.
347   PCHECK(shutdown(server_write_fd_.get(), SHUT_RD) == 0) << "shutdown SHUT_RD";
348   PCHECK(shutdown(client_read_fd_.get(), SHUT_WR) == 0) << "shutdown SHUT_WR";
349 
350   // SIGPIPE is undesirable when writing to this pipe. Allow broken-pipe writes
351   // to fail with EPIPE instead.
352   constexpr int value = 1;
353   PCHECK(setsockopt(server_write_fd_.get(),
354                     SOL_SOCKET,
355                     SO_NOSIGPIPE,
356                     &value,
357                     sizeof(value)) == 0) << "setsockopt";
358 }
359 
~ChildPortHandshake()360 ChildPortHandshake::~ChildPortHandshake() {
361 }
362 
ClientReadFD()363 base::ScopedFD ChildPortHandshake::ClientReadFD() {
364   DCHECK(client_read_fd_.is_valid());
365   return std::move(client_read_fd_);
366 }
367 
ServerWriteFD()368 base::ScopedFD ChildPortHandshake::ServerWriteFD() {
369   DCHECK(server_write_fd_.is_valid());
370   return std::move(server_write_fd_);
371 }
372 
RunServer(PortRightType port_right_type)373 mach_port_t ChildPortHandshake::RunServer(PortRightType port_right_type) {
374   client_read_fd_.reset();
375   return RunServerForFD(std::move(server_write_fd_), port_right_type);
376 }
377 
RunClient(mach_port_t port,mach_msg_type_name_t right_type)378 bool ChildPortHandshake::RunClient(mach_port_t port,
379                                    mach_msg_type_name_t right_type) {
380   server_write_fd_.reset();
381   return RunClientForFD(std::move(client_read_fd_), port, right_type);
382 }
383 
384 // static
RunServerForFD(base::ScopedFD server_write_fd,PortRightType port_right_type)385 mach_port_t ChildPortHandshake::RunServerForFD(base::ScopedFD server_write_fd,
386                                                PortRightType port_right_type) {
387   ChildPortHandshakeServer server;
388   return server.RunServer(std::move(server_write_fd), port_right_type);
389 }
390 
391 // static
RunClientForFD(base::ScopedFD client_read_fd,mach_port_t port,mach_msg_type_name_t right_type)392 bool ChildPortHandshake::RunClientForFD(base::ScopedFD client_read_fd,
393                                         mach_port_t port,
394                                         mach_msg_type_name_t right_type) {
395   DCHECK(client_read_fd.is_valid());
396 
397   // Read the token and the service name from the read side of the pipe.
398   child_port_token_t token;
399   std::string service_name;
400   if (!RunClientInternal_ReadPipe(
401           client_read_fd.get(), &token, &service_name)) {
402     return false;
403   }
404 
405   // Look up the server and check in with it by providing the token and port.
406   return RunClientInternal_SendCheckIn(service_name, token, port, right_type);
407 }
408 
409 // static
RunClientInternal_ReadPipe(int client_read_fd,child_port_token_t * token,std::string * service_name)410 bool ChildPortHandshake::RunClientInternal_ReadPipe(int client_read_fd,
411                                                     child_port_token_t* token,
412                                                     std::string* service_name) {
413   // Read the token from the pipe.
414   if (!LoggingReadFileExactly(client_read_fd, token, sizeof(*token))) {
415     return false;
416   }
417 
418   // Read the service name from the pipe.
419   uint32_t service_name_length;
420   if (!LoggingReadFileExactly(
421           client_read_fd, &service_name_length, sizeof(service_name_length))) {
422     return false;
423   }
424 
425   service_name->resize(service_name_length);
426   if (!service_name->empty() &&
427       !LoggingReadFileExactly(
428           client_read_fd, &(*service_name)[0], service_name_length)) {
429     return false;
430   }
431 
432   return true;
433 }
434 
435 // static
RunClientInternal_SendCheckIn(const std::string & service_name,child_port_token_t token,mach_port_t port,mach_msg_type_name_t right_type)436 bool ChildPortHandshake::RunClientInternal_SendCheckIn(
437     const std::string& service_name,
438     child_port_token_t token,
439     mach_port_t port,
440     mach_msg_type_name_t right_type) {
441   // Get a send right to the server by looking up the service with the bootstrap
442   // server by name.
443   base::mac::ScopedMachSendRight server_port(BootstrapLookUp(service_name));
444   if (server_port == kMachPortNull) {
445     return false;
446   }
447 
448   // Check in with the server.
449   kern_return_t kr = child_port_check_in(
450       server_port.get(), token, port, right_type);
451   if (kr != KERN_SUCCESS) {
452     MACH_LOG(ERROR, kr) << "child_port_check_in";
453     return false;
454   }
455 
456   return true;
457 }
458 
459 }  // namespace crashpad
460