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