]> git.saurik.com Git - apple/cf.git/blob - CFMessagePort.c
CF-744.12.tar.gz
[apple/cf.git] / CFMessagePort.c
1 /*
2 * Copyright (c) 2012 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-2012, 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 CFSpinLock_t __CFAllMessagePortsLock = CFSpinLockInit;
64 static CFMutableDictionaryRef __CFAllLocalMessagePorts = NULL;
65 static CFMutableDictionaryRef __CFAllRemoteMessagePorts = NULL;
66
67 struct __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
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 __CFSpinLock(&(ms->_lock));
136 }
137
138 CF_INLINE void __CFMessagePortUnlock(CFMessagePortRef ms) {
139 __CFSpinUnlock(&(ms->_lock));
140 }
141
142 // Just a heuristic
143 #define __CFMessagePortMaxInlineBytes 4096*10
144
145 struct __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
153 struct __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
168 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) {
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
215 static 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
241 static 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
290 static CFTypeID __kCFMessagePortTypeID = _kCFRuntimeNotATypeID;
291
292 static 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
304 __private_extern__ void __CFMessagePortInitialize(void) {
305 __kCFMessagePortTypeID = _CFRuntimeRegisterClass(&__CFMessagePortClass);
306 }
307
308 CFTypeID CFMessagePortGetTypeID(void) {
309 return __kCFMessagePortTypeID;
310 }
311
312 static 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
342 static void __CFMessagePortDummyCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) {
343 // not supposed to be implemented
344 }
345
346 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port, void *info) {
347 // info has been setup as the CFMessagePort owning the CFMachPort
348 if (info) CFMessagePortInvalidate(info);
349 }
350
351 static 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
478 CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) {
479 return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, false, NULL);
480 }
481
482 CFMessagePortRef CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) {
483 return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, true, NULL);
484 }
485
486 CFMessagePortRef _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
490 static 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
590 CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) {
591 return __CFMessagePortCreateRemote(allocator, name, false, 0);
592 }
593
594 CFMessagePortRef CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator, CFStringRef name, CFIndex pid) {
595 return __CFMessagePortCreateRemote(allocator, name, true, pid);
596 }
597
598 Boolean CFMessagePortIsRemote(CFMessagePortRef ms) {
599 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
600 return __CFMessagePortIsRemote(ms);
601 }
602
603 CFStringRef CFMessagePortGetName(CFMessagePortRef ms) {
604 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
605 return ms->_name;
606 }
607
608 Boolean 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
703 void 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
710 void 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
785 Boolean 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
803 CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms) {
804 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
805 return ms->_icallout;
806 }
807
808 void 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
817 static 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
877 SInt32 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 int64_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, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), 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 < (int64_t)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
987 static 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 static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
994 CFMessagePortRef ms = info;
995 mach_msg_base_t *msgp = msg;
996 mach_msg_base_t *replymsg;
997 void *context_info;
998 void (*context_release)(const void *);
999 CFDataRef returnData, data = NULL;
1000 void *return_bytes = NULL;
1001 CFIndex return_len = 0;
1002 int32_t msgid;
1003
1004 __CFMessagePortLock(ms);
1005 if (!__CFMessagePortIsValid(ms)) {
1006 __CFMessagePortUnlock(ms);
1007 return NULL;
1008 }
1009 if (NULL != ms->_context.retain) {
1010 context_info = (void *)ms->_context.retain(ms->_context.info);
1011 context_release = ms->_context.release;
1012 } else {
1013 context_info = ms->_context.info;
1014 context_release = NULL;
1015 }
1016 __CFMessagePortUnlock(ms);
1017
1018 int32_t byteslen = 0;
1019
1020 Boolean invalidMagic = (MSGP_GET(msgp, magic) != MAGIC) && (CFSwapInt32(MSGP_GET(msgp, magic)) != MAGIC);
1021 Boolean invalidComplex = (0 != msgp->body.msgh_descriptor_count) && !(msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX);
1022 invalidComplex = invalidComplex || ((msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (0 == msgp->body.msgh_descriptor_count));
1023 Boolean wayTooBig = ((msgp->body.msgh_descriptor_count) ? sizeof(struct __CFMessagePortMachMessage1) : sizeof(struct __CFMessagePortMachMessage0) + __CFMessagePortMaxInlineBytes) < msgp->header.msgh_size;
1024 Boolean wayTooSmall = msgp->header.msgh_size < sizeof(struct __CFMessagePortMachMessage0);
1025 Boolean wrongSize = false;
1026 if (!(invalidComplex || wayTooBig || wayTooSmall)) {
1027 byteslen = CFSwapInt32LittleToHost(MSGP_GET(msgp, byteslen));
1028 wrongSize = (byteslen < 0) || (__CFMessagePortMaxDataSize < byteslen);
1029 if (0 != msgp->body.msgh_descriptor_count) {
1030 wrongSize = wrongSize || (MSGP1_FIELD(msgp, ool).size != byteslen);
1031 } else {
1032 wrongSize = wrongSize || (msgp->header.msgh_size - sizeof(struct __CFMessagePortMachMessage0) < byteslen);
1033 }
1034 }
1035 Boolean invalidMsgID = (msgp->header.msgh_id <= 0) || (INT32_MAX < msgp->header.msgh_id); // conversation id
1036 if (invalidMagic || invalidComplex || wayTooBig || wayTooSmall || wrongSize || invalidMsgID) {
1037 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic, invalidComplex, wayTooBig, wayTooSmall, wrongSize, invalidMsgID);
1038 mach_msg_destroy((mach_msg_header_t *)msgp);
1039 return NULL;
1040 }
1041
1042 /* Create no-copy, no-free-bytes wrapper CFData */
1043 if (0 == msgp->body.msgh_descriptor_count) {
1044 uintptr_t msgp_extent = (uintptr_t)((uint8_t *)msgp + msgp->header.msgh_size);
1045 uintptr_t data_extent = (uintptr_t)((uint8_t *)&(MSGP0_FIELD(msgp, bytes)) + byteslen);
1046 msgid = CFSwapInt32LittleToHost(MSGP_GET(msgp, msgid));
1047 if (0 <= byteslen && data_extent <= msgp_extent) {
1048 data = CFDataCreateWithBytesNoCopy(allocator, MSGP0_FIELD(msgp, bytes), byteslen, kCFAllocatorNull);
1049 }
1050 } else {
1051 msgid = CFSwapInt32LittleToHost(MSGP_GET(msgp, msgid));
1052 data = CFDataCreateWithBytesNoCopy(allocator, MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size, kCFAllocatorNull);
1053 }
1054 if (ms->_callout) {
1055 returnData = ms->_callout(ms, msgid, data, context_info);
1056 } else {
1057 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));
1058 returnData = ms->_calloutEx(ms, msgid, data, context_info, trailer, 0);
1059 }
1060 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1061 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1062 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1063 bytes inline in the Mach message, so can release the returnData object
1064 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1065 create a copy of the memory, which we'll have the kernel autodeallocate
1066 for us on send. In case (4) also, the bytes in the return data may be part
1067 of the bytes in "data" that we sent into the callout, so if the incoming
1068 data was received out of line, we wouldn't be able to clean up the out-of-line
1069 wad until the message was sent either, if we didn't make the copy. */
1070 if (NULL != returnData) {
1071 return_len = CFDataGetLength(returnData);
1072 if (__CFMessagePortMaxDataSize < return_len) {
1073 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize);
1074 return_len = 0;
1075 returnData = NULL;
1076 }
1077 if (returnData && return_len < __CFMessagePortMaxInlineBytes) {
1078 return_bytes = (void *)CFDataGetBytePtr(returnData);
1079 } else if (returnData) {
1080 return_bytes = NULL;
1081 vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
1082 /* vm_copy would only be a win here if the source address
1083 is page aligned; it is a lose in all other cases, since
1084 the kernel will just do the memmove for us (but not in
1085 as simple a way). */
1086 memmove(return_bytes, CFDataGetBytePtr(returnData), return_len);
1087 }
1088 }
1089 replymsg = __CFMessagePortCreateMessage(true, msgp->header.msgh_remote_port, MACH_PORT_NULL, -1 * (int32_t)msgp->header.msgh_id, msgid, return_bytes, return_len);
1090 if (1 == replymsg->body.msgh_descriptor_count) {
1091 MSGP1_FIELD(replymsg, ool).deallocate = true;
1092 }
1093 if (data) CFRelease(data);
1094 if (1 == msgp->body.msgh_descriptor_count) {
1095 vm_deallocate(mach_task_self(), (vm_address_t)MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size);
1096 }
1097 if (returnData) CFRelease(returnData);
1098 if (context_release) {
1099 context_release(context_info);
1100 }
1101 return replymsg;
1102 }
1103
1104 CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef ms, CFIndex order) {
1105 CFRunLoopSourceRef result = NULL;
1106 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
1107 if (!CFMessagePortIsValid(ms)) return NULL;
1108 if (__CFMessagePortIsRemote(ms)) return NULL;
1109 __CFMessagePortLock(ms);
1110 if (NULL != ms->_source && !CFRunLoopSourceIsValid(ms->_source)) {
1111 CFRelease(ms->_source);
1112 ms->_source = NULL;
1113 }
1114 if (NULL == ms->_source && NULL == ms->_dispatchSource && __CFMessagePortIsValid(ms)) {
1115 CFRunLoopSourceContext1 context;
1116 context.version = 1;
1117 context.info = (void *)ms;
1118 context.retain = (const void *(*)(const void *))CFRetain;
1119 context.release = (void (*)(const void *))CFRelease;
1120 context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription;
1121 context.equal = NULL;
1122 context.hash = NULL;
1123 context.getPort = __CFMessagePortGetPort;
1124 context.perform = __CFMessagePortPerform;
1125 ms->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
1126 }
1127 if (NULL != ms->_source) {
1128 result = (CFRunLoopSourceRef)CFRetain(ms->_source);
1129 }
1130 __CFMessagePortUnlock(ms);
1131 return result;
1132 }
1133
1134 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms, dispatch_queue_t queue) {
1135 __CFGenericValidateType(ms, __kCFMessagePortTypeID);
1136 __CFMessagePortLock(ms);
1137 if (!__CFMessagePortIsValid(ms)) {
1138 __CFMessagePortUnlock(ms);
1139 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1140 return;
1141 }
1142 if (__CFMessagePortIsRemote(ms)) {
1143 __CFMessagePortUnlock(ms);
1144 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1145 return;
1146 }
1147 if (NULL != ms->_source) {
1148 __CFMessagePortUnlock(ms);
1149 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1150 return;
1151 }
1152
1153 if (ms->_dispatchSource) {
1154 dispatch_source_cancel(ms->_dispatchSource);
1155 ms->_dispatchSource = NULL;
1156 ms->_dispatchQ = NULL;
1157 }
1158
1159 if (queue) {
1160 mach_port_t port = __CFMessagePortGetPort(ms);
1161 if (MACH_PORT_NULL != port) {
1162 static dispatch_queue_t mportQueue = NULL;
1163 static dispatch_once_t once;
1164 dispatch_once(&once, ^{
1165 mportQueue = dispatch_queue_create("CFMessagePort Queue", NULL);
1166 });
1167 dispatch_source_t theSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, port, 0, mportQueue);
1168 dispatch_source_set_cancel_handler(theSource, ^{
1169 dispatch_release(queue);
1170 dispatch_release(theSource);
1171 });
1172 dispatch_source_set_event_handler(theSource, ^{
1173 CFRetain(ms);
1174 mach_msg_header_t *msg = (mach_msg_header_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2048, 0);
1175 msg->msgh_size = 2048;
1176
1177 for (;;) {
1178 msg->msgh_bits = 0;
1179 msg->msgh_local_port = port;
1180 msg->msgh_remote_port = MACH_PORT_NULL;
1181 msg->msgh_id = 0;
1182
1183 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);
1184 if (MACH_MSG_SUCCESS == ret) break;
1185 if (MACH_RCV_TOO_LARGE != ret) HALT;
1186
1187 uint32_t newSize = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
1188 msg = CFAllocatorReallocate(kCFAllocatorSystemDefault, msg, newSize, 0);
1189 msg->msgh_size = newSize;
1190 }
1191
1192 dispatch_async(queue, ^{
1193 mach_msg_header_t *reply = __CFMessagePortPerform(msg, msg->msgh_size, kCFAllocatorSystemDefault, ms);
1194 if (NULL != reply) {
1195 kern_return_t ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
1196 if (KERN_SUCCESS != ret) mach_msg_destroy(reply);
1197 CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
1198 }
1199 CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg);
1200 CFRelease(ms);
1201 });
1202 });
1203 ms->_dispatchSource = theSource;
1204 }
1205 if (ms->_dispatchSource) {
1206 dispatch_retain(queue);
1207 ms->_dispatchQ = queue;
1208 dispatch_resume(ms->_dispatchSource);
1209 } else {
1210 CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1211 }
1212 }
1213 __CFMessagePortUnlock(ms);
1214 }
1215