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