2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2014, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
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>
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>
41 #include <mach/mach_time.h>
43 #include <dispatch/dispatch.h>
44 #include <dispatch/private.h>
46 extern pid_t
getpid(void);
48 #define __kCFMessagePortMaxNameLengthMax 255
50 #if defined(BOOTSTRAP_MAX_NAME_LEN)
51 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
53 #define __kCFMessagePortMaxNameLength 128
56 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
57 #undef __kCFMessagePortMaxNameLength
58 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
61 #define __CFMessagePortMaxDataSize 0x60000000L
63 static CFLock_t __CFAllMessagePortsLock
= CFLockInit
;
64 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
65 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
67 struct __CFMessagePort
{
71 CFMachPortRef _port
; /* immutable; invalidated */
72 CFMutableDictionaryRef _replies
;
74 int32_t _perPID
; /* zero if not per-pid, else pid */
75 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
76 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
77 dispatch_source_t _dispatchSource
; /* only used by local port; invalidated */
78 dispatch_queue_t _dispatchQ
; /* only used by local port */
79 CFMessagePortInvalidationCallBack _icallout
;
80 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
81 CFMessagePortCallBackEx _calloutEx
; /* only used by local port; immutable */
82 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
85 /* Bit 0 in the base reserved bits is used for invalid state */
86 /* Bit 1 of the base reserved bits is used for has-extra-port-refs state */
87 /* Bit 2 of the base reserved bits is used for is-remote state */
88 /* Bit 3 in the base reserved bits is used for is-deallocing state */
90 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
91 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0);
94 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
95 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 1);
98 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
99 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 0);
102 CF_INLINE Boolean
__CFMessagePortExtraMachRef(CFMessagePortRef ms
) {
103 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
106 CF_INLINE
void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms
) {
107 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
110 CF_INLINE
void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms
) {
111 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 0);
114 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
115 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
118 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
119 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
122 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
123 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 0);
126 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
127 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
130 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
131 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3, 1);
134 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
135 __CFLock(&(ms
->_lock
));
138 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
139 __CFUnlock(&(ms
->_lock
));
142 #if !defined(__LP64__)
147 #define __CFMessagePortMaxInlineBytes ((int32_t)4000)
149 struct __CFMessagePortMachMessage
{
150 mach_msg_base_t base
;
151 mach_msg_ool_descriptor_t ool
;
160 #define __INNARD_OFFSET (((!(msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) && ((mach_msg_header_t *)msgp)->msgh_id == 0) || ( (msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) && !__LP64__)) ? 40 : 44)
162 #define MAGIC 0xF0F2F4F8
164 // These 3 macros should ONLY be used on RECEIVED messages, not ones being constructed on the sending side
165 #define MSGP_GET(msgp, ident) (((struct __CFMessagePortMachMessage *)msgp)->ident)
166 #define MSGP_INFO(msgp, ident) ((struct innards *)((void *)msgp + __INNARD_OFFSET))->ident
167 #define MSGP_SIZE(msgp) (__INNARD_OFFSET + sizeof(struct innards))
170 static mach_msg_base_t
*__CFMessagePortCreateMessage(bool reply
, mach_port_t port
, mach_port_t replyPort
, int32_t convid
, int32_t msgid
, const uint8_t *bytes
, int32_t byteslen
) {
171 if (__CFMessagePortMaxDataSize
< byteslen
) return NULL
;
172 if (byteslen
< -1) return NULL
;
173 int32_t rounded_byteslen
= (byteslen
< 0) ? 0 : ((byteslen
+ 7) & ~0x7);
174 int32_t size
= (int32_t)sizeof(struct __CFMessagePortMachMessage
) + ((rounded_byteslen
<= __CFMessagePortMaxInlineBytes
) ? rounded_byteslen
: 0);
175 struct __CFMessagePortMachMessage
*msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
176 if (!msg
) return NULL
;
177 memset(msg
, 0, size
);
179 msg
->base
.header
.msgh_id
= 1;
181 msg
->base
.header
.msgh_id
= 0;
183 msg
->base
.header
.msgh_size
= size
;
184 msg
->base
.header
.msgh_remote_port
= port
;
185 msg
->base
.header
.msgh_local_port
= replyPort
;
186 msg
->base
.header
.msgh_bits
= MACH_MSGH_BITS((reply
? MACH_MSG_TYPE_MOVE_SEND_ONCE
: MACH_MSG_TYPE_COPY_SEND
), (MACH_PORT_NULL
!= replyPort
? MACH_MSG_TYPE_MAKE_SEND_ONCE
: 0));
187 msg
->base
.body
.msgh_descriptor_count
= 0;
188 msg
->innards
.magic
= MAGIC
;
189 msg
->innards
.msgid
= CFSwapInt32HostToLittle(msgid
);
190 msg
->innards
.convid
= CFSwapInt32HostToLittle(convid
);
191 msg
->innards
.byteslen
= CFSwapInt32HostToLittle(byteslen
);
192 if (rounded_byteslen
<= __CFMessagePortMaxInlineBytes
) {
193 if (NULL
!= bytes
&& 0 < byteslen
) {
194 memmove(msg
->innards
.bytes
, bytes
, byteslen
);
197 msg
->base
.header
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
198 msg
->base
.body
.msgh_descriptor_count
= 1;
199 msg
->ool
.deallocate
= false;
200 msg
->ool
.copy
= MACH_MSG_VIRTUAL_COPY
;
201 msg
->ool
.address
= (void *)bytes
;
202 msg
->ool
.size
= byteslen
;
203 msg
->ool
.type
= MACH_MSG_OOL_DESCRIPTOR
;
205 return (mach_msg_base_t
*)msg
;
208 static CFStringRef
__CFMessagePortCopyDescription(CFTypeRef cf
) {
209 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
212 CFStringRef contextDesc
= NULL
;
214 if (__CFMessagePortIsRemote(ms
)) {
215 result
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@}"), cf
, CFGetAllocator(ms
), locked
, (__CFMessagePortIsValid(ms
) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms
) ? "Yes" : "No"), ms
->_name
);
217 if (NULL
!= ms
->_context
.info
&& NULL
!= ms
->_context
.copyDescription
) {
218 contextDesc
= ms
->_context
.copyDescription(ms
->_context
.info
);
220 if (NULL
== contextDesc
) {
221 contextDesc
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort context %p>"), ms
->_context
.info
);
223 void *addr
= ms
->_callout
? (void *)ms
->_callout
: (void *)ms
->_calloutEx
;
225 const char *name
= (dladdr(addr
, &info
) && info
.dli_saddr
== addr
&& info
.dli_sname
) ? info
.dli_sname
: "???";
226 result
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %s (%p), context = %@}"), cf
, CFGetAllocator(ms
), locked
, (__CFMessagePortIsValid(ms
) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms
) ? "Yes" : "No"), ms
->_name
, ms
->_source
, name
, addr
, (NULL
!= contextDesc
? contextDesc
: CFSTR("<no description>")));
228 if (NULL
!= contextDesc
) {
229 CFRelease(contextDesc
);
234 static void __CFMessagePortDeallocate(CFTypeRef cf
) {
235 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
236 __CFMessagePortSetIsDeallocing(ms
);
237 CFMessagePortInvalidate(ms
);
238 // Delay cleanup of _replies until here so that invalidation during
239 // SendRequest does not cause _replies to disappear out from under that function.
240 if (NULL
!= ms
->_replies
) {
241 CFRelease(ms
->_replies
);
243 if (NULL
!= ms
->_name
) {
244 CFRelease(ms
->_name
);
246 if (NULL
!= ms
->_port
) {
247 if (__CFMessagePortExtraMachRef(ms
)) {
248 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_SEND
, -1);
249 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_RECEIVE
, -1);
251 CFMachPortInvalidate(ms
->_port
);
252 CFRelease(ms
->_port
);
255 // A remote message port for a local message port in the same process will get the
256 // same mach port, and the remote port will keep the mach port from being torn down,
257 // thus keeping the remote port from getting any sort of death notification and
258 // auto-invalidating; so we manually implement the 'auto-invalidation' here by
259 // tickling each remote port to check its state after any message port is destroyed,
260 // but most importantly after local message ports are destroyed.
261 __CFLock(&__CFAllMessagePortsLock
);
262 CFMessagePortRef
*remotePorts
= NULL
;
264 if (NULL
!= __CFAllRemoteMessagePorts
) {
265 cnt
= CFDictionaryGetCount(__CFAllRemoteMessagePorts
);
266 remotePorts
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, cnt
* sizeof(CFMessagePortRef
), __kCFAllocatorGCScannedMemory
);
267 CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts
, NULL
, (const void **)remotePorts
);
268 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
269 CFRetain(remotePorts
[idx
]);
272 __CFUnlock(&__CFAllMessagePortsLock
);
274 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
275 // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid
276 CFMessagePortIsValid(remotePorts
[idx
]);
277 CFRelease(remotePorts
[idx
]);
279 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, remotePorts
);
283 static CFTypeID __kCFMessagePortTypeID
= _kCFRuntimeNotATypeID
;
285 static const CFRuntimeClass __CFMessagePortClass
= {
290 __CFMessagePortDeallocate
,
294 __CFMessagePortCopyDescription
297 CFTypeID
CFMessagePortGetTypeID(void) {
298 static dispatch_once_t initOnce
;
299 dispatch_once(&initOnce
, ^{ __kCFMessagePortTypeID
= _CFRuntimeRegisterClass(&__CFMessagePortClass
); });
300 return __kCFMessagePortTypeID
;
303 static CFStringRef
__CFMessagePortSanitizeStringName(CFStringRef name
, uint8_t **utfnamep
, CFIndex
*utfnamelenp
) {
306 CFStringRef result
= NULL
;
307 utfname
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, __kCFMessagePortMaxNameLength
+ 1, 0);
308 CFStringGetBytes(name
, CFRangeMake(0, CFStringGetLength(name
)), kCFStringEncodingUTF8
, 0, false, utfname
, __kCFMessagePortMaxNameLength
, &utflen
);
309 utfname
[utflen
] = '\0';
310 if (strlen((const char *)utfname
) != utflen
) {
311 /* PCA 9194709: refuse to sanitize a string with an embedded nul character */
312 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
316 /* A new string is created, because the original string may have been
317 truncated to the max length, and we want the string name to definitely
318 match the raw UTF-8 chunk that has been created. Also, this is useful
319 to get a constant string in case the original name string was mutable. */
320 result
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, utfname
, utflen
, kCFStringEncodingUTF8
, false);
322 if (NULL
!= utfnamep
) {
324 } else if (NULL
!= utfname
) {
325 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
327 if (NULL
!= utfnamelenp
) {
328 *utfnamelenp
= utflen
;
333 static void __CFMessagePortDummyCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
334 // not supposed to be implemented
337 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port
, void *info
) {
338 // info has been setup as the CFMessagePort owning the CFMachPort
339 if (info
) CFMessagePortInvalidate(info
);
342 static CFMessagePortRef
__CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
, Boolean perPID
, CFMessagePortCallBackEx calloutEx
) {
343 CFMessagePortRef memory
;
344 uint8_t *utfname
= NULL
;
346 if (shouldFreeInfo
) *shouldFreeInfo
= true;
348 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
350 __CFLock(&__CFAllMessagePortsLock
);
351 if (!perPID
&& NULL
!= name
) {
352 CFMessagePortRef existing
;
353 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
355 __CFUnlock(&__CFAllMessagePortsLock
);
357 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
358 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
362 return (CFMessagePortRef
)(existing
);
365 __CFUnlock(&__CFAllMessagePortsLock
);
366 CFIndex size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
367 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, CFMessagePortGetTypeID(), size
, NULL
);
368 if (NULL
== memory
) {
372 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
375 __CFMessagePortUnsetValid(memory
);
376 __CFMessagePortUnsetExtraMachRef(memory
);
377 __CFMessagePortUnsetRemote(memory
);
378 memory
->_lock
= CFLockInit
;
379 memory
->_name
= name
;
380 memory
->_port
= NULL
;
381 memory
->_replies
= NULL
;
382 memory
->_convCounter
= 0;
383 memory
->_perPID
= perPID
? getpid() : 0; // actual value not terribly useful for local ports
384 memory
->_replyPort
= NULL
;
385 memory
->_source
= NULL
;
386 memory
->_dispatchSource
= NULL
;
387 memory
->_dispatchQ
= NULL
;
388 memory
->_icallout
= NULL
;
389 memory
->_callout
= callout
;
390 memory
->_calloutEx
= calloutEx
;
391 memory
->_context
.info
= NULL
;
392 memory
->_context
.retain
= NULL
;
393 memory
->_context
.release
= NULL
;
394 memory
->_context
.copyDescription
= NULL
;
397 CFMachPortRef native
= NULL
;
400 task_get_bootstrap_port(mach_task_self(), &bs
);
402 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
403 if (ret
== KERN_SUCCESS
) {
404 mach_port_type_t type
= 0;
405 ret
= mach_port_type(mach_task_self(), mp
, &type
);
406 if (KERN_SUCCESS
!= ret
|| (0 == (type
& MACH_PORT_TYPE_PORT_RIGHTS
))) {
407 CFLog(kCFLogLevelError
, CFSTR("*** CFMessagePort: bootstrap_check_in() succeeded, but mach port type is unknown (err %d) or returned invalid type (0x%x); name = '%s'"), ret
, type
, utfname
);
408 ret
= KERN_INVALID_RIGHT
;
410 ret
= (KERN_SUCCESS
== ret
) ? mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
) : ret
;
411 if (KERN_SUCCESS
== ret
) {
412 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
413 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
415 mach_port_destroy(mach_task_self(), mp
);
416 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
417 // name is released by deallocation
421 __CFMessagePortSetExtraMachRef(memory
);
423 mach_port_destroy(mach_task_self(), mp
);
424 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
425 // name is released by deallocation
432 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
433 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
435 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
436 // name is released by deallocation
440 mp
= CFMachPortGetPort(native
);
441 #pragma GCC diagnostic push
442 #pragma GCC diagnostic ignored "-Wdeprecated"
443 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
444 #pragma GCC diagnostic pop
445 if (ret
!= KERN_SUCCESS
) {
446 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
447 CFMachPortInvalidate(native
);
449 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
450 // name is released by deallocation
455 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
456 memory
->_port
= native
;
459 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
460 __CFMessagePortSetValid(memory
);
461 if (NULL
!= context
) {
462 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
463 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
465 __CFLock(&__CFAllMessagePortsLock
);
466 if (!perPID
&& NULL
!= name
) {
467 CFMessagePortRef existing
;
468 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
470 __CFUnlock(&__CFAllMessagePortsLock
);
472 return (CFMessagePortRef
)(existing
);
474 if (NULL
== __CFAllLocalMessagePorts
) {
475 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
477 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
479 __CFUnlock(&__CFAllMessagePortsLock
);
480 if (shouldFreeInfo
) *shouldFreeInfo
= false;
484 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
485 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, false, NULL
);
488 CFMessagePortRef
CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
489 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, true, NULL
);
492 CFMessagePortRef
_CFMessagePortCreateLocalEx(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, uintptr_t unused
, CFMessagePortCallBackEx calloutEx
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
493 return __CFMessagePortCreateLocal(allocator
, name
, NULL
, context
, shouldFreeInfo
, perPID
, calloutEx
);
496 static CFMessagePortRef
__CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, CFIndex pid
) {
497 CFMessagePortRef memory
;
498 CFMachPortRef native
;
499 CFMachPortContext ctx
;
500 uint8_t *utfname
= NULL
;
502 mach_port_t bp
, port
;
505 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
509 __CFLock(&__CFAllMessagePortsLock
);
510 if (!perPID
&& NULL
!= name
) {
511 CFMessagePortRef existing
;
512 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
514 __CFUnlock(&__CFAllMessagePortsLock
);
516 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
517 if (!CFMessagePortIsValid(existing
)) { // must do this outside lock to avoid deadlock
521 return (CFMessagePortRef
)(existing
);
524 __CFUnlock(&__CFAllMessagePortsLock
);
525 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
526 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, CFMessagePortGetTypeID(), size
, NULL
);
527 if (NULL
== memory
) {
531 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
534 __CFMessagePortUnsetValid(memory
);
535 __CFMessagePortUnsetExtraMachRef(memory
);
536 __CFMessagePortSetRemote(memory
);
537 memory
->_lock
= CFLockInit
;
538 memory
->_name
= name
;
539 memory
->_port
= NULL
;
540 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
541 memory
->_convCounter
= 0;
542 memory
->_perPID
= perPID
? pid
: 0;
543 memory
->_replyPort
= NULL
;
544 memory
->_source
= NULL
;
545 memory
->_dispatchSource
= NULL
;
546 memory
->_dispatchQ
= NULL
;
547 memory
->_icallout
= NULL
;
548 memory
->_callout
= NULL
;
549 memory
->_calloutEx
= NULL
;
554 ctx
.copyDescription
= NULL
;
555 task_get_bootstrap_port(mach_task_self(), &bp
);
556 ret
= bootstrap_look_up2(bp
, (char *)utfname
, &port
, perPID
? (pid_t
)pid
: 0, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
557 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
558 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
559 if (NULL
== native
) {
560 // name is released by deallocation
564 memory
->_port
= native
;
565 __CFMessagePortSetValid(memory
);
566 __CFLock(&__CFAllMessagePortsLock
);
567 if (!perPID
&& NULL
!= name
) {
568 CFMessagePortRef existing
;
569 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
571 __CFUnlock(&__CFAllMessagePortsLock
);
573 return (CFMessagePortRef
)(existing
);
575 if (NULL
== __CFAllRemoteMessagePorts
) {
576 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
578 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
581 __CFUnlock(&__CFAllMessagePortsLock
);
582 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
583 // that set-invalidation-callback might have called back into us
584 // if the CFMachPort is already bad, but that was a no-op since
585 // there was no callback setup at the (previous) time the CFMachPort
586 // went invalid; so check for validity manually and react
587 if (!CFMachPortIsValid(native
)) {
588 CFRelease(memory
); // does the invalidate
593 return (CFMessagePortRef
)memory
;
596 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
597 return __CFMessagePortCreateRemote(allocator
, name
, false, 0);
600 CFMessagePortRef
CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator
, CFStringRef name
, CFIndex pid
) {
601 return __CFMessagePortCreateRemote(allocator
, name
, true, pid
);
604 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
605 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
606 return __CFMessagePortIsRemote(ms
);
609 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
610 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
614 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
615 CFAllocatorRef allocator
= CFGetAllocator(ms
);
616 uint8_t *utfname
= NULL
;
618 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
619 if (ms
->_perPID
|| __CFMessagePortIsRemote(ms
)) return false;
620 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
624 __CFLock(&__CFAllMessagePortsLock
);
626 CFMessagePortRef existing
;
627 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
628 __CFUnlock(&__CFAllMessagePortsLock
);
630 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
634 __CFUnlock(&__CFAllMessagePortsLock
);
636 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
637 CFMachPortRef oldPort
= ms
->_port
;
638 CFMachPortRef native
= NULL
;
641 task_get_bootstrap_port(mach_task_self(), &bs
);
642 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
643 if (ret
== KERN_SUCCESS
) {
644 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
645 if (KERN_SUCCESS
== ret
) {
646 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
647 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
648 __CFMessagePortSetExtraMachRef(ms
);
650 mach_port_destroy(mach_task_self(), mp
);
651 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
657 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
658 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
660 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
664 mp
= CFMachPortGetPort(native
);
665 #pragma GCC diagnostic push
666 #pragma GCC diagnostic ignored "-Wdeprecated"
667 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, 0);
668 #pragma GCC diagnostic pop
669 if (ret
!= KERN_SUCCESS
) {
670 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
671 CFMachPortInvalidate(native
);
673 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
678 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
680 if (NULL
!= oldPort
&& oldPort
!= native
) {
681 if (__CFMessagePortExtraMachRef(ms
)) {
682 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_SEND
, -1);
683 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_RECEIVE
, -1);
685 CFMachPortInvalidate(oldPort
);
688 __CFLock(&__CFAllMessagePortsLock
);
689 // This relocking without checking to see if something else has grabbed
690 // that name in the cache is rather suspect, but what would that even
691 // mean has happened? We'd expect the bootstrap_* calls above to have
692 // failed for this one and not gotten this far, or failed for all of the
693 // other simultaneous attempts to get the name (and having succeeded for
694 // this one, gotten here). So we're not going to try very hard here
695 // with the thread-safety.
696 if (NULL
== __CFAllLocalMessagePorts
) {
697 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
699 if (NULL
!= ms
->_name
) {
700 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
701 CFRelease(ms
->_name
);
704 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
705 __CFUnlock(&__CFAllMessagePortsLock
);
708 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
712 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
713 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
714 //#warning CF: assert that this is a local port
715 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
716 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
719 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
720 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
721 if (!__CFMessagePortIsDeallocing(ms
)) {
724 __CFMessagePortLock(ms
);
725 if (__CFMessagePortIsValid(ms
)) {
726 if (ms
->_dispatchSource
) {
727 dispatch_source_cancel(ms
->_dispatchSource
);
728 ms
->_dispatchSource
= NULL
;
729 ms
->_dispatchQ
= NULL
;
732 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
733 CFRunLoopSourceRef source
= ms
->_source
;
734 CFMachPortRef replyPort
= ms
->_replyPort
;
735 CFMachPortRef port
= ms
->_port
;
736 CFStringRef name
= ms
->_name
;
739 __CFMessagePortUnsetValid(ms
);
740 if (!__CFMessagePortIsRemote(ms
)) {
741 info
= ms
->_context
.info
;
742 ms
->_context
.info
= NULL
;
745 ms
->_replyPort
= NULL
;
747 __CFMessagePortUnlock(ms
);
749 __CFLock(&__CFAllMessagePortsLock
);
750 if (0 == ms
->_perPID
&& NULL
!= name
&& NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
751 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
753 __CFUnlock(&__CFAllMessagePortsLock
);
754 if (NULL
!= callout
) {
757 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
758 ms
->_context
.release(info
);
760 if (NULL
!= source
) {
761 CFRunLoopSourceInvalidate(source
);
764 if (NULL
!= replyPort
) {
765 CFMachPortInvalidate(replyPort
);
766 CFRelease(replyPort
);
768 if (__CFMessagePortIsRemote(ms
)) {
769 // Get rid of our extra ref on the Mach port gotten from bs server
770 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
773 // We already know we're going invalid, don't need this callback
774 // anymore; plus, this solves a reentrancy deadlock; also, this
775 // must be done before the deallocate of the Mach port, to
776 // avoid a race between the notification message which could be
777 // handled in another thread, and this NULL'ing out.
778 CFMachPortSetInvalidationCallBack(port
, NULL
);
779 if (__CFMessagePortExtraMachRef(ms
)) {
780 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port
), MACH_PORT_RIGHT_SEND
, -1);
781 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port
), MACH_PORT_RIGHT_RECEIVE
, -1);
783 CFMachPortInvalidate(port
);
787 __CFMessagePortUnlock(ms
);
789 if (!__CFMessagePortIsDeallocing(ms
)) {
794 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
795 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
796 if (!__CFMessagePortIsValid(ms
)) return false;
798 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
799 CFMessagePortInvalidate(ms
);
803 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
804 CFMessagePortInvalidate(ms
);
812 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
813 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
814 return ms
->_icallout
;
817 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
818 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
819 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
820 callout(ms
, ms
->_context
.info
);
822 ms
->_icallout
= callout
;
826 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
827 CFMessagePortRef ms
= info
;
828 mach_msg_base_t
*msgp
= msg
;
829 mach_msg_base_t
*replymsg
;
830 __CFMessagePortLock(ms
);
831 if (!__CFMessagePortIsValid(ms
)) {
832 __CFMessagePortUnlock(ms
);
836 int32_t byteslen
= 0;
838 Boolean wayTooSmall
= size
< sizeof(mach_msg_header_t
) || size
< MSGP_SIZE(msgp
) || msgp
->header
.msgh_size
< MSGP_SIZE(msgp
);
839 Boolean invalidMagic
= false;
840 Boolean invalidComplex
= false;
841 Boolean wayTooBig
= false;
843 invalidMagic
= ((MSGP_INFO(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_INFO(msgp
, magic
)) != MAGIC
));
844 invalidComplex
= (msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (1 != msgp
->body
.msgh_descriptor_count
);
845 wayTooBig
= ((int32_t)MSGP_SIZE(msgp
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
; // also less than a 32-bit signed int can hold
847 Boolean wrongSize
= false;
848 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
849 byteslen
= CFSwapInt32LittleToHost(MSGP_INFO(msgp
, byteslen
));
850 wrongSize
= (byteslen
< -1) || (__CFMessagePortMaxDataSize
< byteslen
);
851 if (msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
852 wrongSize
= wrongSize
|| (MSGP_GET(msgp
, ool
).size
!= byteslen
);
854 wrongSize
= wrongSize
|| ((int32_t)msgp
->header
.msgh_size
- (int32_t)MSGP_SIZE(msgp
) < byteslen
);
857 Boolean invalidMsgID
= wayTooSmall
? false : ((0 <= MSGP_INFO(msgp
, convid
)) && (MSGP_INFO(msgp
, convid
) <= INT32_MAX
)); // conversation id
858 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
859 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
860 mach_msg_destroy((mach_msg_header_t
*)msgp
);
861 __CFMessagePortUnlock(ms
);
865 if (CFDictionaryContainsKey(ms
->_replies
, (void *)(uintptr_t)MSGP_INFO(msgp
, convid
))) {
866 CFDataRef reply
= NULL
;
867 replymsg
= (mach_msg_base_t
*)msg
;
868 if (!(replymsg
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
869 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
870 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP_INFO(replymsg
, bytes
)) + byteslen
);
871 if (byteslen
< 0) byteslen
= 0; // from here on, treat negative same as zero -- this is historical behavior: a NULL return from the callback on the other side results in empty data to the original requestor
872 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
873 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP_INFO(replymsg
, bytes
), byteslen
);
875 reply
= (void *)~0; // means NULL data
878 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
879 reply
= CFDataCreate(kCFAllocatorSystemDefault
, MSGP_GET(replymsg
, ool
).address
, MSGP_GET(replymsg
, ool
).size
);
880 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP_GET(replymsg
, ool
).address
, MSGP_GET(replymsg
, ool
).size
);
882 CFDictionarySetValue(ms
->_replies
, (void *)(uintptr_t)MSGP_INFO(msgp
, convid
), (void *)reply
);
883 } else { /* discard message */
884 if (msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
885 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP_GET(msgp
, ool
).address
, MSGP_GET(msgp
, ool
).size
);
888 __CFMessagePortUnlock(ms
);
891 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
892 mach_msg_base_t
*sendmsg
;
893 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
894 CFRunLoopSourceRef source
= NULL
;
895 CFDataRef reply
= NULL
;
897 uint32_t sendOpts
= 0, sendTimeOut
= 0;
898 int32_t desiredReply
;
899 Boolean didRegister
= false;
902 //#warning CF: This should be an assert
903 // if (!__CFMessagePortIsRemote(remote)) return -999;
904 if (data
&& __CFMessagePortMaxDataSize
< CFDataGetLength(data
)) {
905 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
906 return kCFMessagePortTransportError
;
908 __CFMessagePortLock(remote
);
909 if (!__CFMessagePortIsValid(remote
)) {
910 __CFMessagePortUnlock(remote
);
911 return kCFMessagePortIsInvalid
;
913 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
914 if (NULL
== remote
->_replyPort
) {
915 CFMachPortContext context
;
917 context
.info
= remote
;
918 context
.retain
= (const void *(*)(const void *))CFRetain
;
919 context
.release
= (void (*)(const void *))CFRelease
;
920 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
921 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
923 remote
->_convCounter
++;
924 desiredReply
= -remote
->_convCounter
;
925 sendmsg
= __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote
->_port
), (replyMode
!= NULL
? CFMachPortGetPort(remote
->_replyPort
) : MACH_PORT_NULL
), -desiredReply
, msgid
, (data
? CFDataGetBytePtr(data
) : NULL
), (data
? CFDataGetLength(data
) : -1));
927 __CFMessagePortUnlock(remote
);
929 return kCFMessagePortTransportError
;
931 if (replyMode
!= NULL
) {
932 CFDictionarySetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
, NULL
);
933 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
934 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
936 CFRunLoopAddSource(currentRL
, source
, replyMode
);
939 if (sendTimeout
< 10.0*86400) {
940 // anything more than 10 days is no timeout!
941 sendOpts
= MACH_SEND_TIMEOUT
;
942 sendTimeout
*= 1000.0;
943 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
944 sendTimeOut
= floor(sendTimeout
);
946 __CFMessagePortUnlock(remote
);
947 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->header
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
948 __CFMessagePortLock(remote
);
949 if (KERN_SUCCESS
!= ret
) {
950 // need to deallocate the send-once right that might have been created
951 if (replyMode
!= NULL
) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t
*)sendmsg
)->msgh_local_port
);
953 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
955 if (source
) CFRelease(source
);
956 __CFMessagePortUnlock(remote
);
957 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
959 return (MACH_SEND_TIMED_OUT
== ret
) ? kCFMessagePortSendTimeout
: kCFMessagePortTransportError
;
961 __CFMessagePortUnlock(remote
);
962 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
963 if (replyMode
== NULL
) {
965 return kCFMessagePortSuccess
;
967 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
968 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
970 CFRunLoopRunInMode(replyMode
, __CFTimeIntervalUntilTSR(termTSR
), true);
971 // warning: what, if anything, should be done if remote is now invalid?
972 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
973 if (NULL
!= reply
|| termTSR
< mach_absolute_time()) {
976 if (!CFMessagePortIsValid(remote
)) {
977 // no reason that reply port alone should go invalid so we don't check for that
981 // Should we uninstall the notify port? A complex question...
983 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
985 if (source
) CFRelease(source
);
987 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
989 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: kCFMessagePortBecameInvalidError
;
991 if (NULL
!= returnDatap
) {
992 *returnDatap
= ((void *)~0 == reply
) ? NULL
: reply
;
993 } else if ((void *)~0 != reply
) {
996 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
998 return kCFMessagePortSuccess
;
1001 static mach_port_t
__CFMessagePortGetPort(void *info
) {
1002 CFMessagePortRef ms
= info
;
1003 if (!ms
->_port
&& __CFMessagePortIsValid(ms
)) CFLog(kCFLogLevelWarning
, CFSTR("*** Warning: A local CFMessagePort (%p) is being put in a run loop or dispatch queue, but it has not been named yet, so this will be a no-op and no messages are going to be received, even if named later."), info
);
1004 return ms
->_port
? CFMachPortGetPort(ms
->_port
) : MACH_PORT_NULL
;
1008 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
1009 CFMessagePortRef ms
= info
;
1010 mach_msg_base_t
*msgp
= msg
;
1011 mach_msg_base_t
*replymsg
;
1013 void (*context_release
)(const void *);
1014 CFDataRef returnData
, data
= NULL
;
1015 void *return_bytes
= NULL
;
1016 CFIndex return_len
= -1;
1019 __CFMessagePortLock(ms
);
1020 if (!__CFMessagePortIsValid(ms
)) {
1021 __CFMessagePortUnlock(ms
);
1024 if (NULL
!= ms
->_context
.retain
) {
1025 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
1026 context_release
= ms
->_context
.release
;
1028 context_info
= ms
->_context
.info
;
1029 context_release
= NULL
;
1031 __CFMessagePortUnlock(ms
);
1034 int32_t byteslen
= 0;
1036 Boolean wayTooSmall
= size
< sizeof(mach_msg_header_t
) || size
< MSGP_SIZE(msgp
) || msgp
->header
.msgh_size
< MSGP_SIZE(msgp
);
1037 Boolean invalidMagic
= false;
1038 Boolean invalidComplex
= false;
1039 Boolean wayTooBig
= false;
1041 invalidMagic
= ((MSGP_INFO(msgp
, magic
) != MAGIC
) && (CFSwapInt32(MSGP_INFO(msgp
, magic
)) != MAGIC
));
1042 invalidComplex
= (msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (1 != msgp
->body
.msgh_descriptor_count
);
1043 wayTooBig
= ((int32_t)MSGP_SIZE(msgp
) + __CFMessagePortMaxInlineBytes
) < msgp
->header
.msgh_size
; // also less than a 32-bit signed int can hold
1045 Boolean wrongSize
= false;
1046 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
1047 byteslen
= CFSwapInt32LittleToHost(MSGP_INFO(msgp
, byteslen
));
1048 wrongSize
= (byteslen
< -1) || (__CFMessagePortMaxDataSize
< byteslen
);
1049 if (msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
1050 wrongSize
= wrongSize
|| (MSGP_GET(msgp
, ool
).size
!= byteslen
);
1052 wrongSize
= wrongSize
|| ((int32_t)msgp
->header
.msgh_size
- (int32_t)MSGP_SIZE(msgp
) < byteslen
);
1055 Boolean invalidMsgID
= wayTooSmall
? false : ((MSGP_INFO(msgp
, convid
) <= 0) || (INT32_MAX
< MSGP_INFO(msgp
, convid
))); // conversation id
1056 if (invalidMagic
|| invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
1057 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic
, invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
1058 mach_msg_destroy((mach_msg_header_t
*)msgp
);
1062 if (byteslen
< 0) byteslen
= 0; // from here on, treat negative same as zero
1064 /* Create no-copy, no-free-bytes wrapper CFData */
1065 if (!(msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)) {
1066 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->header
.msgh_size
);
1067 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(MSGP_INFO(msgp
, bytes
)) + byteslen
);
1068 msgid
= CFSwapInt32LittleToHost(MSGP_INFO(msgp
, msgid
));
1069 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
1070 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP_INFO(msgp
, bytes
), byteslen
, kCFAllocatorNull
);
1073 msgid
= CFSwapInt32LittleToHost(MSGP_INFO(msgp
, msgid
));
1074 data
= CFDataCreateWithBytesNoCopy(allocator
, MSGP_GET(msgp
, ool
).address
, MSGP_GET(msgp
, ool
).size
, kCFAllocatorNull
);
1077 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
1079 mach_msg_trailer_t
*trailer
= (mach_msg_trailer_t
*)(((uintptr_t)&(msgp
->header
) + msgp
->header
.msgh_size
+ sizeof(natural_t
) - 1) & ~(sizeof(natural_t
) - 1));
1080 returnData
= ms
->_calloutEx(ms
, msgid
, data
, context_info
, trailer
, 0);
1082 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1083 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1084 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1085 bytes inline in the Mach message, so can release the returnData object
1086 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1087 create a copy of the memory, which we'll have the kernel autodeallocate
1088 for us on send. In case (4) also, the bytes in the return data may be part
1089 of the bytes in "data" that we sent into the callout, so if the incoming
1090 data was received out of line, we wouldn't be able to clean up the out-of-line
1091 wad until the message was sent either, if we didn't make the copy. */
1092 if (NULL
!= returnData
) {
1093 return_len
= CFDataGetLength(returnData
);
1094 if (__CFMessagePortMaxDataSize
< return_len
) {
1095 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
1097 CFRelease(returnData
);
1100 if (returnData
&& return_len
< __CFMessagePortMaxInlineBytes
) {
1101 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
1102 } else if (returnData
) {
1103 return_bytes
= NULL
;
1104 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
1105 /* vm_copy would only be a win here if the source address
1106 is page aligned; it is a lose in all other cases, since
1107 the kernel will just do the memmove for us (but not in
1108 as simple a way). */
1109 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
1112 replymsg
= __CFMessagePortCreateMessage(true, msgp
->header
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)MSGP_INFO(msgp
, convid
), msgid
, return_bytes
, return_len
);
1113 if (replymsg
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
1114 MSGP_GET(replymsg
, ool
).deallocate
= true;
1116 if (data
) CFRelease(data
);
1117 if (msgp
->header
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
1118 vm_deallocate(mach_task_self(), (vm_address_t
)MSGP_GET(msgp
, ool
).address
, MSGP_GET(msgp
, ool
).size
);
1120 if (returnData
) CFRelease(returnData
);
1121 if (context_release
) {
1122 context_release(context_info
);
1127 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
1128 CFRunLoopSourceRef result
= NULL
;
1129 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
1130 if (!CFMessagePortIsValid(ms
)) return NULL
;
1131 if (__CFMessagePortIsRemote(ms
)) return NULL
;
1132 __CFMessagePortLock(ms
);
1133 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
1134 CFRelease(ms
->_source
);
1137 if (NULL
== ms
->_source
&& NULL
== ms
->_dispatchSource
&& __CFMessagePortIsValid(ms
)) {
1138 CFRunLoopSourceContext1 context
;
1139 context
.version
= 1;
1140 context
.info
= (void *)ms
;
1141 context
.retain
= (const void *(*)(const void *))CFRetain
;
1142 context
.release
= (void (*)(const void *))CFRelease
;
1143 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
1144 context
.equal
= NULL
;
1145 context
.hash
= NULL
;
1146 context
.getPort
= __CFMessagePortGetPort
;
1147 context
.perform
= __CFMessagePortPerform
;
1148 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
1150 if (NULL
!= ms
->_source
) {
1151 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
1153 __CFMessagePortUnlock(ms
);
1157 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms
, dispatch_queue_t queue
) {
1158 __CFGenericValidateType(ms
, CFMessagePortGetTypeID());
1159 __CFMessagePortLock(ms
);
1160 if (!__CFMessagePortIsValid(ms
)) {
1161 __CFMessagePortUnlock(ms
);
1162 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1165 if (__CFMessagePortIsRemote(ms
)) {
1166 __CFMessagePortUnlock(ms
);
1167 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1170 if (NULL
!= ms
->_source
) {
1171 __CFMessagePortUnlock(ms
);
1172 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1176 if (ms
->_dispatchSource
) {
1177 dispatch_source_cancel(ms
->_dispatchSource
);
1178 ms
->_dispatchSource
= NULL
;
1179 ms
->_dispatchQ
= NULL
;
1183 mach_port_t port
= __CFMessagePortGetPort(ms
);
1184 if (MACH_PORT_NULL
!= port
) {
1185 static dispatch_queue_t mportQueue
= NULL
;
1186 static dispatch_once_t once
;
1187 dispatch_once(&once
, ^{
1188 dispatch_queue_attr_t dqattr
= dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL
, qos_class_main(), 0);
1189 mportQueue
= dispatch_queue_create("com.apple.CFMessagePort", dqattr
);
1191 dispatch_source_t theSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, port
, 0, mportQueue
);
1192 dispatch_source_set_cancel_handler(theSource
, ^{
1193 dispatch_release(queue
);
1194 dispatch_release(theSource
);
1196 dispatch_source_set_event_handler(theSource
, ^{
1198 mach_msg_header_t
*msg
= (mach_msg_header_t
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2048, 0);
1199 msg
->msgh_size
= 2048;
1203 msg
->msgh_local_port
= port
;
1204 msg
->msgh_remote_port
= MACH_PORT_NULL
;
1207 kern_return_t ret
= mach_msg(msg
, MACH_RCV_MSG
|MACH_RCV_LARGE
|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV
), 0, msg
->msgh_size
, port
, 0, MACH_PORT_NULL
);
1208 if (MACH_MSG_SUCCESS
== ret
) break;
1209 if (MACH_RCV_TOO_LARGE
!= ret
) HALT
;
1211 uint32_t newSize
= round_msg(msg
->msgh_size
+ MAX_TRAILER_SIZE
);
1212 msg
= CFAllocatorReallocate(kCFAllocatorSystemDefault
, msg
, newSize
, 0);
1213 msg
->msgh_size
= newSize
;
1216 dispatch_async(queue
, ^{
1217 mach_msg_header_t
*reply
= __CFMessagePortPerform(msg
, msg
->msgh_size
, kCFAllocatorSystemDefault
, ms
);
1218 if (NULL
!= reply
) {
1219 kern_return_t ret
= mach_msg(reply
, MACH_SEND_MSG
, reply
->msgh_size
, 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1220 if (KERN_SUCCESS
!= ret
) mach_msg_destroy(reply
);
1221 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, reply
);
1223 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, msg
);
1227 ms
->_dispatchSource
= theSource
;
1229 if (ms
->_dispatchSource
) {
1230 dispatch_retain(queue
);
1231 ms
->_dispatchQ
= queue
;
1232 dispatch_resume(ms
->_dispatchSource
);
1234 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1237 __CFMessagePortUnlock(ms
);