]> git.saurik.com Git - apple/cf.git/blob - CFMessagePort.c
569b6a0ed23bb03f1397d553fd3da2569fe1c99d
[apple/cf.git] / CFMessagePort.c
1 /*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFMessagePort.c
25 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
27 */
28
29 #include <CoreFoundation/CFMessagePort.h>
30 #include <CoreFoundation/CFRunLoop.h>
31 #include <CoreFoundation/CFMachPort.h>
32 #include <CoreFoundation/CFDictionary.h>
33 #include <CoreFoundation/CFByteOrder.h>
34 #include <limits.h>
35 #include "CFInternal.h"
36 #include <mach/mach.h>
37 #include <mach/message.h>
38 #include <mach/mach_error.h>
39 #include <bootstrap_priv.h>
40 #include <math.h>
41 #include <mach/mach_time.h>
42 #include <dlfcn.h>
43 #include <dispatch/dispatch.h>
44
45 extern pid_t getpid(void);
46
47
48 #define __kCFMessagePortMaxNameLengthMax 255
49
50 #if defined(BOOTSTRAP_MAX_NAME_LEN)
51 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
52 #else
53 #define __kCFMessagePortMaxNameLength 128
54 #endif
55
56 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
57 #undef __kCFMessagePortMaxNameLength
58 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
59 #endif
60
61 #define __CFMessagePortMaxDataSize 0x60000000L
62
63
64 DISPATCH_HELPER_FUNCTIONS(mport, CFMessagePort)
65
66
67 static CFSpinLock_t __CFAllMessagePortsLock = CFSpinLockInit;
68 static CFMutableDictionaryRef __CFAllLocalMessagePorts = NULL;
69 static CFMutableDictionaryRef __CFAllRemoteMessagePorts = NULL;
70
71 struct __CFMessagePort {
72 CFRuntimeBase _base;
73 CFSpinLock_t _lock;
74 CFStringRef _name;
75 CFMachPortRef _port; /* immutable; invalidated */
76 CFMutableDictionaryRef _replies;
77 int32_t _convCounter;
78 int32_t _perPID; /* zero if not per-pid, else pid */
79 CFMachPortRef _replyPort; /* only used by remote port; immutable once created; invalidated */
80 CFRunLoopSourceRef _source; /* only used by local port; immutable once created; invalidated */
81 dispatch_source_t _dispatchSource; /* only used by local port; invalidated */
82 dispatch_queue_t _dispatchQ; /* only used by local port */
83 CFMessagePortInvalidationCallBack _icallout;
84 CFMessagePortCallBack _callout; /* only used by local port; immutable */
85 CFMessagePortContext _context; /* not part of remote port; immutable; invalidated */
86 };
87
88 /* Bit 0 in the base reserved bits is used for invalid state */
89 /* Bit 1 of the base reserved bits is used for has-extra-port-refs state */
90 /* Bit 2 of the base reserved bits is used for is-remote state */
91 /* Bit 3 in the base reserved bits is used for is-deallocing state */
92
93 CF_INLINE Boolean __CFMessagePortIsValid(CFMessagePortRef ms) {
94 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0);
95 }
96
97 CF_INLINE void __CFMessagePortSetValid(CFMessagePortRef ms) {
98 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0, 1);
99 }
100
101 CF_INLINE void __CFMessagePortUnsetValid(CFMessagePortRef ms) {
102 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0, 0);
103 }
104
105 CF_INLINE Boolean __CFMessagePortExtraMachRef(CFMessagePortRef ms) {
106 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1);
107 }
108
109 CF_INLINE void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms) {
110 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
111 }
112
113 CF_INLINE void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms) {
114 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1, 0);
115 }
116
117 CF_INLINE Boolean __CFMessagePortIsRemote(CFMessagePortRef ms) {
118 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2);
119 }
120
121 CF_INLINE void __CFMessagePortSetRemote(CFMessagePortRef ms) {
122 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2, 1);
123 }
124
125 CF_INLINE void __CFMessagePortUnsetRemote(CFMessagePortRef ms) {
126 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2, 0);
127 }
128
129 CF_INLINE Boolean __CFMessagePortIsDeallocing(CFMessagePortRef ms) {
130 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 3, 3);
131 }
132
133 CF_INLINE void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms) {
134 __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
135 }
136
137 CF_INLINE void __CFMessagePortLock(CFMessagePortRef ms) {
138 __CFSpinLock(&(ms->_lock));
139 }
140
141 CF_INLINE void __CFMessagePortUnlock(CFMessagePortRef ms) {
142 __CFSpinUnlock(&(ms->_lock));
143 }
144
145 // Just a heuristic
146 #define __CFMessagePortMaxInlineBytes 4096*10
147
148 struct __CFMessagePortMachMessage0 {
149 mach_msg_base_t base;
150 int32_t magic;
151 int32_t msgid;
152 int32_t byteslen;
153 uint8_t bytes[0];
154 };
155
156 struct __CFMessagePortMachMessage1 {
157 mach_msg_base_t base;
158 mach_msg_ool_descriptor_t ool;
159 int32_t magic;
160 int32_t msgid;
161 int32_t byteslen;
162 };
163
164 #define MAGIC 0xF1F2F3F4
165
166 #define MSGP0_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage0 *)msgp)->ident
167 #define MSGP1_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage1 *)msgp)->ident
168 #define MSGP_GET(msgp, ident) \
169 ((((mach_msg_base_t *)msgp)->body.msgh_descriptor_count) ? MSGP1_FIELD(msgp, ident) : MSGP0_FIELD(msgp, ident))
170
171 static mach_msg_base_t *__CFMessagePortCreateMessage(bool reply, mach_port_t port, mach_port_t replyPort, int32_t convid, int32_t msgid, const uint8_t *bytes, int32_t byteslen) {
172 if (__CFMessagePortMaxDataSize < byteslen) return NULL;
173 int32_t rounded_byteslen = ((byteslen + 3) & ~0x3);
174 if (rounded_byteslen <= __CFMessagePortMaxInlineBytes) {
175 int32_t size = sizeof(struct __CFMessagePortMachMessage0) + rounded_byteslen;
176 struct __CFMessagePortMachMessage0 *msg = CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0);
177 if (!msg) return NULL;
178 memset(msg, 0, size);
179 msg->base.header.msgh_id = convid;
180 msg->base.header.msgh_size = size;
181 msg->base.header.msgh_remote_port = port;
182 msg->base.header.msgh_local_port = replyPort;
183 msg->base.header.msgh_reserved = 0;
184 msg->base.header.msgh_bits = MACH_MSGH_BITS((reply ? MACH_MSG_TYPE_MOVE_SEND_ONCE : MACH_MSG_TYPE_COPY_SEND), (MACH_PORT_NULL != replyPort ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0));
185 msg->base.body.msgh_descriptor_count = 0;
186 msg->magic = MAGIC;
187 msg->msgid = CFSwapInt32HostToLittle(msgid);
188 msg->byteslen = CFSwapInt32HostToLittle(byteslen);
189 if (NULL != bytes && 0 < byteslen) {
190 memmove(msg->bytes, bytes, byteslen);
191 }
192 return (mach_msg_base_t *)msg;
193 } else {
194 int32_t size = sizeof(struct __CFMessagePortMachMessage1);
195 struct __CFMessagePortMachMessage1 *msg = CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0);
196 if (!msg) return NULL;
197 memset(msg, 0, size);
198 msg->base.header.msgh_id = convid;
199 msg->base.header.msgh_size = size;
200 msg->base.header.msgh_remote_port = port;
201 msg->base.header.msgh_local_port = replyPort;
202 msg->base.header.msgh_reserved = 0;
203 msg->base.header.msgh_bits = MACH_MSGH_BITS((reply ? MACH_MSG_TYPE_MOVE_SEND_ONCE : MACH_MSG_TYPE_COPY_SEND), (MACH_PORT_NULL != replyPort ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0));
204 msg->base.header.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
205 msg->base.body.msgh_descriptor_count = 1;
206 msg->magic = MAGIC;
207 msg->msgid = CFSwapInt32HostToLittle(msgid);
208 msg->byteslen = CFSwapInt32HostToLittle(byteslen);
209 msg->ool.deallocate = false;
210 msg->ool.copy = MACH_MSG_VIRTUAL_COPY;
211 msg->ool.address = (void *)bytes;
212 msg->ool.size = byteslen;
213 msg->ool.type = MACH_MSG_OOL_DESCRIPTOR;
214 return (mach_msg_base_t *)msg;
215 }
216 }
217
218 static CFStringRef __CFMessagePortCopyDescription(CFTypeRef cf) {
219 CFMessagePortRef ms = (CFMessagePortRef)cf;
220 CFStringRef result;
221 const char *locked;
222 CFStringRef contextDesc = NULL;
223 locked = ms->_lock ? "Yes" : "No";
224 if (__CFMessagePortIsRemote(ms)) {
225 result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name);
226 } else {
227 if (NULL != ms->_context.info && NULL != ms->_context.copyDescription) {
228 contextDesc = ms->_context.copyDescription(ms->_context.info);
229 }
230 if (NULL == contextDesc) {
231 contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMessagePort context %p>"), ms->_context.info);
232 }
233 void *addr = ms->_callout;
234 Dl_info info;
235 const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";
236 result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name, ms->_source, name, addr, (NULL != contextDesc ? contextDesc : CFSTR("<no description>")));
237 }
238 if (NULL != contextDesc) {
239 CFRelease(contextDesc);
240 }
241 return result;
242 }
243
244 static void __CFMessagePortDeallocate(CFTypeRef cf) {
245 CFMessagePortRef ms = (CFMessagePortRef)cf;
246 __CFMessagePortSetIsDeallocing(ms);
247 CFMessagePortInvalidate(ms);
248 // Delay cleanup of _replies until here so that invalidation during
249 // SendRequest does not cause _replies to disappear out from under that function.
250 if (NULL != ms->_replies) {
251 CFRelease(ms->_replies);
252 }
253 if (NULL != ms->_name) {
254 CFRelease(ms->_name);
255 }
256 if (NULL != ms->_port) {
257 if (__CFMessagePortExtraMachRef(ms)) {
258 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_SEND, -1);
259 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_RECEIVE, -1);
260 }
261 CFMachPortInvalidate(ms->_port);
262 CFRelease(ms->_port);
263 }
264
265 // A remote message port for a local message port in the same process will get the
266 // same mach port, and the remote port will keep the mach port from being torn down,
267 // thus keeping the remote port from getting any sort of death notification and
268 // auto-invalidating; so we manually implement the 'auto-invalidation' here by
269 // tickling each remote port to check its state after any message port is destroyed,
270 // but most importantly after local message ports are destroyed.
271 __CFSpinLock(&__CFAllMessagePortsLock);
272 CFMessagePortRef *remotePorts = NULL;
273 CFIndex cnt = 0;
274 if (NULL != __CFAllRemoteMessagePorts) {
275 cnt = CFDictionaryGetCount(__CFAllRemoteMessagePorts);
276 remotePorts = CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(CFMessagePortRef), __kCFAllocatorGCScannedMemory);
277 CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts, NULL, (const void **)remotePorts);
278 for (CFIndex idx = 0; idx < cnt; idx++) {
279 CFRetain(remotePorts[idx]);
280 }
281 }
282 __CFSpinUnlock(&__CFAllMessagePortsLock);
283 if (remotePorts) {
284 for (CFIndex idx = 0; idx < cnt; idx++) {
285 // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid
286 CFMessagePortIsValid(remotePorts[idx]);
287 CFRelease(remotePorts[idx]);
288 }
289 CFAllocatorDeallocate(kCFAllocatorSystemDefault, remotePorts);
290 }
291 }
292
293 static CFTypeID __kCFMessagePortTypeID = _kCFRuntimeNotATypeID;
294
295 static const CFRuntimeClass __CFMessagePortClass = {
296 0,
297 "CFMessagePort",
298 NULL, // init
299 NULL, // copy
300 __CFMessagePortDeallocate,
301 NULL,
302 NULL,
303 NULL, //
304 __CFMessagePortCopyDescription
305 };
306
307 __private_extern__ void __CFMessagePortInitialize(void) {
308 __kCFMessagePortTypeID = _CFRuntimeRegisterClass(&__CFMessagePortClass);
309 }
310
311 CFTypeID CFMessagePortGetTypeID(void) {
312 return __kCFMessagePortTypeID;
313 }
314
315 static CFStringRef __CFMessagePortSanitizeStringName(CFStringRef name, uint8_t **utfnamep, CFIndex *utfnamelenp) {
316 uint8_t *utfname;
317 CFIndex utflen;
318 CFStringRef result;
319 utfname = CFAllocatorAllocate(kCFAllocatorSystemDefault, __kCFMessagePortMaxNameLength + 1, 0);
320 CFStringGetBytes(name, CFRangeMake(0, CFStringGetLength(name)), kCFStringEncodingUTF8, 0, false, utfname, __kCFMessagePortMaxNameLength, &utflen);
321 utfname[utflen] = '\0';
322 /* A new string is created, because the original string may have been
323 truncated to the max length, and we want the string name to definitely
324 match the raw UTF-8 chunk that has been created. Also, this is useful
325 to get a constant string in case the original name string was mutable. */
326 result = CFStringCreateWithBytes(kCFAllocatorSystemDefault, utfname, utflen, kCFStringEncodingUTF8, false);
327 if (NULL != utfnamep) {
328 *utfnamep = utfname;
329 } else {
330 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
331 }
332 if (NULL != utfnamelenp) {
333 *utfnamelenp = utflen;
334 }
335 return result;
336 }
337
338 static void __CFMessagePortDummyCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) {
339 // not supposed to be implemented
340 }
341
342 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port, void *info) {
343 // info has been setup as the CFMessagePort owning the CFMachPort
344 if (info) CFMessagePortInvalidate(info);
345 }
346
347 static CFMessagePortRef __CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo, Boolean perPID) {
348 CFMessagePortRef memory;
349 uint8_t *utfname = NULL;
350
351 if (shouldFreeInfo) *shouldFreeInfo = true;
352 if (NULL != name) {
353 name = __CFMessagePortSanitizeStringName(name, &utfname, NULL);
354 }
355 __CFSpinLock(&__CFAllMessagePortsLock);
356 if (!perPID && NULL != name) {
357 CFMessagePortRef existing;
358 if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) {
359 CFRetain(existing);
360 __CFSpinUnlock(&__CFAllMessagePortsLock);
361 CFRelease(name);
362 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
363 return (CFMessagePortRef)(existing);
364 }
365 }
366 __CFSpinUnlock(&__CFAllMessagePortsLock);
367 CFIndex size = sizeof(struct __CFMessagePort) - sizeof(CFRuntimeBase);
368 memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL);
369 if (NULL == memory) {
370 if (NULL != name) {
371 CFRelease(name);
372 }
373 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
374 return NULL;
375 }
376 __CFMessagePortUnsetValid(memory);
377 __CFMessagePortUnsetExtraMachRef(memory);
378 __CFMessagePortUnsetRemote(memory);
379 memory->_lock = CFSpinLockInit;
380 memory->_name = name;
381 memory->_port = NULL;
382 memory->_replies = NULL;
383 memory->_convCounter = 0;
384 memory->_perPID = perPID ? getpid() : 0; // actual value not terribly useful for local ports
385 memory->_replyPort = NULL;
386 memory->_source = NULL;
387 memory->_dispatchSource = NULL;
388 memory->_dispatchQ = NULL;
389 memory->_icallout = NULL;
390 memory->_callout = callout;
391 memory->_context.info = NULL;
392 memory->_context.retain = NULL;
393 memory->_context.release = NULL;
394 memory->_context.copyDescription = NULL;
395
396 if (NULL != name) {
397 CFMachPortRef native = NULL;
398 kern_return_t ret;
399 mach_port_t bs, mp;
400 task_get_bootstrap_port(mach_task_self(), &bs);
401 if (!perPID) {
402 ret = bootstrap_check_in(bs, (char *)utfname, &mp); /* If we're started by launchd or the old mach_init */
403 if (ret == KERN_SUCCESS) {
404 ret = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND);
405 if (KERN_SUCCESS == ret) {
406 CFMachPortContext ctx = {0, memory, NULL, NULL, NULL};
407 native = CFMachPortCreateWithPort(allocator, mp, __CFMessagePortDummyCallback, &ctx, NULL);
408 __CFMessagePortSetExtraMachRef(memory);
409 } else {
410 CFLog(kCFLogLevelDebug, CFSTR("*** CFMessagePort: mach_port_insert_member() after bootstrap_check_in(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'"), ret, ret, bootstrap_strerror(ret), mp, utfname);
411 mach_port_destroy(mach_task_self(), mp);
412 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
413 // name is released by deallocation
414 CFRelease(memory);
415 return NULL;
416 }
417 }
418 }
419 if (!native) {
420 CFMachPortContext ctx = {0, memory, NULL, NULL, NULL};
421 native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL);
422 if (!native) {
423 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
424 // name is released by deallocation
425 CFRelease(memory);
426 return NULL;
427 }
428 mp = CFMachPortGetPort(native);
429 ret = bootstrap_register2(bs, (char *)utfname, mp, perPID ? BOOTSTRAP_PER_PID_SERVICE : 0);
430 if (ret != KERN_SUCCESS) {
431 CFLog(kCFLogLevelDebug, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret, ret, bootstrap_strerror(ret), mp, utfname);
432 CFMachPortInvalidate(native);
433 CFRelease(native);
434 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
435 // name is released by deallocation
436 CFRelease(memory);
437 return NULL;
438 }
439 }
440 CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack);
441 memory->_port = native;
442 }
443
444 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
445 __CFMessagePortSetValid(memory);
446 if (NULL != context) {
447 memmove(&memory->_context, context, sizeof(CFMessagePortContext));
448 memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
449 }
450 __CFSpinLock(&__CFAllMessagePortsLock);
451 if (!perPID && NULL != name) {
452 CFMessagePortRef existing;
453 if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) {
454 CFRetain(existing);
455 __CFSpinUnlock(&__CFAllMessagePortsLock);
456 CFRelease(memory);
457 return (CFMessagePortRef)(existing);
458 }
459 if (NULL == __CFAllLocalMessagePorts) {
460 __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
461 }
462 CFDictionaryAddValue(__CFAllLocalMessagePorts, name, memory);
463 }
464 __CFSpinUnlock(&__CFAllMessagePortsLock);
465 if (shouldFreeInfo) *shouldFreeInfo = false;
466 return memory;
467 }
468
469 CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) {
470 return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, false);
471 }
472
473 CFMessagePortRef CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) {
474 return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, true);
475 }
476
477 static CFMessagePortRef __CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name, Boolean perPID, CFIndex pid) {
478 CFMessagePortRef memory;
479 CFMachPortRef native;
480 CFMachPortContext ctx;
481 uint8_t *utfname = NULL;
482 CFIndex size;
483 mach_port_t bp, port;
484 kern_return_t ret;
485
486 name = __CFMessagePortSanitizeStringName(name, &utfname, NULL);
487 if (NULL == name) {
488 return NULL;
489 }
490 __CFSpinLock(&__CFAllMessagePortsLock);
491 if (!perPID && NULL != name) {
492 CFMessagePortRef existing;
493 if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) {
494 CFRetain(existing);
495 __CFSpinUnlock(&__CFAllMessagePortsLock);
496 CFRelease(name);
497 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
498 return (CFMessagePortRef)(existing);
499 }
500 }
501 __CFSpinUnlock(&__CFAllMessagePortsLock);
502 size = sizeof(struct __CFMessagePort) - sizeof(CFMessagePortContext) - sizeof(CFRuntimeBase);
503 memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL);
504 if (NULL == memory) {
505 if (NULL != name) {
506 CFRelease(name);
507 }
508 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
509 return NULL;
510 }
511 __CFMessagePortUnsetValid(memory);
512 __CFMessagePortUnsetExtraMachRef(memory);
513 __CFMessagePortSetRemote(memory);
514 memory->_lock = CFSpinLockInit;
515 memory->_name = name;
516 memory->_port = NULL;
517 memory->_replies = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
518 memory->_convCounter = 0;
519 memory->_perPID = perPID ? pid : 0;
520 memory->_replyPort = NULL;
521 memory->_source = NULL;
522 memory->_dispatchSource = NULL;
523 memory->_dispatchQ = NULL;
524 memory->_icallout = NULL;
525 memory->_callout = NULL;
526 ctx.version = 0;
527 ctx.info = memory;
528 ctx.retain = NULL;
529 ctx.release = NULL;
530 ctx.copyDescription = NULL;
531 task_get_bootstrap_port(mach_task_self(), &bp);
532 ret = bootstrap_look_up2(bp, (char *)utfname, &port, perPID ? (pid_t)pid : 0, perPID ? BOOTSTRAP_PER_PID_SERVICE : 0);
533 native = (KERN_SUCCESS == ret) ? CFMachPortCreateWithPort(allocator, port, __CFMessagePortDummyCallback, &ctx, NULL) : NULL;
534 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
535 if (NULL == native) {
536 // name is released by deallocation
537 CFRelease(memory);
538 return NULL;
539 }
540 memory->_port = native;
541 __CFMessagePortSetValid(memory);
542 __CFSpinLock(&__CFAllMessagePortsLock);
543 if (!perPID && NULL != name) {
544 CFMessagePortRef existing;
545 if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) {
546 CFRetain(existing);
547 __CFSpinUnlock(&__CFAllMessagePortsLock);
548 CFRelease(memory);
549 return (CFMessagePortRef)(existing);
550 }
551 if (NULL == __CFAllRemoteMessagePorts) {
552 __CFAllRemoteMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
553 }
554 CFDictionaryAddValue(__CFAllRemoteMessagePorts, name, memory);
555 }
556 __CFSpinUnlock(&__CFAllMessagePortsLock);
557 CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack);
558 // that set-invalidation-callback might have called back into us
559 // if the CFMachPort is already bad, but that was a no-op since
560 // there was no callback setup at the (previous) time the CFMachPort
561 // went invalid; so check for validity manually and react
562 if (!CFMachPortIsValid(native)) {
563 CFRelease(memory); // does the invalidate
564 return NULL;
565 }
566 return (CFMessagePortRef)memory;
567 }
568
569 CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) {
570 return __CFMessagePortCreateRemote(allocator, name, false, 0);
571 }
572
573 CFMessagePortRef CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator, CFStringRef name, CFIndex pid) {
574 return __CFMessagePortCreateRemote(allocator, name, true, pid);
575 }
576
577 Boolean CFMessagePortIsRemote(CFMessagePortRef ms) {
578 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
579 return __CFMessagePortIsRemote(ms);
580 }
581
582 CFStringRef CFMessagePortGetName(CFMessagePortRef ms) {
583 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
584 return ms->_name;
585 }
586
587 Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef name) {
588 CFAllocatorRef allocator = CFGetAllocator(ms);
589 uint8_t *utfname = NULL;
590
591 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
592 if (ms->_perPID || __CFMessagePortIsRemote(ms)) return false;
593 name = __CFMessagePortSanitizeStringName(name, &utfname, NULL);
594 if (NULL == name) {
595 return false;
596 }
597 __CFSpinLock(&__CFAllMessagePortsLock);
598 if (NULL != name) {
599 CFMessagePortRef existing;
600 if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) {
601 __CFSpinUnlock(&__CFAllMessagePortsLock);
602 CFRelease(name);
603 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
604 return false;
605 }
606 }
607 __CFSpinUnlock(&__CFAllMessagePortsLock);
608
609 if (NULL != name && (NULL == ms->_name || !CFEqual(ms->_name, name))) {
610 CFMachPortRef oldPort = ms->_port;
611 CFMachPortRef native = NULL;
612 kern_return_t ret;
613 mach_port_t bs, mp;
614 task_get_bootstrap_port(mach_task_self(), &bs);
615 ret = bootstrap_check_in(bs, (char *)utfname, &mp); /* If we're started by launchd or the old mach_init */
616 if (ret == KERN_SUCCESS) {
617 ret = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND);
618 if (KERN_SUCCESS == ret) {
619 CFMachPortContext ctx = {0, ms, NULL, NULL, NULL};
620 native = CFMachPortCreateWithPort(allocator, mp, __CFMessagePortDummyCallback, &ctx, NULL);
621 __CFMessagePortSetExtraMachRef(ms);
622 } else {
623 mach_port_destroy(mach_task_self(), mp);
624 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
625 CFRelease(name);
626 return false;
627 }
628 }
629 if (!native) {
630 CFMachPortContext ctx = {0, ms, NULL, NULL, NULL};
631 native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL);
632 if (!native) {
633 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
634 CFRelease(name);
635 return false;
636 }
637 mp = CFMachPortGetPort(native);
638 ret = bootstrap_register2(bs, (char *)utfname, mp, 0);
639 if (ret != KERN_SUCCESS) {
640 CFLog(kCFLogLevelDebug, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret, ret, bootstrap_strerror(ret), mp, utfname);
641 CFMachPortInvalidate(native);
642 CFRelease(native);
643 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
644 CFRelease(name);
645 return false;
646 }
647 }
648 CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack);
649 ms->_port = native;
650 if (NULL != oldPort && oldPort != native) {
651 if (__CFMessagePortExtraMachRef(ms)) {
652 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort), MACH_PORT_RIGHT_SEND, -1);
653 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort), MACH_PORT_RIGHT_RECEIVE, -1);
654 }
655 CFMachPortInvalidate(oldPort);
656 CFRelease(oldPort);
657 }
658 __CFSpinLock(&__CFAllMessagePortsLock);
659 // This relocking without checking to see if something else has grabbed
660 // that name in the cache is rather suspect, but what would that even
661 // mean has happened? We'd expect the bootstrap_* calls above to have
662 // failed for this one and not gotten this far, or failed for all of the
663 // other simultaneous attempts to get the name (and having succeeded for
664 // this one, gotten here). So we're not going to try very hard here
665 // with the thread-safety.
666 if (NULL == __CFAllLocalMessagePorts) {
667 __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
668 }
669 if (NULL != ms->_name) {
670 CFDictionaryRemoveValue(__CFAllLocalMessagePorts, ms->_name);
671 CFRelease(ms->_name);
672 }
673 ms->_name = name;
674 CFDictionaryAddValue(__CFAllLocalMessagePorts, name, ms);
675 __CFSpinUnlock(&__CFAllMessagePortsLock);
676 }
677
678 CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname);
679 return true;
680 }
681
682 void CFMessagePortGetContext(CFMessagePortRef ms, CFMessagePortContext *context) {
683 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
684 //#warning CF: assert that this is a local port
685 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
686 memmove(context, &ms->_context, sizeof(CFMessagePortContext));
687 }
688
689 void CFMessagePortInvalidate(CFMessagePortRef ms) {
690 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
691 if (!__CFMessagePortIsDeallocing(ms)) {
692 CFRetain(ms);
693 }
694 __CFMessagePortLock(ms);
695 if (__CFMessagePortIsValid(ms)) {
696 if (ms->_dispatchSource) {
697 dispatch_cancel(ms->_dispatchSource);
698 ms->_dispatchSource = NULL;
699 ms->_dispatchQ = NULL;
700 }
701
702 CFMessagePortInvalidationCallBack callout = ms->_icallout;
703 CFRunLoopSourceRef source = ms->_source;
704 CFMachPortRef replyPort = ms->_replyPort;
705 CFMachPortRef port = ms->_port;
706 CFStringRef name = ms->_name;
707 void *info = NULL;
708
709 __CFMessagePortUnsetValid(ms);
710 if (!__CFMessagePortIsRemote(ms)) {
711 info = ms->_context.info;
712 ms->_context.info = NULL;
713 }
714 ms->_source = NULL;
715 ms->_replyPort = NULL;
716 __CFMessagePortUnlock(ms);
717
718 __CFSpinLock(&__CFAllMessagePortsLock);
719 if (0 == ms->_perPID && NULL != (__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts)) {
720 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts, name);
721 }
722 __CFSpinUnlock(&__CFAllMessagePortsLock);
723 if (NULL != callout) {
724 callout(ms, info);
725 }
726 // We already know we're going invalid, don't need this callback
727 // anymore; plus, this solves a reentrancy deadlock; also, this
728 // must be done before the deallocate of the Mach port, to
729 // avoid a race between the notification message which could be
730 // handled in another thread, and this NULL'ing out.
731 CFMachPortSetInvalidationCallBack(port, NULL);
732 // For hashing and equality purposes, cannot get rid of _port here
733 if (!__CFMessagePortIsRemote(ms) && NULL != ms->_context.release) {
734 ms->_context.release(info);
735 }
736 if (NULL != source) {
737 CFRunLoopSourceInvalidate(source);
738 CFRelease(source);
739 }
740 if (NULL != replyPort) {
741 CFMachPortInvalidate(replyPort);
742 CFRelease(replyPort);
743 }
744 if (__CFMessagePortIsRemote(ms)) {
745 // Get rid of our extra ref on the Mach port gotten from bs server
746 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port));
747 }
748 } else {
749 __CFMessagePortUnlock(ms);
750 }
751 if (!__CFMessagePortIsDeallocing(ms)) {
752 CFRelease(ms);
753 }
754 }
755
756 Boolean CFMessagePortIsValid(CFMessagePortRef ms) {
757 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
758 if (!__CFMessagePortIsValid(ms)) return false;
759 if (NULL != ms->_port && !CFMachPortIsValid(ms->_port)) {
760 CFMessagePortInvalidate(ms);
761 return false;
762 }
763 if (NULL != ms->_replyPort && !CFMachPortIsValid(ms->_replyPort)) {
764 CFMessagePortInvalidate(ms);
765 return false;
766 }
767 if (NULL != ms->_source && !CFRunLoopSourceIsValid(ms->_source)) {
768 CFMessagePortInvalidate(ms);
769 return false;
770 }
771 return true;
772 }
773
774 CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms) {
775 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
776 return ms->_icallout;
777 }
778
779 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms, CFMessagePortInvalidationCallBack callout) {
780 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
781 if (!__CFMessagePortIsValid(ms) && NULL != callout) {
782 callout(ms, ms->_context.info);
783 } else {
784 ms->_icallout = callout;
785 }
786 }
787
788 static void __CFMessagePortReplyCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info) {
789 CFMessagePortRef ms = info;
790 mach_msg_base_t *msgp = msg;
791 mach_msg_base_t *replymsg;
792 __CFMessagePortLock(ms);
793 if (!__CFMessagePortIsValid(ms)) {
794 __CFMessagePortUnlock(ms);
795 return;
796 }
797
798 int32_t byteslen = 0;
799
800 Boolean invalidMagic = (MSGP_GET(msgp, magic) != MAGIC) && (CFSwapInt32(MSGP_GET(msgp, magic)) != MAGIC);
801 Boolean invalidComplex = (0 != msgp->body.msgh_descriptor_count) && !(msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX);
802 invalidComplex = invalidComplex || ((msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (0 == msgp->body.msgh_descriptor_count));
803 Boolean wayTooBig = ((msgp->body.msgh_descriptor_count) ? sizeof(struct __CFMessagePortMachMessage1) : sizeof(struct __CFMessagePortMachMessage0) + __CFMessagePortMaxInlineBytes) < msgp->header.msgh_size;
804 Boolean wayTooSmall = msgp->header.msgh_size < sizeof(struct __CFMessagePortMachMessage0);
805 Boolean wrongSize = false;
806 if (!(invalidComplex || wayTooBig || wayTooSmall)) {
807 byteslen = CFSwapInt32LittleToHost(MSGP_GET(msgp, byteslen));
808 wrongSize = (byteslen < 0) || (__CFMessagePortMaxDataSize < byteslen);
809 if (0 != msgp->body.msgh_descriptor_count) {
810 wrongSize = wrongSize || (MSGP1_FIELD(msgp, ool).size != byteslen);
811 } else {
812 wrongSize = wrongSize || (msgp->header.msgh_size - sizeof(struct __CFMessagePortMachMessage0) < byteslen);
813 }
814 }
815 Boolean invalidMsgID = (0 <= msgp->header.msgh_id) && (msgp->header.msgh_id <= INT32_MAX); // conversation id
816 if (invalidMagic || invalidComplex || wayTooBig || wayTooSmall || wrongSize || invalidMsgID) {
817 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d%d)"), invalidMagic, invalidComplex, wayTooBig, wayTooSmall, wrongSize, invalidMsgID);
818 mach_msg_destroy((mach_msg_header_t *)msgp);
819 __CFMessagePortUnlock(ms);
820 return;
821 }
822
823 if (CFDictionaryContainsKey(ms->_replies, (void *)(uintptr_t)msgp->header.msgh_id)) {
824 CFDataRef reply = NULL;
825 replymsg = (mach_msg_base_t *)msg;
826 if (0 == replymsg->body.msgh_descriptor_count) {
827 uintptr_t msgp_extent = (uintptr_t)((uint8_t *)msgp + msgp->header.msgh_size);
828 uintptr_t data_extent = (uintptr_t)((uint8_t *)&(MSGP0_FIELD(replymsg, bytes)) + byteslen);
829 if (0 <= byteslen && data_extent <= msgp_extent) {
830 reply = CFDataCreate(kCFAllocatorSystemDefault, MSGP0_FIELD(replymsg, bytes), byteslen);
831 } else {
832 reply = (void *)~0; // means NULL data
833 }
834 } else {
835 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
836 reply = CFDataCreate(kCFAllocatorSystemDefault, MSGP1_FIELD(replymsg, ool).address, MSGP1_FIELD(replymsg, ool).size);
837 vm_deallocate(mach_task_self(), (vm_address_t)MSGP1_FIELD(replymsg, ool).address, MSGP1_FIELD(replymsg, ool).size);
838 }
839 CFDictionarySetValue(ms->_replies, (void *)(uintptr_t)msgp->header.msgh_id, (void *)reply);
840 } else { /* discard message */
841 if (1 == msgp->body.msgh_descriptor_count) {
842 vm_deallocate(mach_task_self(), (vm_address_t)MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size);
843 }
844 }
845 __CFMessagePortUnlock(ms);
846 }
847
848 SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) {
849 mach_msg_base_t *sendmsg;
850 CFRunLoopRef currentRL = CFRunLoopGetCurrent();
851 CFRunLoopSourceRef source = NULL;
852 CFDataRef reply = NULL;
853 int64_t termTSR;
854 uint32_t sendOpts = 0, sendTimeOut = 0;
855 int32_t desiredReply;
856 Boolean didRegister = false;
857 kern_return_t ret;
858
859 //#warning CF: This should be an assert
860 // if (!__CFMessagePortIsRemote(remote)) return -999;
861 if (data && __CFMessagePortMaxDataSize < CFDataGetLength(data)) {
862 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize);
863 return kCFMessagePortTransportError;
864 }
865 __CFMessagePortLock(remote);
866 if (!__CFMessagePortIsValid(remote)) {
867 __CFMessagePortUnlock(remote);
868 return kCFMessagePortIsInvalid;
869 }
870 CFRetain(remote); // retain during run loop to avoid invalidation causing freeing
871 if (NULL == remote->_replyPort) {
872 CFMachPortContext context;
873 context.version = 0;
874 context.info = remote;
875 context.retain = (const void *(*)(const void *))CFRetain;
876 context.release = (void (*)(const void *))CFRelease;
877 context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription;
878 remote->_replyPort = CFMachPortCreate(CFGetAllocator(remote), __CFMessagePortReplyCallBack, &context, NULL);
879 }
880 remote->_convCounter++;
881 desiredReply = -remote->_convCounter;
882 sendmsg = __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote->_port), (replyMode != NULL ? CFMachPortGetPort(remote->_replyPort) : MACH_PORT_NULL), -desiredReply, msgid, (data ? CFDataGetBytePtr(data) : NULL), (data ? CFDataGetLength(data) : 0));
883 if (!sendmsg) {
884 __CFMessagePortUnlock(remote);
885 CFRelease(remote);
886 return kCFMessagePortTransportError;
887 }
888 if (replyMode != NULL) {
889 CFDictionarySetValue(remote->_replies, (void *)(uintptr_t)desiredReply, NULL);
890 source = CFMachPortCreateRunLoopSource(CFGetAllocator(remote), remote->_replyPort, -100);
891 didRegister = !CFRunLoopContainsSource(currentRL, source, replyMode);
892 if (didRegister) {
893 CFRunLoopAddSource(currentRL, source, replyMode);
894 }
895 }
896 if (sendTimeout < 10.0*86400) {
897 // anything more than 10 days is no timeout!
898 sendOpts = MACH_SEND_TIMEOUT;
899 sendTimeout *= 1000.0;
900 if (sendTimeout < 1.0) sendTimeout = 0.0;
901 sendTimeOut = floor(sendTimeout);
902 }
903 ret = mach_msg((mach_msg_header_t *)sendmsg, MACH_SEND_MSG|sendOpts, sendmsg->header.msgh_size, 0, MACH_PORT_NULL, sendTimeOut, MACH_PORT_NULL);
904 if (KERN_SUCCESS != ret) {
905 // need to deallocate the send-once right that might have been created
906 if (replyMode != NULL) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t *)sendmsg)->msgh_local_port);
907 if (didRegister) {
908 CFRunLoopRemoveSource(currentRL, source, replyMode);
909 }
910 if (source) CFRelease(source);
911 __CFMessagePortUnlock(remote);
912 CFAllocatorDeallocate(kCFAllocatorSystemDefault, sendmsg);
913 CFRelease(remote);
914 return (MACH_SEND_TIMED_OUT == ret) ? kCFMessagePortSendTimeout : kCFMessagePortTransportError;
915 }
916 __CFMessagePortUnlock(remote);
917 CFAllocatorDeallocate(kCFAllocatorSystemDefault, sendmsg);
918 if (replyMode == NULL) {
919 CFRelease(remote);
920 return kCFMessagePortSuccess;
921 }
922 _CFMachPortInstallNotifyPort(currentRL, replyMode);
923 termTSR = mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout);
924 for (;;) {
925 CFRunLoopRunInMode(replyMode, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), true);
926 // warning: what, if anything, should be done if remote is now invalid?
927 reply = CFDictionaryGetValue(remote->_replies, (void *)(uintptr_t)desiredReply);
928 if (NULL != reply || termTSR < (int64_t)mach_absolute_time()) {
929 break;
930 }
931 if (!CFMessagePortIsValid(remote)) {
932 // no reason that reply port alone should go invalid so we don't check for that
933 break;
934 }
935 }
936 // Should we uninstall the notify port? A complex question...
937 if (didRegister) {
938 CFRunLoopRemoveSource(currentRL, source, replyMode);
939 }
940 if (source) CFRelease(source);
941 if (NULL == reply) {
942 CFDictionaryRemoveValue(remote->_replies, (void *)(uintptr_t)desiredReply);
943 CFRelease(remote);
944 return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : kCFMessagePortBecameInvalidError;
945 }
946 if (NULL != returnDatap) {
947 *returnDatap = ((void *)~0 == reply) ? NULL : reply;
948 } else if ((void *)~0 != reply) {
949 CFRelease(reply);
950 }
951 CFDictionaryRemoveValue(remote->_replies, (void *)(uintptr_t)desiredReply);
952 CFRelease(remote);
953 return kCFMessagePortSuccess;
954 }
955
956 static mach_port_t __CFMessagePortGetPort(void *info) {
957 CFMessagePortRef ms = info;
958 if (!ms->_port) CFLog(kCFLogLevelWarning, CFSTR("*** Warning: A local CFMessagePort (%p) is being put in a run loop or dispatch queue, but it has not been named yet, so this will be a no-op and no messages are going to be received, even if named later."), info);
959 return ms->_port ? CFMachPortGetPort(ms->_port) : MACH_PORT_NULL;
960 }
961
962 static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
963 CFMessagePortRef ms = info;
964 mach_msg_base_t *msgp = msg;
965 mach_msg_base_t *replymsg;
966 void *context_info;
967 void (*context_release)(const void *);
968 CFDataRef returnData, data = NULL;
969 void *return_bytes = NULL;
970 CFIndex return_len = 0;
971 int32_t msgid;
972
973 __CFMessagePortLock(ms);
974 if (!__CFMessagePortIsValid(ms)) {
975 __CFMessagePortUnlock(ms);
976 return NULL;
977 }
978 if (NULL != ms->_context.retain) {
979 context_info = (void *)ms->_context.retain(ms->_context.info);
980 context_release = ms->_context.release;
981 } else {
982 context_info = ms->_context.info;
983 context_release = NULL;
984 }
985 __CFMessagePortUnlock(ms);
986
987 int32_t byteslen = 0;
988
989 Boolean invalidMagic = (MSGP_GET(msgp, magic) != MAGIC) && (CFSwapInt32(MSGP_GET(msgp, magic)) != MAGIC);
990 Boolean invalidComplex = (0 != msgp->body.msgh_descriptor_count) && !(msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX);
991 invalidComplex = invalidComplex || ((msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (0 == msgp->body.msgh_descriptor_count));
992 Boolean wayTooBig = ((msgp->body.msgh_descriptor_count) ? sizeof(struct __CFMessagePortMachMessage1) : sizeof(struct __CFMessagePortMachMessage0) + __CFMessagePortMaxInlineBytes) < msgp->header.msgh_size;
993 Boolean wayTooSmall = msgp->header.msgh_size < sizeof(struct __CFMessagePortMachMessage0);
994 Boolean wrongSize = false;
995 if (!(invalidComplex || wayTooBig || wayTooSmall)) {
996 byteslen = CFSwapInt32LittleToHost(MSGP_GET(msgp, byteslen));
997 wrongSize = (byteslen < 0) || (__CFMessagePortMaxDataSize < byteslen);
998 if (0 != msgp->body.msgh_descriptor_count) {
999 wrongSize = wrongSize || (MSGP1_FIELD(msgp, ool).size != byteslen);
1000 } else {
1001 wrongSize = wrongSize || (msgp->header.msgh_size - sizeof(struct __CFMessagePortMachMessage0) < byteslen);
1002 }
1003 }
1004 Boolean invalidMsgID = (msgp->header.msgh_id <= 0) || (INT32_MAX < msgp->header.msgh_id); // conversation id
1005 if (invalidMagic || invalidComplex || wayTooBig || wayTooSmall || wrongSize || invalidMsgID) {
1006 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic, invalidComplex, wayTooBig, wayTooSmall, wrongSize, invalidMsgID);
1007 mach_msg_destroy((mach_msg_header_t *)msgp);
1008 return NULL;
1009 }
1010
1011 /* Create no-copy, no-free-bytes wrapper CFData */
1012 if (0 == msgp->body.msgh_descriptor_count) {
1013 uintptr_t msgp_extent = (uintptr_t)((uint8_t *)msgp + msgp->header.msgh_size);
1014 uintptr_t data_extent = (uintptr_t)((uint8_t *)&(MSGP0_FIELD(msgp, bytes)) + byteslen);
1015 msgid = CFSwapInt32LittleToHost(MSGP_GET(msgp, msgid));
1016 if (0 <= byteslen && data_extent <= msgp_extent) {
1017 data = CFDataCreateWithBytesNoCopy(allocator, MSGP0_FIELD(msgp, bytes), byteslen, kCFAllocatorNull);
1018 }
1019 } else {
1020 msgid = CFSwapInt32LittleToHost(MSGP_GET(msgp, msgid));
1021 data = CFDataCreateWithBytesNoCopy(allocator, MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size, kCFAllocatorNull);
1022 }
1023 returnData = ms->_callout(ms, msgid, data, context_info);
1024 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1025 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1026 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1027 bytes inline in the Mach message, so can release the returnData object
1028 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1029 create a copy of the memory, which we'll have the kernel autodeallocate
1030 for us on send. In case (4) also, the bytes in the return data may be part
1031 of the bytes in "data" that we sent into the callout, so if the incoming
1032 data was received out of line, we wouldn't be able to clean up the out-of-line
1033 wad until the message was sent either, if we didn't make the copy. */
1034 if (NULL != returnData) {
1035 return_len = CFDataGetLength(returnData);
1036 if (__CFMessagePortMaxDataSize < return_len) {
1037 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize);
1038 return_len = 0;
1039 returnData = NULL;
1040 }
1041 if (returnData && return_len < __CFMessagePortMaxInlineBytes) {
1042 return_bytes = (void *)CFDataGetBytePtr(returnData);
1043 } else if (returnData) {
1044 return_bytes = NULL;
1045 vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
1046 /* vm_copy would only be a win here if the source address
1047 is page aligned; it is a lose in all other cases, since
1048 the kernel will just do the memmove for us (but not in
1049 as simple a way). */
1050 memmove(return_bytes, CFDataGetBytePtr(returnData), return_len);
1051 }
1052 }
1053 replymsg = __CFMessagePortCreateMessage(true, msgp->header.msgh_remote_port, MACH_PORT_NULL, -1 * (int32_t)msgp->header.msgh_id, msgid, return_bytes, return_len);
1054 if (1 == replymsg->body.msgh_descriptor_count) {
1055 MSGP1_FIELD(replymsg, ool).deallocate = true;
1056 }
1057 if (data) CFRelease(data);
1058 if (1 == msgp->body.msgh_descriptor_count) {
1059 vm_deallocate(mach_task_self(), (vm_address_t)MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size);
1060 }
1061 if (returnData) CFRelease(returnData);
1062 if (context_release) {
1063 context_release(context_info);
1064 }
1065 return replymsg;
1066 }
1067
1068 CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef ms, CFIndex order) {
1069 CFRunLoopSourceRef result = NULL;
1070 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
1071 //#warning CF: This should be an assert
1072 // if (__CFMessagePortIsRemote(ms)) return NULL;
1073 __CFMessagePortLock(ms);
1074 if (NULL == ms->_source && NULL == ms->_dispatchSource && __CFMessagePortIsValid(ms)) {
1075 CFRunLoopSourceContext1 context;
1076 context.version = 1;
1077 context.info = (void *)ms;
1078 context.retain = (const void *(*)(const void *))CFRetain;
1079 context.release = (void (*)(const void *))CFRelease;
1080 context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription;
1081 context.equal = NULL;
1082 context.hash = NULL;
1083 context.getPort = __CFMessagePortGetPort;
1084 context.perform = __CFMessagePortPerform;
1085 ms->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
1086 }
1087 if (NULL != ms->_source) {
1088 result = (CFRunLoopSourceRef)CFRetain(ms->_source);
1089 }
1090 __CFMessagePortUnlock(ms);
1091 return result;
1092 }
1093
1094 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms, dispatch_queue_t queue) {
1095 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
1096 __CFMessagePortLock(ms);
1097 if (!__CFMessagePortIsValid(ms)) {
1098 __CFMessagePortUnlock(ms);
1099 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1100 return;
1101 }
1102 if (__CFMessagePortIsRemote(ms)) {
1103 __CFMessagePortUnlock(ms);
1104 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1105 return;
1106 }
1107 if (NULL != ms->_source) {
1108 __CFMessagePortUnlock(ms);
1109 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1110 return;
1111 }
1112
1113 if (ms->_dispatchSource) {
1114 dispatch_cancel(ms->_dispatchSource);
1115 ms->_dispatchSource = NULL;
1116 ms->_dispatchQ = NULL;
1117 }
1118
1119 if (queue) {
1120 mach_port_t port = __CFMessagePortGetPort(ms);
1121 if (MACH_PORT_NULL != port) {
1122 ms->_dispatchSource = dispatch_source_machport_create(port, DISPATCH_MACHPORT_RECV, DISPATCH_SOURCE_CREATE_SUSPENDED, __mportQueue(), ^(dispatch_source_t source) {
1123 long e = 0, d = dispatch_source_get_error(source, &e);
1124 if (DISPATCH_ERROR_DOMAIN_POSIX == d && ECANCELED == e) {
1125 dispatch_release(queue);
1126 dispatch_release(source);
1127 return;
1128 }
1129 if (DISPATCH_ERROR_DOMAIN_NO_ERROR != d) {
1130 HALT;
1131 }
1132
1133 CFRetain(ms);
1134 mach_port_t port = dispatch_source_get_handle(source);
1135 mach_msg_header_t *msg = (mach_msg_header_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2048, 0);
1136 msg->msgh_size = 2048;
1137
1138 for (;;) {
1139 msg->msgh_bits = 0;
1140 msg->msgh_local_port = port;
1141 msg->msgh_remote_port = MACH_PORT_NULL;
1142 msg->msgh_id = 0;
1143
1144 kern_return_t ret = mach_msg(msg, MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, port, 0, MACH_PORT_NULL);
1145 if (MACH_MSG_SUCCESS == ret) break;
1146 if (MACH_RCV_TOO_LARGE != ret) HALT;
1147
1148 uint32_t newSize = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
1149 msg = CFAllocatorReallocate(kCFAllocatorSystemDefault, msg, newSize, 0);
1150 msg->msgh_size = newSize;
1151 }
1152
1153 dispatch_async(queue, ^{
1154 mach_msg_header_t *reply = __CFMessagePortPerform(msg, msg->msgh_size, kCFAllocatorSystemDefault, ms);
1155 if (NULL != reply) {
1156 kern_return_t ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
1157 if (KERN_SUCCESS != ret) mach_msg_destroy(reply);
1158 CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
1159 }
1160 CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg);
1161 CFRelease(ms);
1162 });
1163 });
1164 }
1165 if (ms->_dispatchSource) {
1166 dispatch_retain(queue);
1167 ms->_dispatchQ = queue;
1168 dispatch_resume(ms->_dispatchSource);
1169 } else {
1170 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1171 }
1172 }
1173 __CFMessagePortUnlock(ms);
1174 }
1175