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