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