2 * Copyright (C) 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "Connection.h"
29 #include "DataReference.h"
30 #include "ImportanceAssertion.h"
32 #include "MachUtilities.h"
33 #include <WebCore/AXObjectCache.h>
34 #include <mach/mach_error.h>
35 #include <mach/vm_map.h>
37 #include <wtf/RunLoop.h>
38 #include <wtf/spi/darwin/XPCSPI.h>
41 #include "ProcessAssertion.h"
44 #if __has_include(<HIServices/AccessibilityPriv.h>)
45 #include <HIServices/AccessibilityPriv.h>
48 AXSuspendStatusRunning = 0,
49 AXSuspendStatusSuspended,
53 #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
54 extern "C" AXError _AXUIElementNotifyProcessSuspendStatus(AXSuspendStatus);
59 static const size_t inlineMessageMaxSize = 4096;
63 MessageBodyIsOutOfLine = 1 << 0
66 // ConnectionTerminationWatchdog does two things:
67 // 1) It sets a watchdog timer to kill the peered process.
68 // 2) On iOS, make the process runnable for the duration of the watchdog
69 // to ensure it has a chance to terminate cleanly.
70 class ConnectionTerminationWatchdog {
72 static void createConnectionTerminationWatchdog(OSObjectPtr<xpc_connection_t>& xpcConnection, double intervalInSeconds)
74 new ConnectionTerminationWatchdog(xpcConnection, intervalInSeconds);
78 ConnectionTerminationWatchdog(OSObjectPtr<xpc_connection_t>& xpcConnection, double intervalInSeconds)
79 : m_xpcConnection(xpcConnection)
80 , m_watchdogTimer(RunLoop::main(), this, &ConnectionTerminationWatchdog::watchdogTimerFired)
82 , m_assertion(std::make_unique<WebKit::ProcessAndUIAssertion>(xpc_connection_get_pid(m_xpcConnection.get()), WebKit::AssertionState::Background))
85 m_watchdogTimer.startOneShot(intervalInSeconds);
88 void watchdogTimerFired()
90 xpc_connection_kill(m_xpcConnection.get(), SIGKILL);
94 OSObjectPtr<xpc_connection_t> m_xpcConnection;
95 RunLoop::Timer<ConnectionTerminationWatchdog> m_watchdogTimer;
97 std::unique_ptr<WebKit::ProcessAndUIAssertion> m_assertion;
101 void Connection::platformInvalidate()
106 m_isConnected = false;
109 ASSERT(m_receivePort);
111 // Unregister our ports.
112 dispatch_source_cancel(m_deadNameSource);
113 dispatch_release(m_deadNameSource);
114 m_deadNameSource = 0;
115 m_sendPort = MACH_PORT_NULL;
117 dispatch_source_cancel(m_receivePortDataAvailableSource);
118 dispatch_release(m_receivePortDataAvailableSource);
119 m_receivePortDataAvailableSource = 0;
120 m_receivePort = MACH_PORT_NULL;
123 if (m_exceptionPort) {
124 dispatch_source_cancel(m_exceptionPortDataAvailableSource);
125 dispatch_release(m_exceptionPortDataAvailableSource);
126 m_exceptionPortDataAvailableSource = 0;
127 m_exceptionPort = MACH_PORT_NULL;
131 m_xpcConnection = nullptr;
134 void Connection::terminateSoon(double intervalInSeconds)
137 ConnectionTerminationWatchdog::createConnectionTerminationWatchdog(m_xpcConnection, intervalInSeconds);
140 void Connection::platformInitialize(Identifier identifier)
143 m_exceptionPort = MACH_PORT_NULL;
144 m_exceptionPortDataAvailableSource = nullptr;
148 m_receivePort = identifier.port;
149 m_sendPort = MACH_PORT_NULL;
151 m_receivePort = MACH_PORT_NULL;
152 m_sendPort = identifier.port;
155 m_deadNameSource = nullptr;
156 m_receivePortDataAvailableSource = nullptr;
158 m_xpcConnection = identifier.xpcConnection;
161 template<typename Function>
162 static dispatch_source_t createDataAvailableSource(mach_port_t receivePort, WorkQueue& workQueue, Function&& function)
164 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, receivePort, 0, workQueue.dispatchQueue());
165 dispatch_source_set_event_handler(source, function);
167 dispatch_source_set_cancel_handler(source, ^{
168 mach_port_mod_refs(mach_task_self(), receivePort, MACH_PORT_RIGHT_RECEIVE, -1);
174 bool Connection::open()
177 ASSERT(m_receivePort);
181 ASSERT(!m_receivePort);
184 // Create the receive port.
185 mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &m_receivePort);
187 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
188 mach_port_set_attributes(mach_task_self(), m_receivePort, MACH_PORT_DENAP_RECEIVER, (mach_port_info_t)0, 0);
190 mach_port_set_attributes(mach_task_self(), m_receivePort, MACH_PORT_IMPORTANCE_RECEIVER, (mach_port_info_t)0, 0);
193 m_isConnected = true;
195 // Send the initialize message, which contains a send right for the server to use.
196 auto encoder = std::make_unique<MessageEncoder>("IPC", "InitializeConnection", 0);
197 encoder->encode(MachPort(m_receivePort, MACH_MSG_TYPE_MAKE_SEND));
199 sendMessage(WTF::move(encoder));
201 initializeDeadNameSource();
204 // Change the message queue length for the receive port.
205 setMachPortQueueLength(m_receivePort, MACH_PORT_QLIMIT_LARGE);
207 // Register the data available handler.
208 RefPtr<Connection> connection(this);
209 m_receivePortDataAvailableSource = createDataAvailableSource(m_receivePort, m_connectionQueue, [connection] {
210 connection->receiveSourceEventHandler();
214 // If we have an exception port, register the data available handler and send over the port to the other end.
215 if (m_exceptionPort) {
216 m_exceptionPortDataAvailableSource = createDataAvailableSource(m_exceptionPort, m_connectionQueue, [connection] {
217 connection->exceptionSourceEventHandler();
220 auto encoder = std::make_unique<MessageEncoder>("IPC", "SetExceptionPort", 0);
221 encoder->encode(MachPort(m_exceptionPort, MACH_MSG_TYPE_MAKE_SEND));
223 sendMessage(WTF::move(encoder));
228 dispatch_async(m_connectionQueue->dispatchQueue(), ^{
229 dispatch_resume(m_receivePortDataAvailableSource);
231 if (m_deadNameSource)
232 dispatch_resume(m_deadNameSource);
234 if (m_exceptionPortDataAvailableSource)
235 dispatch_resume(m_exceptionPortDataAvailableSource);
244 static inline size_t machMessageSize(size_t bodySize, size_t numberOfPortDescriptors = 0, size_t numberOfOOLMemoryDescriptors = 0)
246 size_t size = sizeof(mach_msg_header_t) + bodySize;
247 if (numberOfPortDescriptors || numberOfOOLMemoryDescriptors) {
248 size += sizeof(mach_msg_body_t);
249 if (numberOfPortDescriptors)
250 size += (numberOfPortDescriptors * sizeof(mach_msg_port_descriptor_t));
251 if (numberOfOOLMemoryDescriptors)
252 size += (numberOfOOLMemoryDescriptors * sizeof(mach_msg_ool_descriptor_t));
254 return round_msg(size);
257 bool Connection::platformCanSendOutgoingMessages() const
262 bool Connection::sendOutgoingMessage(std::unique_ptr<MessageEncoder> encoder)
264 Vector<Attachment> attachments = encoder->releaseAttachments();
266 size_t numberOfPortDescriptors = 0;
267 size_t numberOfOOLMemoryDescriptors = 0;
268 for (size_t i = 0; i < attachments.size(); ++i) {
269 Attachment::Type type = attachments[i].type();
270 if (type == Attachment::MachPortType)
271 numberOfPortDescriptors++;
274 size_t messageSize = machMessageSize(encoder->bufferSize(), numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
276 bool messageBodyIsOOL = false;
277 if (messageSize > inlineMessageMaxSize) {
278 messageBodyIsOOL = true;
280 numberOfOOLMemoryDescriptors++;
281 messageSize = machMessageSize(0, numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
284 char stackBuffer[inlineMessageMaxSize];
285 char* buffer = &stackBuffer[0];
286 if (messageSize > inlineMessageMaxSize) {
287 buffer = (char*)mmap(0, messageSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
288 if (buffer == MAP_FAILED)
292 bool isComplex = (numberOfPortDescriptors + numberOfOOLMemoryDescriptors > 0);
294 mach_msg_header_t* header = reinterpret_cast<mach_msg_header_t*>(buffer);
295 header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
296 header->msgh_size = messageSize;
297 header->msgh_remote_port = m_sendPort;
298 header->msgh_local_port = MACH_PORT_NULL;
300 if (messageBodyIsOOL)
301 header->msgh_id |= MessageBodyIsOutOfLine;
303 uint8_t* messageData;
306 header->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
308 mach_msg_body_t* body = reinterpret_cast<mach_msg_body_t*>(header + 1);
309 body->msgh_descriptor_count = numberOfPortDescriptors + numberOfOOLMemoryDescriptors;
310 uint8_t* descriptorData = reinterpret_cast<uint8_t*>(body + 1);
312 for (size_t i = 0; i < attachments.size(); ++i) {
313 Attachment attachment = attachments[i];
315 mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData);
316 switch (attachment.type()) {
317 case Attachment::MachPortType:
318 descriptor->port.name = attachment.port();
319 descriptor->port.disposition = attachment.disposition();
320 descriptor->port.type = MACH_MSG_PORT_DESCRIPTOR;
322 descriptorData += sizeof(mach_msg_port_descriptor_t);
325 ASSERT_NOT_REACHED();
329 if (messageBodyIsOOL) {
330 mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData);
332 descriptor->out_of_line.address = encoder->buffer();
333 descriptor->out_of_line.size = encoder->bufferSize();
334 descriptor->out_of_line.copy = MACH_MSG_VIRTUAL_COPY;
335 descriptor->out_of_line.deallocate = false;
336 descriptor->out_of_line.type = MACH_MSG_OOL_DESCRIPTOR;
338 descriptorData += sizeof(mach_msg_ool_descriptor_t);
341 messageData = descriptorData;
343 messageData = (uint8_t*)(header + 1);
345 // Copy the data if it is not being sent out-of-line.
346 if (!messageBodyIsOOL)
347 memcpy(messageData, encoder->buffer(), encoder->bufferSize());
352 kern_return_t kr = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
353 if (kr != KERN_SUCCESS) {
354 // FIXME: What should we do here?
357 if (buffer != &stackBuffer[0])
358 munmap(buffer, messageSize);
363 void Connection::initializeDeadNameSource()
365 m_deadNameSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, m_sendPort, 0, m_connectionQueue->dispatchQueue());
367 RefPtr<Connection> connection(this);
368 dispatch_source_set_event_handler(m_deadNameSource, [connection] {
369 connection->connectionDidClose();
372 mach_port_t sendPort = m_sendPort;
373 dispatch_source_set_cancel_handler(m_deadNameSource, ^{
374 // Release our send right.
375 mach_port_deallocate(mach_task_self(), sendPort);
379 static std::unique_ptr<MessageDecoder> createMessageDecoder(mach_msg_header_t* header)
381 if (!(header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
382 // We have a simple message.
383 uint8_t* body = reinterpret_cast<uint8_t*>(header + 1);
384 size_t bodySize = header->msgh_size - sizeof(mach_msg_header_t);
386 return std::make_unique<MessageDecoder>(DataReference(body, bodySize), Vector<Attachment>());
389 bool messageBodyIsOOL = header->msgh_id & MessageBodyIsOutOfLine;
391 mach_msg_body_t* body = reinterpret_cast<mach_msg_body_t*>(header + 1);
392 mach_msg_size_t numDescriptors = body->msgh_descriptor_count;
393 ASSERT(numDescriptors);
395 uint8_t* descriptorData = reinterpret_cast<uint8_t*>(body + 1);
397 // If the message body was sent out-of-line, don't treat the last descriptor
398 // as an attachment, since it is really the message body.
399 if (messageBodyIsOOL)
402 // Build attachment list
403 Vector<Attachment> attachments(numDescriptors);
405 for (mach_msg_size_t i = 0; i < numDescriptors; ++i) {
406 mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData);
408 switch (descriptor->type.type) {
409 case MACH_MSG_PORT_DESCRIPTOR:
410 attachments[numDescriptors - i - 1] = Attachment(descriptor->port.name, descriptor->port.disposition);
411 descriptorData += sizeof(mach_msg_port_descriptor_t);
414 ASSERT(false && "Unhandled descriptor type");
418 if (messageBodyIsOOL) {
419 mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData);
420 ASSERT(descriptor->type.type == MACH_MSG_OOL_DESCRIPTOR);
422 uint8_t* messageBody = static_cast<uint8_t*>(descriptor->out_of_line.address);
423 size_t messageBodySize = descriptor->out_of_line.size;
425 auto decoder = std::make_unique<MessageDecoder>(DataReference(messageBody, messageBodySize), WTF::move(attachments));
427 vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(descriptor->out_of_line.address), descriptor->out_of_line.size);
432 uint8_t* messageBody = descriptorData;
433 size_t messageBodySize = header->msgh_size - (descriptorData - reinterpret_cast<uint8_t*>(header));
435 return std::make_unique<MessageDecoder>(DataReference(messageBody, messageBodySize), attachments);
438 // The receive buffer size should always include the maximum trailer size.
439 static const size_t receiveBufferSize = inlineMessageMaxSize + MAX_TRAILER_SIZE;
440 typedef Vector<char, receiveBufferSize> ReceiveBuffer;
442 static mach_msg_header_t* readFromMachPort(mach_port_t machPort, ReceiveBuffer& buffer)
444 buffer.resize(receiveBufferSize);
446 mach_msg_header_t* header = reinterpret_cast<mach_msg_header_t*>(buffer.data());
447 kern_return_t kr = mach_msg(header, MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT, 0, buffer.size(), machPort, 0, MACH_PORT_NULL);
448 if (kr == MACH_RCV_TIMED_OUT)
451 if (kr == MACH_RCV_TOO_LARGE) {
452 // The message was too large, resize the buffer and try again.
453 buffer.resize(header->msgh_size + MAX_TRAILER_SIZE);
454 header = reinterpret_cast<mach_msg_header_t*>(buffer.data());
456 kr = mach_msg(header, MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT, 0, buffer.size(), machPort, 0, MACH_PORT_NULL);
457 ASSERT(kr != MACH_RCV_TOO_LARGE);
460 if (kr != MACH_MSG_SUCCESS) {
461 ASSERT_NOT_REACHED();
468 void Connection::receiveSourceEventHandler()
470 ReceiveBuffer buffer;
472 mach_msg_header_t* header = readFromMachPort(m_receivePort, buffer);
476 std::unique_ptr<MessageDecoder> decoder = createMessageDecoder(header);
480 decoder->setImportanceAssertion(std::make_unique<ImportanceAssertion>(header));
483 if (decoder->messageReceiverName() == "IPC" && decoder->messageName() == "InitializeConnection") {
485 ASSERT(!m_isConnected);
489 if (!decoder->decode(port)) {
490 // FIXME: Disconnect.
494 m_sendPort = port.port();
497 initializeDeadNameSource();
498 dispatch_resume(m_deadNameSource);
501 m_isConnected = true;
503 // Send any pending outgoing messages.
504 sendOutgoingMessages();
510 if (decoder->messageReceiverName() == "IPC" && decoder->messageName() == "SetExceptionPort") {
512 // Server connections aren't supposed to have their exception ports overriden. Treat this as an invalid message.
513 m_clientRunLoop.dispatch(bind(&Connection::dispatchDidReceiveInvalidMessage, this, decoder->messageReceiverName().toString(), decoder->messageName().toString()));
516 MachPort exceptionPort;
517 if (!decoder->decode(exceptionPort))
520 setMachExceptionPort(exceptionPort.port());
525 processIncomingMessage(WTF::move(decoder));
529 void Connection::exceptionSourceEventHandler()
531 ReceiveBuffer buffer;
533 mach_msg_header_t* header = readFromMachPort(m_exceptionPort, buffer);
537 // We've read the exception message. Now send it on to the real exception port.
539 // The remote port should have a send once right.
540 ASSERT(MACH_MSGH_BITS_REMOTE(header->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE);
542 // Now get the real exception port.
543 mach_port_t exceptionPort = machExceptionPort();
545 // First, get the complex bit from the source message.
546 mach_msg_bits_t messageBits = header->msgh_bits & MACH_MSGH_BITS_COMPLEX;
547 messageBits |= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
549 header->msgh_bits = messageBits;
550 header->msgh_local_port = header->msgh_remote_port;
551 header->msgh_remote_port = exceptionPort;
553 // Now send along the message.
554 kern_return_t kr = mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
555 if (kr != KERN_SUCCESS)
556 LOG_ERROR("Failed to send message to real exception port. %s (%x)", mach_error_string(kr), kr);
558 connectionDidClose();
561 void Connection::setShouldCloseConnectionOnMachExceptions()
563 ASSERT(m_exceptionPort == MACH_PORT_NULL);
565 if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &m_exceptionPort) != KERN_SUCCESS)
566 ASSERT_NOT_REACHED();
568 if (mach_port_insert_right(mach_task_self(), m_exceptionPort, m_exceptionPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)
569 ASSERT_NOT_REACHED();
573 IPC::Connection::Identifier Connection::identifier() const
575 return Identifier(m_isServer ? m_receivePort : m_sendPort, m_xpcConnection);
578 bool Connection::getAuditToken(audit_token_t& auditToken)
580 if (!m_xpcConnection)
583 xpc_connection_get_audit_token(m_xpcConnection.get(), &auditToken);
587 bool Connection::kill()
589 if (m_xpcConnection) {
590 xpc_connection_kill(m_xpcConnection.get(), SIGKILL);
597 void Connection::willSendSyncMessage(unsigned flags)
599 #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
600 if ((flags & InformPlatformProcessWillSuspend) && WebCore::AXObjectCache::accessibilityEnabled())
601 _AXUIElementNotifyProcessSuspendStatus(AXSuspendStatusSuspended);
605 void Connection::didReceiveSyncReply(unsigned flags)
607 #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
608 if ((flags & InformPlatformProcessWillSuspend) && WebCore::AXObjectCache::accessibilityEnabled())
609 _AXUIElementNotifyProcessSuspendStatus(AXSuspendStatusRunning);
613 pid_t Connection::remoteProcessID() const
615 if (!m_xpcConnection)
618 return xpc_connection_get_pid(m_xpcConnection.get());