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 
5 #include "mojo/core/channel.h"
6 
7 #include <mach/mach.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <algorithm>
12 #include <memory>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/bind.h"
17 #include "base/containers/buffer_iterator.h"
18 #include "base/containers/circular_deque.h"
19 #include "base/containers/span.h"
20 #include "base/logging.h"
21 #include "base/mac/mach_logging.h"
22 #include "base/mac/scoped_mach_msg_destroy.h"
23 #include "base/mac/scoped_mach_port.h"
24 #include "base/mac/scoped_mach_vm.h"
25 #include "base/message_loop/message_pump_for_io.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/task/current_thread.h"
28 
29 extern "C" {
30 kern_return_t fileport_makeport(int fd, mach_port_t*);
31 int fileport_makefd(mach_port_t);
32 }  // extern "C"
33 
34 namespace mojo {
35 namespace core {
36 
37 namespace {
38 
39 constexpr mach_msg_id_t kChannelMacHandshakeMsgId = 'mjhs';
40 constexpr mach_msg_id_t kChannelMacInlineMsgId = 'MOJO';
41 constexpr mach_msg_id_t kChannelMacOOLMsgId = 'MOJ+';
42 
43 class ChannelMac : public Channel,
44                    public base::CurrentThread::DestructionObserver,
45                    public base::MessagePumpKqueue::MachPortWatcher {
46  public:
ChannelMac(Delegate * delegate,ConnectionParams connection_params,HandlePolicy handle_policy,scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)47   ChannelMac(Delegate* delegate,
48              ConnectionParams connection_params,
49              HandlePolicy handle_policy,
50              scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
51       : Channel(delegate, handle_policy, DispatchBufferPolicy::kUnmanaged),
52         self_(this),
53         io_task_runner_(io_task_runner),
54         watch_controller_(FROM_HERE) {
55     PlatformHandle channel_handle;
56     if (connection_params.server_endpoint().is_valid()) {
57       channel_handle =
58           connection_params.TakeServerEndpoint().TakePlatformHandle();
59     } else {
60       channel_handle = connection_params.TakeEndpoint().TakePlatformHandle();
61     }
62 
63     if (channel_handle.is_mach_send()) {
64       send_port_ = channel_handle.TakeMachSendRight();
65     } else if (channel_handle.is_mach_receive()) {
66       receive_port_ = channel_handle.TakeMachReceiveRight();
67     } else {
68       NOTREACHED();
69     }
70   }
71 
Start()72   void Start() override {
73     io_task_runner_->PostTask(
74         FROM_HERE, base::BindOnce(&ChannelMac::StartOnIOThread, this));
75   }
76 
ShutDownImpl()77   void ShutDownImpl() override {
78     io_task_runner_->PostTask(
79         FROM_HERE, base::BindOnce(&ChannelMac::ShutDownOnIOThread, this));
80   }
81 
Write(MessagePtr message)82   void Write(MessagePtr message) override {
83     base::AutoLock lock(write_lock_);
84 
85     if (reject_writes_) {
86       return;
87     }
88 
89     // If the channel is not fully established, queue pending messages.
90     if (!handshake_done_) {
91       pending_messages_.push_back(std::move(message));
92       return;
93     }
94 
95     // If messages are being queued, enqueue |message| and try to flush
96     // the queue.
97     if (send_buffer_contains_message_ || !pending_messages_.empty()) {
98       pending_messages_.push_back(std::move(message));
99       SendPendingMessagesLocked();
100       return;
101     }
102 
103     SendMessageLocked(std::move(message));
104   }
105 
LeakHandle()106   void LeakHandle() override {
107     DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
108     leak_handles_ = true;
109   }
110 
GetReadPlatformHandles(const void * payload,size_t payload_size,size_t num_handles,const void * extra_header,size_t extra_header_size,std::vector<PlatformHandle> * handles,bool * deferred)111   bool GetReadPlatformHandles(const void* payload,
112                               size_t payload_size,
113                               size_t num_handles,
114                               const void* extra_header,
115                               size_t extra_header_size,
116                               std::vector<PlatformHandle>* handles,
117                               bool* deferred) override {
118     // Validate the incoming handles. If validation fails, ensure they are
119     // destroyed.
120     std::vector<PlatformHandle> incoming_handles;
121     std::swap(incoming_handles, incoming_handles_);
122 
123     if (extra_header_size <
124         sizeof(Message::MachPortsExtraHeader) +
125             (incoming_handles.size() * sizeof(Message::MachPortsEntry))) {
126       return false;
127     }
128 
129     const auto* mach_ports_header =
130         reinterpret_cast<const Message::MachPortsExtraHeader*>(extra_header);
131     if (mach_ports_header->num_ports != incoming_handles.size()) {
132       return false;
133     }
134 
135     for (uint16_t i = 0; i < mach_ports_header->num_ports; ++i) {
136       auto type =
137           static_cast<PlatformHandle::Type>(mach_ports_header->entries[i].type);
138       if (type == PlatformHandle::Type::kNone) {
139         return false;
140       } else if (type == PlatformHandle::Type::kFd &&
141                  incoming_handles[i].is_mach_send()) {
142         int fd = fileport_makefd(incoming_handles[i].GetMachSendRight().get());
143         if (fd < 0) {
144           return false;
145         }
146         incoming_handles[i] = PlatformHandle(base::ScopedFD(fd));
147       } else if (type != incoming_handles[i].type()) {
148         return false;
149       }
150     }
151 
152     *handles = std::move(incoming_handles);
153     return true;
154   }
155 
156  private:
157   ~ChannelMac() override = default;
158 
StartOnIOThread()159   void StartOnIOThread() {
160     vm_address_t address = 0;
161     const vm_size_t size = getpagesize();
162     kern_return_t kr =
163         vm_allocate(mach_task_self(), &address, size,
164                     VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | VM_FLAGS_ANYWHERE);
165     MACH_CHECK(kr == KERN_SUCCESS, kr) << "vm_allocate";
166     send_buffer_.reset(address, size);
167 
168     kr = vm_allocate(mach_task_self(), &address, size,
169                      VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | VM_FLAGS_ANYWHERE);
170     MACH_CHECK(kr == KERN_SUCCESS, kr) << "vm_allocate";
171     receive_buffer_.reset(address, size);
172 
173     // When a channel is created, it only has one end of communication (either
174     // send or receive). If it was created with a receive port, the first thing
175     // a channel does is receive a special channel-internal message containing
176     // its peer's send right. If the channel was created with a send right, it
177     // creates a new receive right and sends to its peer (using the send right
178     // it was created with) a new send right to the receive right. This
179     // establishes the bidirectional communication channel.
180     if (send_port_ != MACH_PORT_NULL) {
181       DCHECK(receive_port_ == MACH_PORT_NULL);
182       CHECK(base::mac::CreateMachPort(&receive_port_, nullptr,
183                                       MACH_PORT_QLIMIT_LARGE));
184       if (!RequestSendDeadNameNotification()) {
185         OnError(Error::kConnectionFailed);
186         return;
187       }
188       SendHandshake();
189     } else if (receive_port_ != MACH_PORT_NULL) {
190       DCHECK(send_port_ == MACH_PORT_NULL);
191       // Wait for the received message via the MessageLoop.
192     } else {
193       NOTREACHED();
194     }
195 
196     base::CurrentThread::Get()->AddDestructionObserver(this);
197     base::CurrentIOThread::Get()->WatchMachReceivePort(
198         receive_port_.get(), &watch_controller_, this);
199   }
200 
ShutDownOnIOThread()201   void ShutDownOnIOThread() {
202     base::CurrentThread::Get()->RemoveDestructionObserver(this);
203 
204     watch_controller_.StopWatchingMachPort();
205 
206     send_buffer_.reset();
207     receive_buffer_.reset();
208     incoming_handles_.clear();
209 
210     if (leak_handles_) {
211       ignore_result(receive_port_.release());
212       ignore_result(send_port_.release());
213     } else {
214       receive_port_.reset();
215       send_port_.reset();
216     }
217 
218     // May destroy the |this| if it was the last reference.
219     self_ = nullptr;
220   }
221 
222   // Requests that the kernel notify the |receive_port_| when the receive right
223   // connected to |send_port_| becomes a dead name. This should be called as
224   // soon as the Channel establishes both the send and receive ports.
RequestSendDeadNameNotification()225   bool RequestSendDeadNameNotification() {
226     base::mac::ScopedMachSendRight previous;
227     kern_return_t kr = mach_port_request_notification(
228         mach_task_self(), send_port_.get(), MACH_NOTIFY_DEAD_NAME, 0,
229         receive_port_.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE,
230         base::mac::ScopedMachSendRight::Receiver(previous).get());
231     if (kr != KERN_SUCCESS) {
232       // If port is already a dead name (i.e. the receiver is already gone),
233       // then the channel should be shut down by the caller.
234       MACH_LOG_IF(ERROR, kr != KERN_INVALID_ARGUMENT, kr)
235           << "mach_port_request_notification";
236       return false;
237     }
238     return true;
239   }
240 
241   // SendHandshake() sends to the |receive_port_| a right to |send_port_|,
242   // establishing bi-directional communication with the peer. After the
243   // handshake message has been sent, this Channel can queue any pending
244   // messages for its peer.
SendHandshake()245   void SendHandshake() {
246     mach_msg_header_t message{};
247     message.msgh_bits =
248         MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
249     message.msgh_size = sizeof(message);
250     message.msgh_remote_port = send_port_.get();
251     message.msgh_local_port = receive_port_.get();
252     message.msgh_id = kChannelMacHandshakeMsgId;
253     kern_return_t kr =
254         mach_msg(&message, MACH_SEND_MSG, sizeof(message), 0, MACH_PORT_NULL,
255                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
256     if (kr != KERN_SUCCESS) {
257       MACH_LOG(ERROR, kr) << "mach_msg send handshake";
258 
259       base::AutoLock lock(write_lock_);
260       OnWriteErrorLocked(Error::kConnectionFailed);
261       return;
262     }
263 
264     base::AutoLock lock(write_lock_);
265     handshake_done_ = true;
266     SendPendingMessagesLocked();
267   }
268 
269   // Acquires the peer's send right from the handshake message sent via
270   // SendHandshake(). After this, bi-directional communication is established
271   // and this Channel can send to its peer any pending messages.
ReceiveHandshake(base::BufferIterator<const char> buffer)272   bool ReceiveHandshake(base::BufferIterator<const char> buffer) {
273     if (handshake_done_) {
274       OnError(Error::kReceivedMalformedData);
275       return false;
276     }
277 
278     DCHECK(send_port_ == MACH_PORT_NULL);
279 
280     auto* message = buffer.Object<mach_msg_header_t>();
281     if (message->msgh_id != kChannelMacHandshakeMsgId ||
282         message->msgh_local_port == MACH_PORT_NULL) {
283       OnError(Error::kConnectionFailed);
284       return false;
285     }
286 
287     // Record the audit token of the sender. All messages received by the
288     // channel must be from this same sender.
289     auto* trailer = buffer.Object<mach_msg_audit_trailer_t>();
290     peer_audit_token_.reset(new audit_token_t);
291     memcpy(peer_audit_token_.get(), &trailer->msgh_audit,
292            sizeof(audit_token_t));
293 
294     send_port_ = base::mac::ScopedMachSendRight(message->msgh_remote_port);
295 
296     if (!RequestSendDeadNameNotification()) {
297       OnError(Error::kConnectionFailed);
298       return false;
299     }
300 
301     base::AutoLock lock(write_lock_);
302     handshake_done_ = true;
303     SendPendingMessagesLocked();
304 
305     return true;
306   }
307 
SendPendingMessages()308   void SendPendingMessages() {
309     base::AutoLock lock(write_lock_);
310     SendPendingMessagesLocked();
311   }
312 
SendPendingMessagesLocked()313   void SendPendingMessagesLocked() {
314     // If a previous send failed due to the receiver's kernel message queue
315     // being full, attempt to send that failed message first.
316     if (send_buffer_contains_message_ && !reject_writes_) {
317       auto* header =
318           reinterpret_cast<mach_msg_header_t*>(send_buffer_.address());
319       if (!MachMessageSendLocked(header)) {
320         // The send failed again. If the peer is still unable to receive,
321         // MachMessageSendLocked() will have arranged another attempt. If an
322         // error occurred, the channel will be shut down.
323         return;
324       }
325     }
326 
327     // Try and send any other pending messages that were queued.
328     while (!pending_messages_.empty() && !reject_writes_) {
329       bool did_send = SendMessageLocked(std::move(pending_messages_.front()));
330       // If the message failed to send because the kernel message queue is
331       // full, the message will have been fully serialized and
332       // |send_buffer_contains_message_| will be set to true. The Mojo message
333       // object can be destroyed at this point.
334       pending_messages_.pop_front();
335       if (!did_send)
336         break;
337     }
338   }
339 
SendMessageLocked(MessagePtr message)340   bool SendMessageLocked(MessagePtr message) {
341     DCHECK(!send_buffer_contains_message_);
342     base::BufferIterator<char> buffer(
343         reinterpret_cast<char*>(send_buffer_.address()), send_buffer_.size());
344 
345     auto* header = buffer.MutableObject<mach_msg_header_t>();
346     *header = mach_msg_header_t{};
347 
348     std::vector<PlatformHandleInTransit> handles = message->TakeHandles();
349 
350     // Compute the total size of the message. If the message data are larger
351     // than the allocated receive buffer, the data will be transferred out-of-
352     // line. The receive buffer is the same size as the send buffer, but there
353     // also needs to be room to receive the trailer.
354     const size_t mach_header_size =
355         sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t) +
356         (handles.size() * sizeof(mach_msg_port_descriptor_t));
357     const size_t expected_message_size =
358         round_msg(mach_header_size + sizeof(uint64_t) +
359                   message->data_num_bytes() + sizeof(mach_msg_audit_trailer_t));
360     const bool transfer_message_ool =
361         expected_message_size >= send_buffer_.size();
362 
363     const bool is_complex = !handles.empty() || transfer_message_ool;
364 
365     header->msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) |
366                         (is_complex ? MACH_MSGH_BITS_COMPLEX : 0);
367     header->msgh_remote_port = send_port_.get();
368     header->msgh_id =
369         transfer_message_ool ? kChannelMacOOLMsgId : kChannelMacInlineMsgId;
370 
371     auto* body = buffer.MutableObject<mach_msg_body_t>();
372     body->msgh_descriptor_count = handles.size();
373 
374     auto descriptors =
375         buffer.MutableSpan<mach_msg_port_descriptor_t>(handles.size());
376     for (size_t i = 0; i < handles.size(); ++i) {
377       auto* descriptor = &descriptors[i];
378       descriptor->pad1 = 0;
379       descriptor->pad2 = 0;
380       descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
381 
382       PlatformHandle handle = handles[i].TakeHandle();
383 
384       switch (handle.type()) {
385         case PlatformHandle::Type::kMachSend:
386           descriptor->name = handle.ReleaseMachSendRight();
387           descriptor->disposition = MACH_MSG_TYPE_MOVE_SEND;
388           break;
389         case PlatformHandle::Type::kMachReceive:
390           descriptor->name = handle.ReleaseMachReceiveRight();
391           descriptor->disposition = MACH_MSG_TYPE_MOVE_RECEIVE;
392           break;
393         case PlatformHandle::Type::kFd: {
394           // After putting the FD in a fileport, the kernel will keep a
395           // reference to the opened file, and the local descriptor can be
396           // closed.
397           kern_return_t kr =
398               fileport_makeport(handle.GetFD().get(), &descriptor->name);
399           if (kr != KERN_SUCCESS) {
400             MACH_LOG(ERROR, kr) << "fileport_makeport";
401             OnWriteErrorLocked(Error::kDisconnected);
402             return false;
403           }
404           descriptor->disposition = MACH_MSG_TYPE_MOVE_SEND;
405           break;
406         }
407         default:
408           NOTREACHED() << "Unsupported handle type "
409                        << static_cast<int>(handle.type());
410           OnWriteErrorLocked(Error::kDisconnected);
411       }
412     }
413 
414     if (transfer_message_ool) {
415       auto* descriptor = buffer.MutableObject<mach_msg_ool_descriptor_t>();
416       descriptor->address = const_cast<void*>(message->data());
417       descriptor->size = message->data_num_bytes();
418       descriptor->copy = MACH_MSG_VIRTUAL_COPY;
419       descriptor->deallocate = false;
420       descriptor->pad1 = 0;
421       descriptor->type = MACH_MSG_OOL_DESCRIPTOR;
422       ++body->msgh_descriptor_count;
423     } else {
424       auto* data_size = buffer.MutableObject<uint64_t>();
425       *data_size = message->data_num_bytes();
426 
427       auto data = buffer.MutableSpan<char>(message->data_num_bytes());
428       memcpy(data.data(), message->data(), message->data_num_bytes());
429     }
430 
431     header->msgh_size = round_msg(buffer.position());
432     return MachMessageSendLocked(header);
433   }
434 
MachMessageSendLocked(mach_msg_header_t * header)435   bool MachMessageSendLocked(mach_msg_header_t* header) {
436     kern_return_t kr = mach_msg(header, MACH_SEND_MSG | MACH_SEND_TIMEOUT,
437                                 header->msgh_size, 0, MACH_PORT_NULL,
438                                 /*timeout=*/0, MACH_PORT_NULL);
439     if (kr != KERN_SUCCESS) {
440       if (kr == MACH_SEND_TIMED_OUT) {
441         // The kernel message queue for the peer's receive port is full, so the
442         // send timed out. Since the send buffer contains a fully serialized
443         // message, set a flag to indicate this condition and arrange to try
444         // sending it again.
445         send_buffer_contains_message_ = true;
446         io_task_runner_->PostTask(
447             FROM_HERE, base::BindOnce(&ChannelMac::SendPendingMessages, this));
448       } else {
449         // If the message failed to send for other reasons, destroy it.
450         send_buffer_contains_message_ = false;
451         mach_msg_destroy(header);
452         if (kr != MACH_SEND_INVALID_DEST) {
453           // If the message failed to send because the receiver is a dead-name,
454           // wait for the Channel to process the dead-name notification.
455           // Otherwise, the notification message will never be received and the
456           // dead-name right contained within it will be leaked
457           // (https://crbug.com/1041682). If the message failed to send for any
458           // other reason, report an error and shut down.
459           MACH_LOG(ERROR, kr) << "mach_msg send";
460           OnWriteErrorLocked(Error::kDisconnected);
461         }
462       }
463       return false;
464     }
465 
466     send_buffer_contains_message_ = false;
467     return true;
468   }
469 
470   // base::CurrentThread::DestructionObserver:
WillDestroyCurrentMessageLoop()471   void WillDestroyCurrentMessageLoop() override {
472     DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
473     if (self_)
474       ShutDownOnIOThread();
475   }
476 
477   // base::MessagePumpKqueue::MachPortWatcher:
OnMachMessageReceived(mach_port_t port)478   void OnMachMessageReceived(mach_port_t port) override {
479     DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
480 
481     base::BufferIterator<const char> buffer(
482         reinterpret_cast<const char*>(receive_buffer_.address()),
483         receive_buffer_.size());
484     auto* header = buffer.MutableObject<mach_msg_header_t>();
485     *header = mach_msg_header_t{};
486     header->msgh_size = buffer.total_size();
487     header->msgh_local_port = receive_port_.get();
488 
489     const mach_msg_option_t rcv_options =
490         MACH_RCV_MSG | MACH_RCV_TIMEOUT |
491         MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
492         MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
493     kern_return_t kr =
494         mach_msg(header, rcv_options, 0, header->msgh_size, receive_port_.get(),
495                  /*timeout=*/0, MACH_PORT_NULL);
496     if (kr != KERN_SUCCESS) {
497       if (kr == MACH_RCV_TIMED_OUT)
498         return;
499       MACH_LOG(ERROR, kr) << "mach_msg receive";
500       OnError(Error::kDisconnected);
501       return;
502     }
503 
504     base::ScopedMachMsgDestroy scoped_message(header);
505 
506     if (header->msgh_id == kChannelMacHandshakeMsgId) {
507       buffer.Seek(0);
508       if (ReceiveHandshake(buffer))
509         scoped_message.Disarm();
510       return;
511     }
512 
513     if (header->msgh_id == MACH_NOTIFY_DEAD_NAME) {
514       // The DEAD_NAME notification contains a port right that must be
515       // explicitly destroyed, as it is not carried in a descriptor.
516       buffer.Seek(0);
517       auto* notification = buffer.Object<mach_dead_name_notification_t>();
518 
519       // Verify that the kernel sent the notification.
520       buffer.Seek(notification->not_header.msgh_size);
521       auto* trailer = buffer.Object<mach_msg_audit_trailer_t>();
522       static const audit_token_t kernel_audit_token = KERNEL_AUDIT_TOKEN_VALUE;
523       if (memcmp(&trailer->msgh_audit, &kernel_audit_token,
524                  sizeof(audit_token_t)) == 0) {
525         DCHECK(notification->not_port == send_port_);
526         // Release the notification's send right using this scoper.
527         base::mac::ScopedMachSendRight notify_port(notification->not_port);
528       }
529       OnError(Error::kDisconnected);
530       return;
531     } else if (header->msgh_id == MACH_NOTIFY_SEND_ONCE) {
532       // Notification of an extant send-once right being destroyed. This is
533       // sent for the right allocated in RequestSendDeadNameNotification(),
534       // and no action needs to be taken. Since it is ignored, the kernel
535       // audit token need not be checked.
536       return;
537     }
538 
539     if (header->msgh_size < sizeof(mach_msg_base_t)) {
540       OnError(Error::kReceivedMalformedData);
541       return;
542     }
543 
544     if (peer_audit_token_) {
545       buffer.Seek(header->msgh_size);
546       auto* trailer = buffer.Object<mach_msg_audit_trailer_t>();
547       if (memcmp(&trailer->msgh_audit, peer_audit_token_.get(),
548                  sizeof(audit_token_t)) != 0) {
549         // Do not shut down the channel because this endpoint could be
550         // accessible via the bootstrap server, which means anyone could send
551         // messages to it.
552         LOG(ERROR) << "Rejecting message from unauthorized peer";
553         return;
554       }
555       buffer.Seek(sizeof(*header));
556     }
557 
558     auto* body = buffer.Object<mach_msg_body_t>();
559     if (((header->msgh_bits & MACH_MSGH_BITS_COMPLEX) != 0) !=
560         (body->msgh_descriptor_count > 0)) {
561       LOG(ERROR) << "Message complex bit does not match descriptor count";
562       OnError(Error::kReceivedMalformedData);
563       return;
564     }
565 
566     bool transfer_message_ool = false;
567     mach_msg_size_t mojo_handle_count = body->msgh_descriptor_count;
568     if (header->msgh_id == kChannelMacOOLMsgId) {
569       transfer_message_ool = true;
570       // The number of Mojo handles to process will be one fewer, since the
571       // message itself was transferred using OOL memory.
572       if (body->msgh_descriptor_count < 1) {
573         LOG(ERROR) << "OOL message does not have descriptor";
574         OnError(Error::kReceivedMalformedData);
575         return;
576       }
577       --mojo_handle_count;
578     } else if (header->msgh_id != kChannelMacInlineMsgId) {
579       OnError(Error::kReceivedMalformedData);
580       return;
581     }
582 
583     incoming_handles_.clear();
584     incoming_handles_.reserve(mojo_handle_count);
585 
586     // Accept the descriptors into |incoming_handles_|. They will be validated
587     // in GetReadPlatformHandles(). If the handle is accepted, the name in the
588     // descriptor is cleared, so that it is not double-unrefed if the
589     // |scoped_message| destroys the message on error.
590     auto descriptors =
591         buffer.MutableSpan<mach_msg_port_descriptor_t>(mojo_handle_count);
592     for (auto& descriptor : descriptors) {
593       if (descriptor.type != MACH_MSG_PORT_DESCRIPTOR) {
594         LOG(ERROR) << "Incorrect descriptor type " << descriptor.type;
595         OnError(Error::kReceivedMalformedData);
596         return;
597       }
598       switch (descriptor.disposition) {
599         case MACH_MSG_TYPE_MOVE_SEND:
600           incoming_handles_.emplace_back(
601               base::mac::ScopedMachSendRight(descriptor.name));
602           descriptor.name = MACH_PORT_NULL;
603           break;
604         case MACH_MSG_TYPE_MOVE_RECEIVE:
605           incoming_handles_.emplace_back(
606               base::mac::ScopedMachReceiveRight(descriptor.name));
607           descriptor.name = MACH_PORT_NULL;
608           break;
609         default:
610           DLOG(ERROR) << "Unhandled descriptor disposition "
611                       << descriptor.disposition;
612           OnError(Error::kReceivedMalformedData);
613           return;
614       }
615     }
616 
617     base::span<const char> payload;
618     base::mac::ScopedMachVM ool_memory;
619     if (transfer_message_ool) {
620       auto* descriptor = buffer.Object<mach_msg_ool_descriptor_t>();
621       if (descriptor->type != MACH_MSG_OOL_DESCRIPTOR) {
622         LOG(ERROR) << "Incorrect descriptor type " << descriptor->type;
623         OnError(Error::kReceivedMalformedData);
624         return;
625       }
626 
627       payload = base::span<const char>(
628           reinterpret_cast<const char*>(descriptor->address), descriptor->size);
629       // The kernel page-aligns the OOL memory when performing the mach_msg on
630       // the send side, but it preserves the original size in the descriptor.
631       ool_memory.reset_unaligned(
632           reinterpret_cast<vm_address_t>(descriptor->address),
633           descriptor->size);
634     } else {
635       auto* data_size_ptr = buffer.Object<uint64_t>();
636       payload = buffer.Span<const char>(*data_size_ptr);
637     }
638 
639     if (payload.empty()) {
640       OnError(Error::kReceivedMalformedData);
641       return;
642     }
643 
644     scoped_message.Disarm();
645 
646     size_t ignored;
647     DispatchResult result = TryDispatchMessage(payload, &ignored);
648     if (result != DispatchResult::kOK) {
649       OnError(Error::kReceivedMalformedData);
650       return;
651     }
652   }
653 
654   // Marks the channel as unaccepting of new messages and shuts it down.
OnWriteErrorLocked(Error error)655   void OnWriteErrorLocked(Error error) {
656     reject_writes_ = true;
657     io_task_runner_->PostTask(
658         FROM_HERE, base::BindOnce(&ChannelMac::OnError, this, error));
659   }
660 
661   // Keeps the Channel alive at least until explicit shutdown on the IO thread.
662   scoped_refptr<ChannelMac> self_;
663 
664   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
665 
666   base::mac::ScopedMachReceiveRight receive_port_;
667   base::mac::ScopedMachSendRight send_port_;
668 
669   // Whether to leak the above Mach ports when the channel is shut down.
670   bool leak_handles_ = false;
671 
672   // Whether or not the channel-internal handshake, which establishes bi-
673   // directional communication, is complete. If false, calls to Write() will
674   // enqueue messages on |pending_messages_|.
675   bool handshake_done_ = false;
676 
677   // If the channel was created with a receive right, the first message it
678   // receives is the internal handshake. The audit token of the sender of the
679   // handshake is recorded here, and all future messages are required to be
680   // from that sender.
681   std::unique_ptr<audit_token_t> peer_audit_token_;
682 
683   // IO buffer for receiving Mach messages. Only accessed on |io_task_runner_|.
684   base::mac::ScopedMachVM receive_buffer_;
685 
686   // Handles that were received with a message that are validated and returned
687   // in GetReadPlatformHandles(). Only accessed on |io_task_runner_|.
688   std::vector<PlatformHandle> incoming_handles_;
689 
690   // Watch controller for |receive_port_|, calls OnMachMessageReceived() when
691   // new messages are available.
692   base::MessagePumpForIO::MachPortWatchController watch_controller_;
693 
694   // Lock that protects the following members.
695   base::Lock write_lock_;
696   // Whether writes should be rejected due to an internal error.
697   bool reject_writes_ = false;
698   // IO buffer for sending Mach messages.
699   base::mac::ScopedMachVM send_buffer_;
700   // If a message timed out during send in MachMessageSendLocked(), this will
701   // be true to indicate that |send_buffer_| contains a message that must
702   // be sent. If this is true, then other calls to Write() queue messages onto
703   // |pending_messages_|.
704   bool send_buffer_contains_message_ = false;
705   // When |handshake_done_| is false or |send_buffer_contains_message_| is true,
706   // calls to Write() will enqueue messages here.
707   base::circular_deque<MessagePtr> pending_messages_;
708 
709   DISALLOW_COPY_AND_ASSIGN(ChannelMac);
710 };
711 
712 }  // namespace
713 
714 MOJO_SYSTEM_IMPL_EXPORT
Create(Channel::Delegate * delegate,ConnectionParams connection_params,Channel::HandlePolicy handle_policy,scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)715 scoped_refptr<Channel> Channel::Create(
716     Channel::Delegate* delegate,
717     ConnectionParams connection_params,
718     Channel::HandlePolicy handle_policy,
719     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
720   return new ChannelMac(delegate, std::move(connection_params), handle_policy,
721                         io_task_runner);
722 }
723 
724 }  // namespace core
725 }  // namespace mojo
726