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