Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2010 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      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.
     12  *
     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.
     24  */
     25 
     26 #include "config.h"
     27 #include "Connection.h"
     28 
     29 #include "CoreIPCMessageKinds.h"
     30 #include "MachPort.h"
     31 #include "MachUtilities.h"
     32 #include "RunLoop.h"
     33 #include <mach/vm_map.h>
     34 
     35 using namespace std;
     36 
     37 namespace CoreIPC {
     38 
     39 static const size_t inlineMessageMaxSize = 4096;
     40 
     41 enum {
     42     MessageBodyIsOOL = 1 << 31
     43 };
     44 
     45 void Connection::platformInvalidate()
     46 {
     47     if (!m_isConnected)
     48         return;
     49 
     50     m_isConnected = false;
     51 
     52     ASSERT(m_sendPort);
     53     ASSERT(m_receivePort);
     54 
     55     // Unregister our ports.
     56     m_connectionQueue.unregisterMachPortEventHandler(m_sendPort);
     57     m_sendPort = MACH_PORT_NULL;
     58 
     59     m_connectionQueue.unregisterMachPortEventHandler(m_receivePort);
     60     m_receivePort = MACH_PORT_NULL;
     61 
     62     if (m_exceptionPort) {
     63         m_connectionQueue.unregisterMachPortEventHandler(m_exceptionPort);
     64         m_exceptionPort = MACH_PORT_NULL;
     65     }
     66 }
     67 
     68 void Connection::platformInitialize(Identifier identifier)
     69 {
     70     m_exceptionPort = MACH_PORT_NULL;
     71 
     72     if (m_isServer) {
     73         m_receivePort = identifier;
     74         m_sendPort = MACH_PORT_NULL;
     75     } else {
     76         m_receivePort = MACH_PORT_NULL;
     77         m_sendPort = identifier;
     78     }
     79 }
     80 
     81 bool Connection::open()
     82 {
     83     if (m_isServer) {
     84         ASSERT(m_receivePort);
     85         ASSERT(!m_sendPort);
     86 
     87     } else {
     88         ASSERT(!m_receivePort);
     89         ASSERT(m_sendPort);
     90 
     91         // Create the receive port.
     92         mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &m_receivePort);
     93 
     94         m_isConnected = true;
     95 
     96         // Send the initialize message, which contains a send right for the server to use.
     97         deprecatedSend(CoreIPCMessage::InitializeConnection, 0, MachPort(m_receivePort, MACH_MSG_TYPE_MAKE_SEND));
     98 
     99         // Set the dead name handler for our send port.
    100         initializeDeadNameSource();
    101     }
    102 
    103     // Change the message queue length for the receive port.
    104     setMachPortQueueLength(m_receivePort, MACH_PORT_QLIMIT_LARGE);
    105 
    106     // Register the data available handler.
    107     m_connectionQueue.registerMachPortEventHandler(m_receivePort, WorkQueue::MachPortDataAvailable, WorkItem::create(this, &Connection::receiveSourceEventHandler));
    108 
    109     // If we have an exception port, register the data available handler and send over the port to the other end.
    110     if (m_exceptionPort) {
    111         m_connectionQueue.registerMachPortEventHandler(m_exceptionPort, WorkQueue::MachPortDataAvailable, WorkItem::create(this, &Connection::exceptionSourceEventHandler));
    112 
    113         deprecatedSend(CoreIPCMessage::SetExceptionPort, 0, MachPort(m_exceptionPort, MACH_MSG_TYPE_MAKE_SEND));
    114     }
    115 
    116     return true;
    117 }
    118 
    119 static inline size_t machMessageSize(size_t bodySize, size_t numberOfPortDescriptors = 0, size_t numberOfOOLMemoryDescriptors = 0)
    120 {
    121     size_t size = sizeof(mach_msg_header_t) + bodySize;
    122     if (numberOfPortDescriptors || numberOfOOLMemoryDescriptors) {
    123         size += sizeof(mach_msg_body_t);
    124         if (numberOfPortDescriptors)
    125             size += (numberOfPortDescriptors * sizeof(mach_msg_port_descriptor_t));
    126         if (numberOfOOLMemoryDescriptors)
    127             size += (numberOfOOLMemoryDescriptors * sizeof(mach_msg_ool_ports_descriptor_t));
    128     }
    129     return round_msg(size);
    130 }
    131 
    132 bool Connection::platformCanSendOutgoingMessages() const
    133 {
    134     return true;
    135 }
    136 
    137 bool Connection::sendOutgoingMessage(MessageID messageID, PassOwnPtr<ArgumentEncoder> arguments)
    138 {
    139     Vector<Attachment> attachments = arguments->releaseAttachments();
    140 
    141     size_t numberOfPortDescriptors = 0;
    142     size_t numberOfOOLMemoryDescriptors = 0;
    143     for (size_t i = 0; i < attachments.size(); ++i) {
    144         Attachment::Type type = attachments[i].type();
    145         if (type == Attachment::MachPortType)
    146             numberOfPortDescriptors++;
    147         else if (type == Attachment::MachOOLMemoryType)
    148             numberOfOOLMemoryDescriptors++;
    149     }
    150 
    151     size_t messageSize = machMessageSize(arguments->bufferSize(), numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
    152     char buffer[inlineMessageMaxSize];
    153 
    154     bool messageBodyIsOOL = false;
    155     if (messageSize > sizeof(buffer)) {
    156         messageBodyIsOOL = true;
    157 
    158         attachments.append(Attachment(arguments->buffer(), arguments->bufferSize(), MACH_MSG_VIRTUAL_COPY, false));
    159         numberOfOOLMemoryDescriptors++;
    160         messageSize = machMessageSize(0, numberOfPortDescriptors, numberOfOOLMemoryDescriptors);
    161     }
    162 
    163     bool isComplex = (numberOfPortDescriptors + numberOfOOLMemoryDescriptors > 0);
    164 
    165     mach_msg_header_t* header = reinterpret_cast<mach_msg_header_t*>(&buffer);
    166     header->msgh_bits = isComplex ? MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND | MACH_MSGH_BITS_COMPLEX, 0) : MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
    167     header->msgh_size = messageSize;
    168     header->msgh_remote_port = m_sendPort;
    169     header->msgh_local_port = MACH_PORT_NULL;
    170     header->msgh_id = messageID.toInt();
    171     if (messageBodyIsOOL)
    172         header->msgh_id |= MessageBodyIsOOL;
    173 
    174     uint8_t* messageData;
    175 
    176     if (isComplex) {
    177         mach_msg_body_t* body = reinterpret_cast<mach_msg_body_t*>(header + 1);
    178         body->msgh_descriptor_count = numberOfPortDescriptors + numberOfOOLMemoryDescriptors;
    179 
    180         uint8_t* descriptorData = reinterpret_cast<uint8_t*>(body + 1);
    181         for (size_t i = 0; i < attachments.size(); ++i) {
    182             Attachment attachment = attachments[i];
    183 
    184             mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData);
    185             switch (attachment.type()) {
    186             case Attachment::MachPortType:
    187                 descriptor->port.name = attachment.port();
    188                 descriptor->port.disposition = attachment.disposition();
    189                 descriptor->port.type = MACH_MSG_PORT_DESCRIPTOR;
    190 
    191                 descriptorData += sizeof(mach_msg_port_descriptor_t);
    192                 break;
    193             case Attachment::MachOOLMemoryType:
    194                 descriptor->out_of_line.address = attachment.address();
    195                 descriptor->out_of_line.size = attachment.size();
    196                 descriptor->out_of_line.copy = attachment.copyOptions();
    197                 descriptor->out_of_line.deallocate = attachment.deallocate();
    198                 descriptor->out_of_line.type = MACH_MSG_OOL_DESCRIPTOR;
    199 
    200                 descriptorData += sizeof(mach_msg_ool_descriptor_t);
    201                 break;
    202             default:
    203                 ASSERT_NOT_REACHED();
    204             }
    205         }
    206 
    207         messageData = descriptorData;
    208     } else
    209         messageData = (uint8_t*)(header + 1);
    210 
    211     // Copy the data if it is not being sent out-of-line.
    212     if (!messageBodyIsOOL)
    213         memcpy(messageData, arguments->buffer(), arguments->bufferSize());
    214 
    215     ASSERT(m_sendPort);
    216 
    217     // Send the message.
    218     kern_return_t kr = mach_msg(header, MACH_SEND_MSG, messageSize, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    219     if (kr != KERN_SUCCESS) {
    220         // FIXME: What should we do here?
    221     }
    222 
    223     return true;
    224 }
    225 
    226 void Connection::initializeDeadNameSource()
    227 {
    228     m_connectionQueue.registerMachPortEventHandler(m_sendPort, WorkQueue::MachPortDeadNameNotification, WorkItem::create(this, &Connection::connectionDidClose));
    229 }
    230 
    231 static PassOwnPtr<ArgumentDecoder> createArgumentDecoder(mach_msg_header_t* header)
    232 {
    233     if (!(header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
    234         // We have a simple message.
    235         size_t bodySize = header->msgh_size - sizeof(mach_msg_header_t);
    236         uint8_t* body = reinterpret_cast<uint8_t*>(header + 1);
    237 
    238         return adoptPtr(new ArgumentDecoder(body, bodySize));
    239     }
    240 
    241     bool messageBodyIsOOL = header->msgh_id & MessageBodyIsOOL;
    242 
    243     mach_msg_body_t* body = reinterpret_cast<mach_msg_body_t*>(header + 1);
    244     mach_msg_size_t numDescriptors = body->msgh_descriptor_count;
    245     ASSERT(numDescriptors);
    246 
    247     // Build attachment list
    248     Deque<Attachment> attachments;
    249     uint8_t* descriptorData = reinterpret_cast<uint8_t*>(body + 1);
    250 
    251     // If the message body was sent out-of-line, don't treat the last descriptor
    252     // as an attachment, since it is really the message body.
    253     if (messageBodyIsOOL)
    254         --numDescriptors;
    255 
    256     for (mach_msg_size_t i = 0; i < numDescriptors; ++i) {
    257         mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData);
    258 
    259         switch (descriptor->type.type) {
    260         case MACH_MSG_PORT_DESCRIPTOR:
    261             attachments.append(Attachment(descriptor->port.name, descriptor->port.disposition));
    262             descriptorData += sizeof(mach_msg_port_descriptor_t);
    263             break;
    264         case MACH_MSG_OOL_DESCRIPTOR:
    265             attachments.append(Attachment(descriptor->out_of_line.address, descriptor->out_of_line.size,
    266                                           descriptor->out_of_line.copy, descriptor->out_of_line.deallocate));
    267             descriptorData += sizeof(mach_msg_ool_descriptor_t);
    268             break;
    269         default:
    270             ASSERT(false && "Unhandled descriptor type");
    271         }
    272     }
    273 
    274     if (messageBodyIsOOL) {
    275         mach_msg_descriptor_t* descriptor = reinterpret_cast<mach_msg_descriptor_t*>(descriptorData);
    276         ASSERT(descriptor->type.type == MACH_MSG_OOL_DESCRIPTOR);
    277         Attachment messageBodyAttachment(descriptor->out_of_line.address, descriptor->out_of_line.size,
    278                                          descriptor->out_of_line.copy, descriptor->out_of_line.deallocate);
    279 
    280         uint8_t* messageBody = static_cast<uint8_t*>(messageBodyAttachment.address());
    281         size_t messageBodySize = messageBodyAttachment.size();
    282 
    283         ArgumentDecoder* argumentDecoder;
    284 
    285         if (attachments.isEmpty())
    286             argumentDecoder = new ArgumentDecoder(messageBody, messageBodySize);
    287         else
    288             argumentDecoder = new ArgumentDecoder(messageBody, messageBodySize, attachments);
    289 
    290         vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(messageBodyAttachment.address()), messageBodyAttachment.size());
    291 
    292         return adoptPtr(argumentDecoder);
    293     }
    294 
    295     uint8_t* messageBody = descriptorData;
    296     size_t messageBodySize = header->msgh_size - (descriptorData - reinterpret_cast<uint8_t*>(header));
    297 
    298     return adoptPtr(new ArgumentDecoder(messageBody, messageBodySize, attachments));
    299 }
    300 
    301 // The receive buffer size should always include the maximum trailer size.
    302 static const size_t receiveBufferSize = inlineMessageMaxSize + MAX_TRAILER_SIZE;
    303 typedef Vector<char, receiveBufferSize> ReceiveBuffer;
    304 
    305 static mach_msg_header_t* readFromMachPort(mach_port_t machPort, ReceiveBuffer& buffer)
    306 {
    307     buffer.resize(receiveBufferSize);
    308 
    309     mach_msg_header_t* header = reinterpret_cast<mach_msg_header_t*>(buffer.data());
    310     kern_return_t kr = mach_msg(header, MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT, 0, buffer.size(), machPort, 0, MACH_PORT_NULL);
    311     if (kr == MACH_RCV_TIMED_OUT)
    312         return 0;
    313 
    314     if (kr == MACH_RCV_TOO_LARGE) {
    315         // The message was too large, resize the buffer and try again.
    316         buffer.resize(header->msgh_size + MAX_TRAILER_SIZE);
    317         header = reinterpret_cast<mach_msg_header_t*>(buffer.data());
    318 
    319         kr = mach_msg(header, MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_TIMEOUT, 0, buffer.size(), machPort, 0, MACH_PORT_NULL);
    320         ASSERT(kr != MACH_RCV_TOO_LARGE);
    321     }
    322 
    323     if (kr != MACH_MSG_SUCCESS) {
    324         ASSERT_NOT_REACHED();
    325         return 0;
    326     }
    327 
    328     return header;
    329 }
    330 
    331 void Connection::receiveSourceEventHandler()
    332 {
    333     ReceiveBuffer buffer;
    334 
    335     mach_msg_header_t* header = readFromMachPort(m_receivePort, buffer);
    336     if (!header)
    337         return;
    338 
    339     MessageID messageID = MessageID::fromInt(header->msgh_id);
    340     OwnPtr<ArgumentDecoder> arguments = createArgumentDecoder(header);
    341     ASSERT(arguments);
    342 
    343     if (messageID == MessageID(CoreIPCMessage::InitializeConnection)) {
    344         ASSERT(m_isServer);
    345         ASSERT(!m_isConnected);
    346         ASSERT(!m_sendPort);
    347 
    348         MachPort port;
    349         if (!arguments->decode(port)) {
    350             // FIXME: Disconnect.
    351             return;
    352         }
    353 
    354         m_sendPort = port.port();
    355 
    356         // Set the dead name source if needed.
    357         if (m_sendPort)
    358             initializeDeadNameSource();
    359 
    360         m_isConnected = true;
    361 
    362         // Send any pending outgoing messages.
    363         sendOutgoingMessages();
    364 
    365         return;
    366     }
    367 
    368     if (messageID == MessageID(CoreIPCMessage::SetExceptionPort)) {
    369         MachPort exceptionPort;
    370         if (!arguments->decode(exceptionPort))
    371             return;
    372 
    373         setMachExceptionPort(exceptionPort.port());
    374         return;
    375     }
    376 
    377     processIncomingMessage(messageID, arguments.release());
    378 }
    379 
    380 void Connection::exceptionSourceEventHandler()
    381 {
    382     ReceiveBuffer buffer;
    383 
    384     mach_msg_header_t* header = readFromMachPort(m_exceptionPort, buffer);
    385     if (!header)
    386         return;
    387 
    388     // We've read the exception message. Now send it on to the real exception port.
    389 
    390     // The remote port should have a send once right.
    391     ASSERT(MACH_MSGH_BITS_REMOTE(header->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE);
    392 
    393     // Now get the real exception port.
    394     mach_port_t exceptionPort = machExceptionPort();
    395 
    396     // First, get the complex bit from the source message.
    397     mach_msg_bits_t messageBits = header->msgh_bits & MACH_MSGH_BITS_COMPLEX;
    398     messageBits |= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
    399 
    400     header->msgh_bits = messageBits;
    401     header->msgh_local_port = header->msgh_remote_port;
    402     header->msgh_remote_port = exceptionPort;
    403 
    404     // Now send along the message.
    405     kern_return_t kr = mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    406     if (kr != KERN_SUCCESS) {
    407         LOG_ERROR("Failed to send message to real exception port, error %x", kr);
    408         ASSERT_NOT_REACHED();
    409     }
    410 
    411     connectionDidClose();
    412 }
    413 
    414 void Connection::setShouldCloseConnectionOnMachExceptions()
    415 {
    416     ASSERT(m_exceptionPort == MACH_PORT_NULL);
    417 
    418     if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &m_exceptionPort) != KERN_SUCCESS)
    419         ASSERT_NOT_REACHED();
    420 
    421     if (mach_port_insert_right(mach_task_self(), m_exceptionPort, m_exceptionPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)
    422         ASSERT_NOT_REACHED();
    423 }
    424 
    425 } // namespace CoreIPC
    426