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