• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
platformInvalidate()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 
platformInitialize(Identifier identifier)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 
open()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 
machMessageSize(size_t bodySize,size_t numberOfPortDescriptors=0,size_t numberOfOOLMemoryDescriptors=0)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 
platformCanSendOutgoingMessages() const132 bool Connection::platformCanSendOutgoingMessages() const
133 {
134     return true;
135 }
136 
sendOutgoingMessage(MessageID messageID,PassOwnPtr<ArgumentEncoder> arguments)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 
initializeDeadNameSource()226 void Connection::initializeDeadNameSource()
227 {
228     m_connectionQueue.registerMachPortEventHandler(m_sendPort, WorkQueue::MachPortDeadNameNotification, WorkItem::create(this, &Connection::connectionDidClose));
229 }
230 
createArgumentDecoder(mach_msg_header_t * header)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 
readFromMachPort(mach_port_t machPort,ReceiveBuffer & buffer)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 
receiveSourceEventHandler()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 
exceptionSourceEventHandler()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 
setShouldCloseConnectionOnMachExceptions()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