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_loop_current.h"
26 #include "base/message_loop/message_pump_for_io.h"
27 #include "base/strings/stringprintf.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::MessageLoopCurrent::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::MessageLoopCurrent::Get()->AddDestructionObserver(this);
197 base::MessageLoopCurrentForIO::Get()->WatchMachReceivePort(
198 receive_port_.get(), &watch_controller_, this);
199 }
200
ShutDownOnIOThread()201 void ShutDownOnIOThread() {
202 base::MessageLoopCurrent::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::MessageLoopCurrent::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