diff --git a/CoreFoundation/AppServices.subproj/CFUserNotification.c b/CoreFoundation/AppServices.subproj/CFUserNotification.c index 95c6ffee37..24c7f41d39 100644 --- a/CoreFoundation/AppServices.subproj/CFUserNotification.c +++ b/CoreFoundation/AppServices.subproj/CFUserNotification.c @@ -299,6 +299,7 @@ static void _CFUserNotificationMachPortCallBack(CFMachPortRef port, void *m, CFI } SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFUserNotification, userNotification); CHECK_FOR_FORK(); SInt32 retval = ERR_SUCCESS; mach_msg_timeout_t msgtime = (timeout > 0.0 && 1000.0 * timeout < INT_MAX) ? (mach_msg_timeout_t)(1000.0 * timeout) : 0; @@ -343,11 +344,15 @@ SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, } CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFUserNotification, userNotification); CHECK_FOR_FORK(); CFStringRef retval = NULL; CFTypeRef value = NULL; if (userNotification && userNotification->_responseDictionary) { value = CFDictionaryGetValue(userNotification->_responseDictionary, key); + if (value == NULL) { + return NULL; + } if (CFGetTypeID(value) == CFStringGetTypeID()) { if (0 == idx) retval = (CFStringRef)value; } else if (CFGetTypeID(value) == CFArrayGetTypeID()) { @@ -358,11 +363,13 @@ CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotific } CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFUserNotification, userNotification); CHECK_FOR_FORK(); return userNotification ? userNotification->_responseDictionary : NULL; } SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFUserNotification, userNotification); CHECK_FOR_FORK(); SInt32 retval = ERR_SUCCESS; if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { @@ -373,6 +380,7 @@ SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeIn } SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFUserNotification, userNotification); CHECK_FOR_FORK(); SInt32 retval = ERR_SUCCESS; if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { @@ -383,6 +391,7 @@ SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) { } CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFUserNotification, userNotification); CHECK_FOR_FORK(); CFRunLoopSourceRef source = NULL; if (userNotification && callout && !userNotification->_machPort && MACH_PORT_NULL != userNotification->_replyPort) { diff --git a/CoreFoundation/Base.subproj/CFAvailability.h b/CoreFoundation/Base.subproj/CFAvailability.h index ef4b6edbb5..d790782ad3 100644 --- a/CoreFoundation/Base.subproj/CFAvailability.h +++ b/CoreFoundation/Base.subproj/CFAvailability.h @@ -135,13 +135,13 @@ #define __CF_ENUM_FIXED_IS_AVAILABLE (__cplusplus && __cplusplus >= 201103L && (__has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum))) || (!__cplusplus && (__has_feature(objc_fixed_enum) || __has_extension(cxx_fixed_enum))) #if __CF_ENUM_FIXED_IS_AVAILABLE -#define __CF_NAMED_ENUM(_type, _name) int __CF_ENUM_ ## _name; enum __CF_ENUM_ATTRIBUTES _name : _type; typedef enum _name _name; enum _name : _type +#define __CF_NAMED_ENUM(_type, _name) enum __CF_ENUM_ATTRIBUTES _name : _type _name; enum _name : _type #define __CF_ANON_ENUM(_type) enum __CF_ENUM_ATTRIBUTES : _type -#define CF_CLOSED_ENUM(_type, _name) int __CF_ENUM_ ## _name; enum __CF_CLOSED_ENUM_ATTRIBUTES _name : _type; typedef enum _name _name; enum _name : _type +#define CF_CLOSED_ENUM(_type, _name) enum __CF_CLOSED_ENUM_ATTRIBUTES _name : _type _name; enum _name : _type #if (__cplusplus) #define CF_OPTIONS(_type, _name) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _type #else -#define CF_OPTIONS(_type, _name) int __CF_OPTIONS_ ## _name; enum __CF_OPTIONS_ATTRIBUTES _name : _type; typedef enum _name _name; enum _name : _type +#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type #endif #else #define __CF_NAMED_ENUM(_type, _name) _type _name; enum diff --git a/CoreFoundation/Base.subproj/CFBase.c b/CoreFoundation/Base.subproj/CFBase.c index 6d16c85750..4560bc7fed 100644 --- a/CoreFoundation/Base.subproj/CFBase.c +++ b/CoreFoundation/Base.subproj/CFBase.c @@ -59,7 +59,7 @@ struct __CFAllocator { }; CF_INLINE uintptr_t __CFISAForCFAllocator(void) { - return __CFRuntimeObjCClassTable[_kCFRuntimeIDCFAllocator]; + return _GetCFRuntimeObjcClassAtIndex(_kCFRuntimeIDCFAllocator); } CF_INLINE CFAllocatorRetainCallBack __CFAllocatorGetRetainFunction(const CFAllocatorContext *context) { @@ -478,7 +478,7 @@ void CFAllocatorSetDefault(CFAllocatorRef allocator) { } #endif #if TARGET_OS_MAC - if (allocator && allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (allocator && _CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * return; // require allocator to this function to be an allocator } #endif @@ -505,7 +505,7 @@ static CFAllocatorRef __CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorC CFAllocatorAllocateCallBack allocateFunc; void *retainedInfo; #if TARGET_OS_MAC - if (allocator && kCFAllocatorUseContext != allocator && allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (allocator && kCFAllocatorUseContext != allocator && _CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * return NULL; // require allocator to this function to be an allocator } #endif @@ -588,7 +588,7 @@ void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags } #if defined(DEBUG) && TARGET_OS_MAC - if (allocator->_base._cfisa == __CFISAForCFAllocator()) { + if (_CFTypeGetClass(allocator) == __CFISAForCFAllocator()) { __CFGenericValidateType(allocator, _kCFRuntimeIDCFAllocator); } #else @@ -596,7 +596,7 @@ void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags #endif if (0 == size) return NULL; #if TARGET_OS_MAC - if (allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (_CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * return malloc_zone_malloc((malloc_zone_t *)allocator, size); } #endif @@ -619,7 +619,7 @@ void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize } #if defined(DEBUG) && TARGET_OS_MAC - if (allocator->_base._cfisa == __CFISAForCFAllocator()) { + if (_CFTypeGetClass(allocator) == __CFISAForCFAllocator()) { __CFGenericValidateType(allocator, _kCFRuntimeIDCFAllocator); } #else @@ -627,7 +627,7 @@ void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize #endif if (NULL == ptr && 0 < newsize) { #if TARGET_OS_MAC - if (allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (_CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * return malloc_zone_malloc((malloc_zone_t *)allocator, newsize); } #endif @@ -640,7 +640,7 @@ void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize } if (NULL != ptr && 0 == newsize) { #if TARGET_OS_MAC - if (allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (_CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * #if defined(DEBUG) size_t size = malloc_size(ptr); if (size) memset(ptr, 0xCC, size); @@ -657,7 +657,7 @@ void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize } if (NULL == ptr && 0 == newsize) return NULL; #if TARGET_OS_MAC - if (allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (_CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * return malloc_zone_realloc((malloc_zone_t *)allocator, ptr, newsize); } #endif @@ -675,14 +675,14 @@ void CFAllocatorDeallocate(CFAllocatorRef allocator, void *ptr) { } #if defined(DEBUG) && TARGET_OS_MAC - if (allocator->_base._cfisa == __CFISAForCFAllocator()) { + if (_CFTypeGetClass(allocator) == __CFISAForCFAllocator()) { __CFGenericValidateType(allocator, _kCFRuntimeIDCFAllocator); } #else __CFGenericValidateType(allocator, _kCFRuntimeIDCFAllocator); #endif #if TARGET_OS_MAC - if (allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (_CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * #if defined(DEBUG) size_t size = malloc_size(ptr); if (size) memset(ptr, 0xCC, size); @@ -704,15 +704,15 @@ CFIndex CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex siz allocator = __CFGetDefaultAllocator(); } -#if defined(DEBUG) && TARGET_OS_MAC - if (allocator->_base._cfisa == __CFISAForCFAllocator()) { +#if TARGET_OS_MAC + if (_CFTypeGetClass(allocator) == __CFISAForCFAllocator()) { __CFGenericValidateType(allocator, _kCFRuntimeIDCFAllocator); } #else __CFGenericValidateType(allocator, _kCFRuntimeIDCFAllocator); #endif #if TARGET_OS_MAC - if (allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (_CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * return malloc_good_size(size); } #endif @@ -729,8 +729,8 @@ void CFAllocatorGetContext(CFAllocatorRef allocator, CFAllocatorContext *context allocator = __CFGetDefaultAllocator(); } -#if defined(DEBUG) && TARGET_OS_MAC - if (allocator->_base._cfisa == __CFISAForCFAllocator()) { +#if TARGET_OS_MAC + if (_CFTypeGetClass(allocator) == __CFISAForCFAllocator()) { __CFGenericValidateType(allocator, _kCFRuntimeIDCFAllocator); } #else @@ -738,7 +738,7 @@ void CFAllocatorGetContext(CFAllocatorRef allocator, CFAllocatorContext *context #endif CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); #if TARGET_OS_MAC - if (allocator->_base._cfisa != __CFISAForCFAllocator()) { // malloc_zone_t * + if (_CFTypeGetClass(allocator) != __CFISAForCFAllocator()) { // malloc_zone_t * return; } #endif @@ -808,7 +808,7 @@ struct __CFNull { DECLARE_STATIC_CLASS_REF(NSNull); -static struct __CFNull _CF_CONSTANT_OBJECT_BACKING __kCFNull = { +struct __CFNull _CF_CONSTANT_OBJECT_BACKING __kCFNull = { INIT_CFRUNTIME_BASE_WITH_CLASS(NSNull, _kCFRuntimeIDCFNull) }; const CFNullRef kCFNull = &__kCFNull; @@ -853,7 +853,8 @@ void _CFRuntimeSetCFMPresent(void *addr) { /* Keep this assembly at the bottom of the source file! */ -extern void __HALT() { +extern void __HALT(void); +void __HALT() { __builtin_trap(); } diff --git a/CoreFoundation/Base.subproj/CFBase.h b/CoreFoundation/Base.subproj/CFBase.h index 8a0cbde819..5af3402164 100644 --- a/CoreFoundation/Base.subproj/CFBase.h +++ b/CoreFoundation/Base.subproj/CFBase.h @@ -10,7 +10,12 @@ #if !defined(__COREFOUNDATION_CFBASE__) #define __COREFOUNDATION_CFBASE__ 1 +#if __has_include() #include +#else +#include +#endif + #include #if (defined(__CYGWIN32__) || defined(_WIN32)) && !defined(__WIN32__) @@ -690,6 +695,23 @@ CFTypeRef CFMakeCollectable(CFTypeRef cf) CF_AUTOMATED_REFCOUNT_UNAVAILABLE; #define _CF_CONSTANT_OBJECT_STRONG_RC ((uintptr_t)_CF_SWIFT_RC_PINNED_FLAG) #endif +#if __has_include() +#include +#endif + +#ifndef __ptrauth_objc_isa_pointer +#define __ptrauth_objc_isa_pointer +#endif + +#define ISA_PTRAUTH_DISCRIMINATOR 0x6AE1 +#if defined(__ptrauth_objc_isa_uintptr) +#define __ptrauth_cf_objc_isa_pointer __ptrauth_objc_isa_uintptr +#elif defined(__arm64e__) && defined(__PTRAUTH_INTRINSICS__) && __has_feature(ptrauth_objc_isa) +#define __ptrauth_cf_objc_isa_pointer __ptrauth_restricted_intptr(ptrauth_key_objc_isa_pointer, 1, ISA_PTRAUTH_DISCRIMINATOR) +#else +#define __ptrauth_cf_objc_isa_pointer +#endif + CF_EXTERN_C_END #endif /* ! __COREFOUNDATION_CFBASE__ */ diff --git a/CoreFoundation/Base.subproj/CFFileUtilities.c b/CoreFoundation/Base.subproj/CFFileUtilities.c index 635bef707d..53596fb964 100644 --- a/CoreFoundation/Base.subproj/CFFileUtilities.c +++ b/CoreFoundation/Base.subproj/CFFileUtilities.c @@ -371,6 +371,9 @@ CF_PRIVATE CFMutableArrayRef _CFCreateContentsOfDirectory(CFAllocatorRef alloc, } } +#if TARGET_OS_MAC + struct dirent buffer; +#endif struct dirent *dp; int err; @@ -387,7 +390,11 @@ CF_PRIVATE CFMutableArrayRef _CFCreateContentsOfDirectory(CFAllocatorRef alloc, } files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks); +#if TARGET_OS_MAC + while((0 == readdir_r(dirp, &buffer, &dp)) && dp) { +#else while((dp = readdir(dirp))) { +#endif CFURLRef fileURL; unsigned namelen = strlen(dp->d_name); diff --git a/CoreFoundation/Base.subproj/CFInternal.h b/CoreFoundation/Base.subproj/CFInternal.h index e2d3215c74..b737f30ec7 100644 --- a/CoreFoundation/Base.subproj/CFInternal.h +++ b/CoreFoundation/Base.subproj/CFInternal.h @@ -100,6 +100,7 @@ CF_EXTERN_C_BEGIN #include #include #include +#include "CFRuntime_Internal.h" #include #include #include @@ -121,6 +122,9 @@ CF_EXTERN_C_BEGIN #if _POSIX_THREADS #include #endif +#if __has_include() +#include +#endif #if !DEPLOYMENT_RUNTIME_SWIFT && __has_include() #include @@ -130,6 +134,7 @@ typedef struct os_log_s *os_log_t; #define os_log_info(...) do { } while (0) #define os_log_debug(...) do { } while (0) #define os_log_error(...) do { } while (0) +#define os_log_fault(...) do { } while (0) #define os_log_create(...) (NULL) #define os_log_debug_enabled(...) (0) #endif @@ -442,10 +447,12 @@ extern CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef f /* Enhanced string formatting support */ -CF_PRIVATE CFDictionaryRef _CFStringGetFormatSpecifierConfiguration(CFStringRef aFormatString); CF_PRIVATE CFStringRef _CFStringCopyWithFomatStringConfiguration(CFStringRef aFormatString, CFDictionaryRef formatConfiguration); CF_PRIVATE CFStringRef _CFCopyResolvedFormatStringWithConfiguration(CFTypeRef anObject, CFDictionaryRef aConfiguration, CFDictionaryRef formatOptions); CF_PRIVATE CFStringRef _CFStringCreateWithWidthContexts(CFDictionaryRef widthContexts); +CF_PRIVATE CFStringRef _CFStringCreateWithMarkdownAndConfiguration(CFStringRef stringWithMarkup, CFDictionaryRef configuration, CFURLRef tableURL); +CF_PRIVATE Boolean _CFStringObjCFormatRequiresInflection(CFStringRef format); +CF_PRIVATE CFStringRef _CFStringCreateFormatWithInflectionAndArguments(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments); /* result is long long or int, depending on doLonglong */ @@ -548,8 +555,6 @@ CF_EXPORT void __CFSetLastAllocationEventName(void *ptr, const char *classname); /* Comparators are passed the address of the values; this is somewhat different than CFComparatorFunction is used in public API usually. */ CF_EXPORT CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context); -CF_EXPORT CFHashCode CFHashBytes(UInt8 *bytes, CFIndex length); - CF_EXPORT CFStringEncoding CFStringFileSystemEncoding(void); CF_PRIVATE CFStringRef __CFStringCreateImmutableFunnel3(CFAllocatorRef alloc, const void *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean possiblyExternalFormat, Boolean tryToReduceUnicode, Boolean hasLengthByte, Boolean hasNullByte, Boolean noCopy, CFAllocatorRef contentsDeallocator, UInt32 converterFlags); @@ -571,52 +576,28 @@ CF_EXPORT id const __NSArray0__; #else CF_EXPORT id __NSDictionary0__; CF_EXPORT id __NSArray0__; -#endif // __OBJC2__ +#endif #include -#if __has_include() -#include -#elif _POSIX_THREADS -#define OS_UNFAIR_LOCK_INIT PTHREAD_MUTEX_INITIALIZER -typedef pthread_mutex_t os_unfair_lock; -typedef pthread_mutex_t * os_unfair_lock_t; -CF_INLINE void os_unfair_lock_lock(os_unfair_lock_t lock) { pthread_mutex_lock(lock); } -CF_INLINE void os_unfair_lock_unlock(os_unfair_lock_t lock) { pthread_mutex_unlock(lock); } -#elif defined(_WIN32) -#define OS_UNFAIR_LOCK_INIT CFLockInit -#define os_unfair_lock CFLock_t -#define os_unfair_lock_t CFLock_t * -#define os_unfair_lock_lock __CFLock -#define os_unfair_lock_unlock __CFUnlock -#endif // __has_include() - -#if __has_include() -#include -#else -// Private: -#define OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION 0 -CF_INLINE void os_unfair_lock_lock_with_options(os_unfair_lock_t lock, uint32_t options) { os_unfair_lock_lock(lock); } -#endif - #if _POSIX_THREADS typedef pthread_mutex_t _CFMutex; #define _CF_MUTEX_STATIC_INITIALIZER PTHREAD_MUTEX_INITIALIZER -static int _CFMutexCreate(_CFMutex *lock) { +CF_INLINE int _CFMutexCreate(_CFMutex *lock) { return pthread_mutex_init(lock, NULL); } -static int _CFMutexDestroy(_CFMutex *lock) { +CF_INLINE int _CFMutexDestroy(_CFMutex *lock) { return pthread_mutex_destroy(lock); } -static int _CFMutexLock(_CFMutex *lock) { +CF_INLINE int _CFMutexLock(_CFMutex *lock) { return pthread_mutex_lock(lock); } -static int _CFMutexUnlock(_CFMutex *lock) { +CF_INLINE int _CFMutexUnlock(_CFMutex *lock) { return pthread_mutex_unlock(lock); } typedef pthread_mutex_t _CFRecursiveMutex; -static int _CFRecursiveMutexCreate(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexCreate(_CFRecursiveMutex *mutex) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); @@ -627,49 +608,49 @@ static int _CFRecursiveMutexCreate(_CFRecursiveMutex *mutex) { return result; } -static int _CFRecursiveMutexDestroy(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexDestroy(_CFRecursiveMutex *mutex) { return pthread_mutex_destroy(mutex); } -static int _CFRecursiveMutexLock(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexLock(_CFRecursiveMutex *mutex) { return pthread_mutex_lock(mutex); } -static int _CFRecursiveMutexUnlock(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexUnlock(_CFRecursiveMutex *mutex) { return pthread_mutex_unlock(mutex); } #elif defined(_WIN32) typedef SRWLOCK _CFMutex; #define _CF_MUTEX_STATIC_INITIALIZER SRWLOCK_INIT -static int _CFMutexCreate(_CFMutex *lock) { +CF_INLINE int _CFMutexCreate(_CFMutex *lock) { InitializeSRWLock(lock); return 0; } -static int _CFMutexDestroy(_CFMutex *lock) { +CF_INLINE int _CFMutexDestroy(_CFMutex *lock) { (void)lock; return 0; } -static int _CFMutexLock(_CFMutex *lock) { +CF_INLINE int _CFMutexLock(_CFMutex *lock) { AcquireSRWLockExclusive(lock); return 0; } -static int _CFMutexUnlock(_CFMutex *lock) { +CF_INLINE int _CFMutexUnlock(_CFMutex *lock) { ReleaseSRWLockExclusive(lock); return 0; } typedef CRITICAL_SECTION _CFRecursiveMutex; -static int _CFRecursiveMutexCreate(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexCreate(_CFRecursiveMutex *mutex) { InitializeCriticalSection(mutex); return 0; } -static int _CFRecursiveMutexDestroy(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexDestroy(_CFRecursiveMutex *mutex) { DeleteCriticalSection(mutex); return 0; } -static int _CFRecursiveMutexLock(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexLock(_CFRecursiveMutex *mutex) { EnterCriticalSection(mutex); return 0; } -static int _CFRecursiveMutexUnlock(_CFRecursiveMutex *mutex) { +CF_INLINE int _CFRecursiveMutexUnlock(_CFRecursiveMutex *mutex) { LeaveCriticalSection(mutex); return 0; } @@ -677,6 +658,47 @@ static int _CFRecursiveMutexUnlock(_CFRecursiveMutex *mutex) { #error "do not know how to define mutex and recursive mutex for this OS" #endif +#if __has_include() +#include +#if __has_include() +#include +#define _CF_HAS_OS_UNFAIR_RECURSIVE_LOCK 1 +#else +#define os_unfair_lock_lock_with_options(lock, options) os_unfair_lock_lock(lock) +#define OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION (0) +#endif + +#elif _POSIX_THREADS +#define OS_UNFAIR_LOCK_INIT PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t os_unfair_lock; +typedef pthread_mutex_t * os_unfair_lock_t; +typedef uint32_t os_unfair_lock_options_t; +#define OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION (0) +static void os_unfair_lock_lock(os_unfair_lock_t lock) { pthread_mutex_lock(lock); } +static void os_unfair_lock_lock_with_options(os_unfair_lock_t lock, os_unfair_lock_options_t options) { pthread_mutex_lock(lock); } +static void os_unfair_lock_unlock(os_unfair_lock_t lock) { pthread_mutex_unlock(lock); } +#elif defined(_WIN32) +#define OS_UNFAIR_LOCK_INIT CFLockInit +#define os_unfair_lock CFLock_t +#define os_unfair_lock_lock __CFLock +#define os_unfair_lock_unlock __CFUnlock +#define os_unfair_lock_lock_with_options(lock, options) __CFLock(lock) +#define OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION +#endif // __has_include() + +#if defined(_CF_HAS_OS_UNFAIR_RECURSIVE_LOCK) +#undef _CF_HAS_OS_UNFAIR_RECURSIVE_LOCK // Nothing to do here. +#define _CFPerformDynamicInitOfOSRecursiveLock(lock) do {} while (0) +#else +#define os_unfair_recursive_lock _CFRecursiveMutex +#define OS_UNFAIR_RECURSIVE_LOCK_INIT { 0 } +#define _CFPerformDynamicInitOfOSRecursiveLock _CFRecursiveMutexCreate +#define os_unfair_recursive_lock_lock _CFRecursiveMutexLock +#define os_unfair_recursive_lock_lock_with_options(lock, more) _CFRecursiveMutexLock(lock) +#define os_unfair_recursive_lock_unlock _CFRecursiveMutexUnlock +#endif + + #if !__HAS_DISPATCH__ typedef volatile long dispatch_once_t; @@ -821,18 +843,96 @@ extern void _CFRuntimeSetInstanceTypeIDAndIsa(CFTypeRef cf, CFTypeID newTypeID); #define CF_SWIFT_CALLV(obj, fn, ...) (0) #endif -CF_PRIVATE CFRuntimeClass const * __CFRuntimeClassTable[__CFRuntimeClassTableSize * 2]; +#ifndef __has_attribute +#define __has_attribute(...) 0 +#endif + +#if TARGET_OS_WIN32 +#define _CF_VISIBILITY_HIDDEN_ATTRIBUTE +#elif __has_attribute(visibility) +#define _CF_VISIBILITY_HIDDEN_ATTRIBUTE __attribute__((visibility("hidden"))) +#else +#define _CF_VISIBILITY_HIDDEN_ATTRIBUTE +#endif + +typedef struct __CFClassTables { + CFRuntimeClass const * classTable[__CFRuntimeClassTableSize]; + // This can be safely `_Atomic` because we just store the signed classes; you can't sign / auth _Atomic pointers + _Atomic(uintptr_t) objCClassTable[__CFRuntimeClassTableSize]; +} _CFClassTables; + +// IMPORTANT: 'heap' and other memory tools look up this symbol by name. Even though it is not exported, the name is ABI. Changes must be coordinated with them. +CF_PRIVATE _CFClassTables __CFRuntimeClassTables; + +#define __CFRuntimeClassTable __CFRuntimeClassTables.classTable +#define __CFRuntimeObjCClassTable __CFRuntimeClassTables.objCClassTable + +#if __has_feature(ptrauth_intrinsics) +__attribute__((visibility("hidden"))) +CF_INLINE uintptr_t ___CFRUNTIME_OBJC_CLASSTABLE_PTRAUTH_DISCRIMINATOR(void const * const tableSlotAddr) { + return ptrauth_blend_discriminator(tableSlotAddr, ptrauth_string_discriminator("__CFRuntimeObjCClassTable")); +} +#endif + +CF_INLINE uintptr_t _GetCFRuntimeObjcClassAtIndex(CFTypeID typeID) { + uintptr_t obj = atomic_load_explicit(&__CFRuntimeObjCClassTable[typeID], memory_order_relaxed); -#define __CFRuntimeObjCClassTable (((uintptr_t *)__CFRuntimeClassTable) + __CFRuntimeClassTableSize) +#if __has_feature(ptrauth_intrinsics) + // Auth using a discriminator that uses the address of the slot + // in __CFRuntimeObjCClassTable and a known string discriminator. + void const * const slot = &__CFRuntimeObjCClassTable[typeID]; + return (uintptr_t)ptrauth_auth_data((void *)obj, + ptrauth_key_process_dependent_data, + ___CFRUNTIME_OBJC_CLASSTABLE_PTRAUTH_DISCRIMINATOR(slot)); +#else + return (uintptr_t)obj; +#endif +} + +CF_INLINE void _SetCFRuntimeObjcClass(uintptr_t aClass, CFTypeID typeID) { + uintptr_t classToStore = aClass; + +#if __has_feature(ptrauth_intrinsics) + // validate the current entry; ignore the return value we just want to ensure our table is in a valid state before mutation + _GetCFRuntimeObjcClassAtIndex(typeID); + // If we're using ptrauth, we'll sign using a discriminator that uses the address of the slot + // in __CFRuntimeObjCClassTable and a known string discriminator. + // Later we'll auth this using the the same discriminator to ensure the table hasn't been messed with + // and that the class we've stored in the table is the one we expect it to be. + void const * const slot = &__CFRuntimeObjCClassTable[typeID]; + classToStore = (uintptr_t)ptrauth_sign_unauthenticated((void *)classToStore, + ptrauth_key_process_dependent_data, + ___CFRUNTIME_OBJC_CLASSTABLE_PTRAUTH_DISCRIMINATOR(slot)); +#endif + + atomic_store_explicit(&__CFRuntimeObjCClassTable[typeID], classToStore, memory_order_relaxed); +} CF_INLINE uintptr_t __CFISAForTypeID(CFTypeID typeID) { - return (typeID < __CFRuntimeClassTableSize) ? __CFRuntimeObjCClassTable[typeID] : 0; + if (typeID < __CFRuntimeClassTableSize) { + // There is a "race" here between CFRetain / CFRelease (which call CF_IS_OBJC) + // and _CFRuntimeBridgeClasses. Except... that because this array is + // pointer-sized, the only possible races are on access to the same index in + // both cases. + // So we have two cases: + // - if you call CF_IS_OBJC on a CF object, it means that type has been registered + // previously: no race + // - if you call CF_IS_OBJC on an objc object, and __CFGenericTypeID_inline + // interpreted some bits of the object as a type ID, we don't really care + // if the value we read is outdated or not, since we will fail the isa comparison + // in CF_IS_OBJC + + return _GetCFRuntimeObjcClassAtIndex(typeID); + } else { + return 0; + } } #define CF_OBJC_FUNCDISPATCHV(typeID, obj, ...) do { } while (0) #define CF_OBJC_RETAINED_FUNCDISPATCHV(typeID, obj, ...) do { } while (0) #define CF_OBJC_CALLV(obj, ...) (0) #define CF_IS_OBJC(typeID, obj) (0) +#define _CFTypeGetClass(obj) ((uintptr_t)((CFRuntimeBase *)obj)->_cfisa) /* See comments in CFBase.c */ @@ -1032,7 +1132,7 @@ CF_INLINE const char *CFPathRelativeToAppleFrameworksRoot(const char *path, Bool enum { DISPATCH_QUEUE_OVERCOMMIT = 0x2ull, }; -#endif // __has_include() +#endif #if TARGET_OS_LINUX || TARGET_OS_WIN32 || TARGET_OS_BSD #define QOS_CLASS_USER_INITIATED DISPATCH_QUEUE_PRIORITY_HIGH @@ -1048,7 +1148,7 @@ CF_INLINE long qos_class_self() { return QOS_CLASS_DEFAULT; } -#endif // TARGET_OS_LINUX || TARGET_OS_WIN32 +#endif // Returns a generic dispatch queue for when you want to just throw some work // into the concurrent pile to execute, and don't care about specifics except @@ -1072,13 +1172,12 @@ CF_INLINE dispatch_queue_t __CFDispatchQueueGetGenericBackground(void) { return dispatch_get_global_queue(QOS_CLASS_UTILITY, DISPATCH_QUEUE_OVERCOMMIT); } -CF_PRIVATE dispatch_data_t _CFDataCreateDispatchData(CFDataRef data); //avoids copying in most cases - -#endif // __HAS_DISPATCH__ +#endif CF_PRIVATE CFStringRef _CFStringCopyBundleUnloadingProtectedString(CFStringRef str); CF_PRIVATE uint8_t *_CFDataGetBytePtrNonObjC(CFDataRef data); +CF_PRIVATE dispatch_data_t _CFDataCreateDispatchData(CFDataRef data); //avoids copying in most cases // Use this for functions that are intended to be breakpoint hooks. If you do not, the compiler may optimize them away. // Based on: BREAKPOINT_FUNCTION in objc-os.h @@ -1134,7 +1233,7 @@ CF_EXTERN_C_END // processor and the compiler will convert these byte accesses into the appropiate DWORD/QWORD memory // access. -CF_INLINE uint32_t unaligned_load32(const void *ptr) { +CF_INLINE uint32_t _CFUnalignedLoad32(const void *ptr) { uint8_t *bytes = (uint8_t *)ptr; #if __LITTLE_ENDIAN__ uint32_t result = (uint32_t)bytes[0]; @@ -1151,7 +1250,7 @@ CF_INLINE uint32_t unaligned_load32(const void *ptr) { } -CF_INLINE void unaligned_store32(void *ptr, uint32_t value) { +CF_INLINE void _CFUnalignedStore32(void *ptr, uint32_t value) { uint8_t *bytes = (uint8_t *)ptr; #if __LITTLE_ENDIAN__ bytes[0] = (uint8_t)(value & 0xff); @@ -1168,7 +1267,7 @@ CF_INLINE void unaligned_store32(void *ptr, uint32_t value) { // Load values stored in Big Endian order in memory. -CF_INLINE uint16_t unaligned_load16be(const void *ptr) { +CF_INLINE uint16_t _CFUnalignedLoad16BE(const void *ptr) { uint8_t *bytes = (uint8_t *)ptr; uint16_t result = (uint16_t)bytes[0] << 8; result |= (uint16_t)bytes[1]; @@ -1177,7 +1276,7 @@ CF_INLINE uint16_t unaligned_load16be(const void *ptr) { } -CF_INLINE uint32_t unaligned_load32be(const void *ptr) { +CF_INLINE uint32_t _CFUnalignedLoad32BE(const void *ptr) { uint8_t *bytes = (uint8_t *)ptr; uint32_t result = (uint32_t)bytes[0] << 24; result |= ((uint32_t)bytes[1] << 16); @@ -1188,7 +1287,7 @@ CF_INLINE uint32_t unaligned_load32be(const void *ptr) { } -CF_INLINE uint64_t unaligned_load64be(const void *ptr) { +CF_INLINE uint64_t _CFUnalignedLoad64BE(const void *ptr) { uint8_t *bytes = (uint8_t *)ptr; uint64_t result = (uint64_t)bytes[0] << 56; result |= ((uint64_t)bytes[1] << 48); diff --git a/CoreFoundation/Base.subproj/CFLocking.h b/CoreFoundation/Base.subproj/CFLocking.h index 10da46f2cd..b797c1aff8 100644 --- a/CoreFoundation/Base.subproj/CFLocking.h +++ b/CoreFoundation/Base.subproj/CFLocking.h @@ -1,4 +1,4 @@ -/* CFInternal.h +/* CFLocking.h Copyright (c) 1998-2018, Apple Inc. and the Swift project authors Portions Copyright (c) 2014-2018, Apple Inc. and the Swift project authors @@ -14,7 +14,11 @@ #if !defined(__COREFOUNDATION_CFLOCKING_H__) #define __COREFOUNDATION_CFLOCKING_H__ 1 +#if __has_include() #include +#else +#include +#endif #if TARGET_OS_MAC diff --git a/CoreFoundation/Base.subproj/CFPlatform.c b/CoreFoundation/Base.subproj/CFPlatform.c index 74cec94520..bc348187f6 100644 --- a/CoreFoundation/Base.subproj/CFPlatform.c +++ b/CoreFoundation/Base.subproj/CFPlatform.c @@ -273,7 +273,10 @@ CF_PRIVATE CFStringRef _CFProcessNameString(void) { const char *processName = *_CFGetProgname(); if (!processName) processName = ""; CFStringRef newStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, processName, kCFPlatformInterfaceStringEncoding); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *) newStr, (void * volatile *)& __CFProcessNameString)) { +#pragma GCC diagnostic pop CFRelease(newStr); // someone else made the assignment, so just release the extra string. } } diff --git a/CoreFoundation/Base.subproj/CFPriv.h b/CoreFoundation/Base.subproj/CFPriv.h index 40852ce078..692a106a99 100644 --- a/CoreFoundation/Base.subproj/CFPriv.h +++ b/CoreFoundation/Base.subproj/CFPriv.h @@ -52,6 +52,7 @@ #endif #include + CF_EXTERN_C_BEGIN CF_EXPORT void _CFRuntimeSetCFMPresent(void *a); @@ -251,6 +252,10 @@ CF_EXPORT const CFStringRef kCFHTTPURLStatusLine; CF_EXPORT CFStringRef CFCopySystemVersionString(void); // Human-readable string containing both marketing and build version CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); CF_EXPORT CFDictionaryRef _CFCopyServerVersionDictionary(void); + +// Returns the 'true' contents of SystemVersion.plist even when running in apps linked before 10.16 +CF_EXPORT CFDictionaryRef _CFCopySystemVersionPlatformDictionary(void) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + CF_EXPORT CFStringRef _CFCopySystemVersionDictionaryValue(CFStringRef key) API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)); CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; CF_EXPORT const CFStringRef _kCFSystemVersionProductCopyrightKey; @@ -265,6 +270,8 @@ CF_EXPORT const CFStringRef _kCFSystemVersionBuildStringKey; // Localized strin CF_EXPORT void CFMergeSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context); CF_EXPORT void CFQSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context); +CF_EXPORT CFHashCode CFHashBytes(UInt8 *bytes, CFIndex length); + // For non-Darwin platforms _CFExecutableLinkedOnOrAfter(…) always returns true. typedef CF_ENUM(CFIndex, CFSystemVersion) { @@ -283,7 +290,6 @@ typedef CF_ENUM(CFIndex, CFSystemVersion) { CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version); - typedef CF_ENUM(CFIndex, CFStringCharacterClusterType) { kCFStringGraphemeCluster = 1, /* Unicode Grapheme Cluster */ kCFStringComposedCharacterCluster = 2, /* Compose all non-base (including spacing marks) */ @@ -569,7 +575,6 @@ CF_EXPORT CFHashCode _CFNonObjCHash(CFTypeRef cf); CF_EXPORT Boolean CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(CFStringRef localeIdentifier, LangCode *langCode, RegionCode *regCode, ScriptCode *scriptCode, CFStringEncoding *stringEncoding); -CF_EXPORT void _CFLocaleResetCurrent(void); CF_EXPORT void _CFCalendarResetCurrent(void); #if TARGET_OS_WIN32 @@ -710,6 +715,12 @@ CF_EXPORT CFStringRef _CFXDGCreateRuntimeDirectoryPath(void) CF_RETURNS_RETAINED #endif // !DEPLOYMENT_RUNTIME_OBJC +/// Retrieve a local handle for an inserted (DYLD_INSERT_LIBRARIES) or interposing library. +CF_EXPORT void * _CFGetHandleForInsertedOrInterposingLibrary(char const *namePrefix) API_AVAILABLE(ios(13.0), macos(10.15), watchos(6.0), tvos(13.0)); + +CF_EXPORT Boolean _CFRunLoopPerCalloutAutoreleasepoolEnabled(void) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); +CF_EXPORT Boolean _CFRunLoopSetPerCalloutAutoreleasepoolEnabled(Boolean enabled) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + CF_EXTERN_C_END #endif /* ! __COREFOUNDATION_CFPRIV__ */ diff --git a/CoreFoundation/Base.subproj/CFRuntime.c b/CoreFoundation/Base.subproj/CFRuntime.c index 07761c0fc3..0e0c2febb5 100644 --- a/CoreFoundation/Base.subproj/CFRuntime.c +++ b/CoreFoundation/Base.subproj/CFRuntime.c @@ -190,7 +190,9 @@ CF_PRIVATE void objc_terminate(void) { // the lock does not protect most reading of these; we just leak the old table to allow read-only accesses to continue to work static os_unfair_lock __CFBigRuntimeFunnel = OS_UNFAIR_LOCK_INIT; -CFRuntimeClass const * __CFRuntimeClassTable[__CFRuntimeClassTableSize * 2] __attribute__((aligned)) = { + +_CFClassTables __CFRuntimeClassTables __attribute__((aligned)) = { + .classTable = { [_kCFRuntimeIDNotAType] = &__CFNotATypeClass, [_kCFRuntimeIDCFType] = &__CFTypeClass, [_kCFRuntimeIDCFAllocator] = &__CFAllocatorClass, @@ -254,7 +256,10 @@ CFRuntimeClass const * __CFRuntimeClassTable[__CFRuntimeClassTableSize * 2] __at [_kCFRuntimeIDCFURLComponents] = &__CFURLComponentsClass, [_kCFRuntimeIDCFDateComponents] = &__CFDateComponentsClass, - + [_kCFRuntimeIDCFRelativeDateTimeFormatter] = &__CFRelativeDateTimeFormatterClass, + [_kCFRuntimeIDCFListFormatter] = &__CFListFormatterClass, + }, + .objCClassTable = {0} }; static int32_t __CFRuntimeClassTableCount = _kCFRuntimeStartingClassID; @@ -601,7 +606,7 @@ void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID newTypeID) { CF_PRIVATE void _CFRuntimeSetInstanceTypeIDAndIsa(CFTypeRef cf, CFTypeID newTypeID) { _CFRuntimeSetInstanceTypeID(cf, newTypeID); #if DEPLOYMENT_RUNTIME_SWIFT - if (((CFRuntimeBase *)cf)->_cfisa != __CFISAForTypeID(newTypeID)) { + if (_CFTypeGetClass(cf) != __CFISAForTypeID(newTypeID)) { ((CFSwiftRef)cf)->isa = (uintptr_t)__CFISAForTypeID(newTypeID); } #endif @@ -720,6 +725,31 @@ CFTypeID CFGetTypeID(CFTypeRef cf) { return __CFGenericTypeID_inline(cf); } +CF_PRIVATE CFTypeID _CFGetNonObjCTypeID(CFTypeRef cf) { + __CFGenericAssertIsCF(cf); + return __CFGenericTypeID_inline(cf); +} + +static const char *const _CFGetTypeIDDescription(CFTypeID type) { + if (type < __CFRuntimeClassTableCount && + NULL != __CFRuntimeClassTable[type] && + _kCFRuntimeIDNotAType != type && + _kCFRuntimeIDCFType != type) { + return __CFRuntimeClassTable[type]->className; + } else { + return NULL; + } +} + +__attribute__((cold, noinline, noreturn, not_tail_called)) +CF_PRIVATE void _CFAssertMismatchedTypeID(CFTypeID expected, CFTypeID actual) { + char msg[255]; + const char *const expectedName = _CFGetTypeIDDescription(expected) ?: ""; + const char *const actualName = _CFGetTypeIDDescription(actual) ?: ""; + snprintf(msg, 255, "Expected typeID %lu (%s) does not match actual typeID %lu (%s)", expected, expectedName, actual, actualName); + HALT_MSG(msg); +} + CF_INLINE CFTypeID __CFGenericTypeID_inline(const void *cf) { return __CFTypeIDFromInfo(atomic_load(&(((CFRuntimeBase *)cf)->_cfinfoa))); } @@ -1119,16 +1149,19 @@ _CFThreadRef _CF_pthread_main_thread_np(void) { #if TARGET_OS_LINUX || TARGET_OS_BSD static void __CFInitialize(void) __attribute__ ((constructor)); -static #endif #if TARGET_OS_WIN32 CF_EXPORT #endif +CF_PRIVATE os_unfair_recursive_lock CFPlugInGlobalDataLock; + void __CFInitialize(void) { if (!__CFInitialized && !__CFInitializing) { __CFInitializing = 1; + // This is a no-op on Darwin, but is needed on Linux and Windows. + _CFPerformDynamicInitOfOSRecursiveLock(&CFPlugInGlobalDataLock); #if TARGET_OS_WIN32 if (!pthread_main_np()) HALT; // CoreFoundation must be initialized on the main thread @@ -1389,7 +1422,7 @@ static CFTypeRef _CFRetain(CFTypeRef cf, Boolean tryR) { refcount(+1, cf); } else { #if TARGET_RT_64_BIT - __CFInfoType newInfo; + __CFInfoType newInfo = info; do { if (__builtin_expect(tryR && (info & (RC_DEALLOCATING_BIT | RC_DEALLOCATED_BIT)), false)) { // This object is marked for deallocation @@ -1483,11 +1516,20 @@ static void _CFRelease(CFTypeRef CF_RELEASES_ARGUMENT cf) { swift_release((void *)cf); #else __CFInfoType info = atomic_load(&(((CFRuntimeBase *)cf)->_cfinfoa)); + CFTypeID typeID = __CFTypeIDFromInfo(info); if (info & RC_DEALLOCATED_BIT) { - CRSetCrashLogMessage("Detected over-release of a CFTypeRef"); + char msg[256]; + + // Extra checking of values here because we're already in memory-corruption land + if (typeID < __CFRuntimeClassTableSize) { + CFRuntimeClass const *cfClass = __CFRuntimeClassTable[typeID]; + snprintf(msg, 256, "Detected over-release of a CFTypeRef %p (%lu / %s)", cf, typeID, cfClass ? cfClass->className : "unknown"); + } else { + snprintf(msg, 256, "Detected over-release of a CFTypeRef %p (unknown type)", cf); + } + CRSetCrashLogMessage(msg); HALT; } - CFTypeID typeID = __CFTypeIDFromInfo(info); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" CFIndex start_rc = __builtin_expect(__CFOASafe, 0) ? CFGetRetainCount(cf) : 0; @@ -1647,7 +1689,7 @@ static void _CFRelease(CFTypeRef CF_RELEASES_ARGUMENT cf) { allocator = CFGetAllocator(cf); usesSystemDefaultAllocator = _CFAllocatorIsSystemDefault(allocator); - if (_kCFRuntimeIDCFAllocator != __CFGenericTypeID_inline(cf)) { + if (__kCFAllocatorTypeID_CONST != __CFGenericTypeID_inline(cf)) { allocatorToRelease = (CFAllocatorRef _Nonnull)__CFGetAllocator(cf); } } @@ -1751,7 +1793,6 @@ const char *_NSPrintForDebugger(void *cf) { if (!desc) { return ""; } - CFRelease(desc); const char *cheapResult = CFStringGetCStringPtr((CFTypeRef)cf, kCFStringEncodingUTF8); if (cheapResult) { return cheapResult; diff --git a/CoreFoundation/Base.subproj/CFRuntime.h b/CoreFoundation/Base.subproj/CFRuntime.h index 17e58b80af..13efc68a95 100644 --- a/CoreFoundation/Base.subproj/CFRuntime.h +++ b/CoreFoundation/Base.subproj/CFRuntime.h @@ -14,6 +14,18 @@ #include #include +#if __has_include() +#include +#endif + +#ifndef __ptrauth_cf_objc_isa_pointer +#define __ptrauth_cf_objc_isa_pointer +#endif + +#ifndef __ptrauth_objc_isa_pointer +#define __ptrauth_objc_isa_pointer +#endif + CF_EXTERN_C_BEGIN #if (TARGET_OS_MAC && !TARGET_OS_IPHONE && !__x86_64h__) @@ -192,7 +204,7 @@ CF_EXPORT void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID); typedef struct __attribute__((__aligned__(8))) __CFRuntimeBase { // This matches the isa and retain count storage in Swift - uintptr_t _cfisa; + __ptrauth_cf_objc_isa_pointer uintptr_t _cfisa; uintptr_t _swift_rc; // This is for CF's use, and must match __NSCFType/_CFInfo layout _Atomic(uint64_t) _cfinfoa; @@ -201,10 +213,9 @@ typedef struct __attribute__((__aligned__(8))) __CFRuntimeBase { #define INIT_CFRUNTIME_BASE(...) {0, _CF_CONSTANT_OBJECT_STRONG_RC, 0x0000000000000080ULL} #else - typedef struct __CFRuntimeBase { - uintptr_t _cfisa; -#if TARGET_RT_64_BIT + __ptrauth_cf_objc_isa_pointer uintptr_t _cfisa; +#if defined(__LP64__) || defined(__LLP64__) _Atomic(uint64_t) _cfinfoa; #else _Atomic(uint32_t) _cfinfoa; diff --git a/CoreFoundation/Base.subproj/CFRuntime_Internal.h b/CoreFoundation/Base.subproj/CFRuntime_Internal.h index edfe7b9fa8..d3c4e79e6b 100644 --- a/CoreFoundation/Base.subproj/CFRuntime_Internal.h +++ b/CoreFoundation/Base.subproj/CFRuntime_Internal.h @@ -20,14 +20,14 @@ enum { _kCFRuntimeIDCFAllocator = 2, _kCFRuntimeIDCFBasicHash = 3, _kCFRuntimeIDCFBag = 4, - _kCFRuntimeIDCFString = 7, + _kCFRuntimeIDCFString = 7, // Note: value copied into NSKeyedArchiver _kCFRuntimeIDCFNull = 16, _kCFRuntimeIDCFSet = 17, - _kCFRuntimeIDCFDictionary = 18, - _kCFRuntimeIDCFArray = 19, - _kCFRuntimeIDCFData = 20, - _kCFRuntimeIDCFBoolean = 21, - _kCFRuntimeIDCFNumber = 22, + _kCFRuntimeIDCFDictionary = 18, // Note: value copied into NSKeyedArchiver + _kCFRuntimeIDCFArray = 19, // Note: value copied into NSKeyedArchiver + _kCFRuntimeIDCFData = 20, // Note: value copied into NSKeyedArchiver + _kCFRuntimeIDCFBoolean = 21, // Note: value copied into NSKeyedArchiver + _kCFRuntimeIDCFNumber = 22, // Note: value copied into NSKeyedArchiver _kCFRuntimeIDCFBinaryHeap = 23, _kCFRuntimeIDCFBitVector = 24, _kCFRuntimeIDCFCharacterSet = 25, @@ -47,7 +47,7 @@ enum { _kCFRuntimeIDCFReadStream = 38, _kCFRuntimeIDCFWriteStream = 39, _kCFRuntimeIDCFKeyedArchiverUID = 41, - _kCFRuntimeIDCFDate = 42, + _kCFRuntimeIDCFDate = 42, // Note: value copied into NSKeyedArchiver _kCFRuntimeIDCFRunLoop = 43, _kCFRuntimeIDCFRunLoopMode = 44, _kCFRuntimeIDCFRunLoopObserver = 45, @@ -77,6 +77,8 @@ enum { _kCFRuntimeIDCFAttributedString = 62, _kCFRuntimeIDCFRunArray = 63, _kCFRuntimeIDCFDateComponents = 66, + _kCFRuntimeIDCFRelativeDateTimeFormatter = 67, + _kCFRuntimeIDCFListFormatter = 68, _kCFRuntimeIDCFDateIntervalFormatter = 69, // If you are adding a new value, it goes below this line.: @@ -85,6 +87,36 @@ enum { _kCFRuntimeStartingClassID }; +CF_PRIVATE CFTypeID _CFGetNonObjCTypeID(CFTypeRef cf); + +__attribute__((cold, noinline, noreturn, not_tail_called)) +CF_PRIVATE void _CFAssertMismatchedTypeID(CFTypeID expected, CFTypeID actual); + +/// CF Maintainers: +/// Use this assert in CoreFoundation functions that directly dereference `cf` to get at its ivars. +/// Do not use this assert before calling a CF API on that type if you do not own it. +/// It is only for use in CF types that are not bridged and may never be nil. +CF_INLINE void CF_ASSERT_TYPE(CFTypeID expected, CFTypeRef cf) { + CFTypeID actual = _CFGetNonObjCTypeID(cf); + if (__builtin_expect(expected != actual, false)) { + _CFAssertMismatchedTypeID(expected, actual); + } +} + +/// CF Maintainers: +/// Use this assert in CoreFoundation functions that directly dereference `cf` to get at its ivars. +/// Do not use this assert before calling a CF API on that type if you do not own it. +/// It is only for use in CF types that are not bridged and may be nil. +CF_INLINE void CF_ASSERT_TYPE_OR_NULL(CFTypeID expected, CFTypeRef /*_Nullable*/ cf) { + if (cf == ((void*)0)) { + return; + } + CFTypeID actual = _CFGetNonObjCTypeID(cf); + if (__builtin_expect(expected != actual, false)) { + _CFAssertMismatchedTypeID(expected, actual); + } +} + CF_PRIVATE const CFRuntimeClass __CFAllocatorClass; CF_PRIVATE const CFRuntimeClass __CFBasicHashClass; CF_PRIVATE const CFRuntimeClass __CFBagClass; @@ -144,6 +176,8 @@ CF_PRIVATE const CFRuntimeClass __CFWindowsNamedPipeClass; CF_PRIVATE const CFRuntimeClass __CFTimeZoneClass; CF_PRIVATE const CFRuntimeClass __CFCalendarClass; CF_PRIVATE const CFRuntimeClass __CFDateComponentsClass; +CF_PRIVATE const CFRuntimeClass __CFRelativeDateTimeFormatterClass; +CF_PRIVATE const CFRuntimeClass __CFListFormatterClass; CF_PRIVATE const CFRuntimeClass __CFDateIntervalFormatterClass; #pragma mark - Private initialiers to run at process start time @@ -159,5 +193,4 @@ CF_PRIVATE void __CFTSDWindowsInitialize(void); CF_PRIVATE void __CFXPreferencesInitialize(void); #endif - #endif /* CFRuntime_Internal_h */ diff --git a/CoreFoundation/Base.subproj/CFSystemDirectories.c b/CoreFoundation/Base.subproj/CFSystemDirectories.c index 6df0d01070..a480903710 100644 --- a/CoreFoundation/Base.subproj/CFSystemDirectories.c +++ b/CoreFoundation/Base.subproj/CFSystemDirectories.c @@ -27,9 +27,6 @@ #include #include #include -#if __has_include() -#include -#endif // For now, CFSystemDirectories SPIs are exactly equivalent to (or at least a subset of) sysdir's. NSSearchPath* APIs in Foundation are not a subset of sysdir, so don't attempt to push that functionality down here without accommodating the differences. #define CFSearchPathToSysdir(dir) ((sysdir_search_path_directory_t)dir) diff --git a/CoreFoundation/Base.subproj/CFUUID.c b/CoreFoundation/Base.subproj/CFUUID.c index 6b64258dc5..73e819345d 100644 --- a/CoreFoundation/Base.subproj/CFUUID.c +++ b/CoreFoundation/Base.subproj/CFUUID.c @@ -293,6 +293,8 @@ CFUUIDRef CFUUIDCreateFromString(CFAllocatorRef alloc, CFStringRef uuidStr) { } CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFUUID, uuid); + CFMutableStringRef str = CFStringCreateMutable(alloc, 0); UniChar buff[12]; @@ -363,6 +365,7 @@ CFUUIDRef CFUUIDGetConstantUUIDWithBytes(CFAllocatorRef alloc, uint8_t byte0, ui } CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFUUID, uuid); return uuid->_bytes; } diff --git a/CoreFoundation/Base.subproj/CFUtilities.c b/CoreFoundation/Base.subproj/CFUtilities.c index ece51868ab..7c3fc9f8e7 100644 --- a/CoreFoundation/Base.subproj/CFUtilities.c +++ b/CoreFoundation/Base.subproj/CFUtilities.c @@ -392,6 +392,26 @@ CFDictionaryRef _CFCopySystemVersionDictionary(void) { } } +CFDictionaryRef _CFCopySystemVersionPlatformDictionary(void) { + static dispatch_once_t onceToken; + static CFDictionaryRef result = NULL; + dispatch_once(&onceToken, ^{ + CFStringRef path = copySystemVersionPath(CFSTR("/System/Library/CoreServices/.SystemVersionPlatform.plist")); + CFPropertyListRef plist = _CFCopyVersionDictionary(path); + CFRelease(path); + if (!plist) { + // Fall back to the normal one in case the .SystemVersionPlatform.plist symlink is missing + plist = _CFCopySystemVersionDictionary(); + } + result = (CFDictionaryRef)plist; + }); + if (result) { + return CFRetain(result); + } else { + return NULL; + } +} + CFDictionaryRef _CFCopyServerVersionDictionary(void) { static dispatch_once_t onceToken; static CFDictionaryRef result = NULL; @@ -418,7 +438,8 @@ static CFOperatingSystemVersion _CFCalculateOSVersion(void) { versionStruct.patchVersion = windowsVersion.dwBuildNumber; } #else - CFStringRef productVersion = _CFCopySystemVersionDictionaryValue(_kCFSystemVersionProductVersionKey); + CFStringRef resolvedProductVersionKey = _kCFSystemVersionProductVersionKey; + CFStringRef productVersion = _CFCopySystemVersionDictionaryValue(resolvedProductVersionKey); if (productVersion) { CFArrayRef components = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, productVersion, CFSTR(".")); if (components) { @@ -586,7 +607,7 @@ CF_PRIVATE CFIndex __CFProcessorCount() { CF_PRIVATE uint64_t __CFMemorySize() { uint64_t memsize = 0; #if TARGET_OS_MAC - int32_t mib[] = {CTL_HW, HW_NCPU}; + int32_t mib[] = {CTL_HW, HW_MEMSIZE}; size_t len = sizeof(memsize); int32_t result = sysctl(mib, sizeof(mib) / sizeof(int32_t), &memsize, &len, NULL, 0); if (result != 0) { diff --git a/CoreFoundation/Base.subproj/CoreFoundation_Prefix.h b/CoreFoundation/Base.subproj/CoreFoundation_Prefix.h index 35e08036d8..956b5b0524 100644 --- a/CoreFoundation/Base.subproj/CoreFoundation_Prefix.h +++ b/CoreFoundation/Base.subproj/CoreFoundation_Prefix.h @@ -12,12 +12,11 @@ #if __has_include() #include +#define __TARGETCONDITIONALS__ // Prevent loading the macOS TargetConditionals.h at all. #else #include #endif -#define _DARWIN_UNLIMITED_SELECT 1 - #include #if TARGET_OS_WASI @@ -26,6 +25,41 @@ #define __HAS_DISPATCH__ 1 #endif +// Darwin may or may not define these macros, but we rely on them for building in Swift; define them privately. +#ifndef TARGET_OS_LINUX +#define TARGET_OS_LINUX 0 +#endif +#ifndef TARGET_OS_BSD +#define TARGET_OS_BSD 0 +#endif +#ifndef TARGET_OS_ANDROID +#define TARGET_OS_ANDROID 0 +#endif +#ifndef TARGET_OS_CYGWIN +#define TARGET_OS_CYGWIN 0 +#endif +#ifndef TARGET_OS_WASI +#define TARGET_OS_WASI 0 +#endif +#ifndef TARGET_OS_MAC +#define TARGET_OS_MAC 0 +#endif +#ifndef TARGET_OS_IPHONE +#define TARGET_OS_IPHONE 0 +#endif +#ifndef TARGET_OS_OSX +#define TARGET_OS_OSX 0 +#endif +#ifndef TARGET_OS_IOS +#define TARGET_OS_IOS 0 +#endif +#ifndef TARGET_OS_TV +#define TARGET_OS_TV 0 +#endif +#ifndef TARGET_OS_WATCH +#define TARGET_OS_WATCH 0 +#endif + #include @@ -406,7 +440,7 @@ CF_INLINE int popcountll(long long x) { #endif #if !defined(CF_PRIVATE) -#define CF_PRIVATE extern __attribute__((__visibility__("hidden"))) +#define CF_PRIVATE __attribute__((__visibility__("hidden"))) extern #endif // [FIXED_35517899] We can't currently support this, but would like to leave things annotated diff --git a/CoreFoundation/Base.subproj/ForFoundationOnly.h b/CoreFoundation/Base.subproj/ForFoundationOnly.h index 99407b783d..1f80febdff 100644 --- a/CoreFoundation/Base.subproj/ForFoundationOnly.h +++ b/CoreFoundation/Base.subproj/ForFoundationOnly.h @@ -74,11 +74,41 @@ CF_EXPORT void *_Nonnull __CFSafelyReallocate(void * _Nullable destination, size CF_EXPORT void *_Nonnull __CFSafelyReallocateWithAllocator(CFAllocatorRef _Nullable, void * _Nullable destination, size_t newCapacity, CFOptionFlags options, void (^_Nullable reallocationFailureHandler)(void *_Nonnull original, bool *_Nonnull outRecovered)); #endif +Boolean __CFAllocatorRespectsHintZeroWhenAllocating(CFAllocatorRef _Nullable allocator); +typedef CF_ENUM(CFOptionFlags, _CFAllocatorHint) { + _CFAllocatorHintZeroWhenAllocating = 1 +}; + +// Arguments to these are id, but this header is non-Objc +#ifdef __OBJC__ +#define NSISARGTYPE id _Nullable +#else +#define NSISARGTYPE void * _Nullable +#define BOOL _Bool +#endif + +CF_EXPORT BOOL _NSIsNSArray(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSData(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSDate(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSDictionary(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSObject(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSOrderedSet(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSNumber(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSSet(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSString(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSTimeZone(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSValue(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSCFConstantString(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSIndexSet(NSISARGTYPE arg); +CF_EXPORT BOOL _NSIsNSAttributedString(NSISARGTYPE arg); + #if !TARGET_OS_WASI #pragma mark - CFBundle #include +#define _CFBundleDefaultStringTableName CFSTR("Localizable") + _CF_EXPORT_SCOPE_BEGIN CF_EXPORT const CFStringRef _kCFBundleExecutablePathKey; @@ -116,6 +146,28 @@ CF_INLINE Boolean __CFisEqualUUIDBytes(const void * const lhs, const void * cons return equal == 0; } +/// Compares UUID bytes order using a secure constant-time comparison +/// Ensure that `lhs` and `rhs` are 128-bytes (`CFUUIDBytes` or `uuid_t`) for the comparison to be valid. +CF_INLINE int __CFCompareUUIDBytes(const void * const lhs, const void * const rhs) { + unsigned char lhsCharBytes[16]; + memcpy(lhsCharBytes, lhs, sizeof(lhsCharBytes)); + + unsigned char rhsCharBytes[16]; + memcpy(rhsCharBytes, rhs, sizeof(rhsCharBytes)); + + int result = 0, diff; + for (int i = sizeof(lhsCharBytes) - 1; i >= 0; i--) { + diff = lhsCharBytes[i] - rhsCharBytes[i]; + // Constant time, no branching equivalent of + // if (diff != 0) { + // result = diff; + // } + result = (result & -!diff) | diff; + } + + return result; +} + _CF_EXPORT_SCOPE_END #pragma mark - CFPreferences @@ -296,6 +348,12 @@ CF_EXPORT Boolean __CFStringDecodeByteStream3(const UInt8 *bytes, CFIndex len, C */ CF_EXPORT CFStringEncodingCheapEightBitToUnicodeProc __CFCharToUniCharFunc; +/* Built-in constant char to unichar tables that help avoid dynamic allocation and dirtying of memory in common scenarios */ +#if TARGET_OS_OSX || TARGET_OS_IPHONE +CF_EXPORT UniChar const __CFMacRomanCharToUnicharTable[256]; +#endif +CF_EXPORT UniChar const __CFIdempotentCharToUniCharTable[256]; + /* Character class functions UnicodeData-2_1_5.txt */ CF_INLINE Boolean __CFIsWhitespace(UniChar theChar) { @@ -349,6 +407,17 @@ CF_EXPORT void _CFStringAppendFormatAndArgumentsAux2(CFMutableStringRef outputSt CF_EXPORT CFStringRef _Nullable _CFStringCreateWithFormatAndArgumentsAux2(CFAllocatorRef _Nullable alloc, CFStringRef _Nonnull (*_Nullable copyDescFunc)(void *, const void *loc), CFStringRef _Nonnull (*_Nullable contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef _Nullable formatOptions, CFStringRef format, va_list arguments); CF_EXPORT CFStringRef _Nullable CFStringCreateStringWithValidatedFormat(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef validFormatSpecifiers, CFStringRef format, va_list arguments, CFErrorRef _Nullable *_Nullable errorPtr) API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0)); +CF_EXPORT CFDictionaryRef _Nullable _CFStringGetFormatSpecifierConfiguration(CFStringRef aFormatString); + +CF_EXPORT CFStringRef const _kCFStringFormatMetadataReplacementIndexKey; +CF_EXPORT CFStringRef const _kCFStringFormatMetadataSpecifierRangeLocationInFormatStringKey; +CF_EXPORT CFStringRef const _kCFStringFormatMetadataSpecifierRangeLengthInFormatStringKey; +CF_EXPORT CFStringRef const _kCFStringFormatMetadataReplacementRangeLocationKey; +CF_EXPORT CFStringRef const _kCFStringFormatMetadataReplacementRangeLengthKey; +CF_EXPORT CFStringRef const _kCFStringFormatMetadataArgumentObjectKey; +CF_EXPORT CFStringRef const _kCFStringFormatMetadataArgumentNumberKey; +CF_EXPORT CFStringRef _Nullable _CFStringCreateWithFormatAndArgumentsReturningMetadata(CFAllocatorRef _Nullable alloc, CFStringRef _Nonnull (*_Nullable copyDescFunc)(void *, const void *loc), CFStringRef _Nonnull (*_Nullable contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef _Nullable formatOptions, CFDictionaryRef _Nullable formatConfiguration, CFStringRef format, CFArrayRef _Nullable *_Nullable outMetadata, va_list arguments); + /* For NSString (and NSAttributedString) usage, mutate with isMutable check */ enum {_CFStringErrNone = 0, _CFStringErrNotMutable = 1, _CFStringErrNilArg = 2, _CFStringErrBounds = 3}; @@ -369,7 +438,6 @@ CF_EXPORT CFHashCode CFStringHashISOLatin1CString(const uint8_t *bytes, CFIndex CF_EXPORT CFHashCode CFStringHashCString(const uint8_t *bytes, CFIndex len); CF_EXPORT CFHashCode CFStringHashCharacters(const UniChar *characters, CFIndex len); CF_EXPORT CFHashCode CFStringHashNSString(CFStringRef str); -CF_EXPORT CFHashCode CFHashBytes(uint8_t *_Nullable bytes, CFIndex length); _CF_EXPORT_SCOPE_END @@ -502,6 +570,7 @@ CF_EXPORT void _CFArraySetCapacity(CFMutableArrayRef array, CFIndex cap); CF_EXPORT void _CFBagSetCapacity(CFMutableBagRef bag, CFIndex cap); CF_EXPORT void _CFDictionarySetCapacity(CFMutableDictionaryRef dict, CFIndex cap); CF_EXPORT void _CFSetSetCapacity(CFMutableSetRef set, CFIndex cap); +CF_EXPORT CFIndex _CFBagGetUniqueCount(CFBagRef hc); CF_EXPORT const void *_CFArrayCheckAndGetValueAtIndex(CFArrayRef array, CFIndex idx, Boolean *outOfBounds); CF_EXPORT void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void *_Nullable * _Nullable newValues, CFIndex newCount); @@ -537,7 +606,7 @@ CF_INLINE CFHashCode _CFHashInt(long i) { CF_INLINE CFHashCode _CFHashDouble(const double d) { const double positive = (d < 0) ? -d : d; const double positiveInt = floor(positive + 0.5); - const double fractional = (positive - positiveInt) * (double)ULONG_MAX; + const double fractional = (positive - positiveInt) * (double)ULONG_MAX; // Casting `ULONG_MAX` to 'double' changes value from `18446744073709551615` to `18446744073709551616` [-Wimplicit-int-float-conversion] CFHashCode result = HASHFACTOR * (CFHashCode)fmod(positiveInt, (double)ULONG_MAX); if (fractional < 0) { // UBSan: Certain negative floating-point numbers are unrepresentable as 'unsigned long' which enters into undefined behavior territory in C. Thus we ensure it is positive, cast and then subtract as an integer where numbers behave correctly. @@ -666,12 +735,6 @@ CF_EXPORT CFOperatingSystemVersion _CFOperatingSystemVersionGetCurrent(void); CF_EXPORT Boolean _CFOperatingSystemVersionIsAtLeastVersion(CFOperatingSystemVersion version); -CF_CROSS_PLATFORM_EXPORT Boolean _CFCalendarInitWithIdentifier(CFCalendarRef calendar, CFStringRef identifier); -CF_EXPORT Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int32_t *vector, int32_t count); -CF_EXPORT Boolean _CFCalendarDecomposeAbsoluteTimeV(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, int32_t *_Nonnull * _Nonnull vector, int32_t count); -CF_EXPORT Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, int32_t *vector, int32_t count); -CF_EXPORT Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, int32_t *_Nonnull * _Nonnull vector, int32_t count); - CF_CROSS_PLATFORM_EXPORT Boolean _CFLocaleInit(CFLocaleRef locale, CFStringRef identifier); /// Returns a result similar to `CFLocaleCopyPreferredLanguages` but by specifically reading the preferences for `kCFPreferencesCurrentUser` as opposed to walking up the preferences chain. This is needed by specific callers (e.g. `+[NSLocale setPreferredLanguages:]`) to check whether the defaults being set have changed from what’s already set. @@ -757,5 +820,61 @@ CF_EXPORT void *_CFCreateArrayStorage(size_t numPointers, Boolean zeroed, size_t _CF_EXPORT_SCOPE_END +#if __OBJC__ + +#define _scoped_id_array(N, C, Z, S) \ + size_t N ## _count__ = (C); \ + if (N ## _count__ > LONG_MAX / sizeof(id)) { \ + CFStringRef reason = CFStringCreateWithFormat(NULL, NULL, CFSTR("*** attempt to create a temporary id buffer which is too large or with a negative count (%lu) -- possibly data is corrupt"), N ## _count__); \ + NSException *e = [NSException exceptionWithName:NSGenericException reason:(NSString *)reason userInfo:nil]; \ + CFRelease(reason); \ + @throw e; \ + } \ + Boolean N ## _is_stack__ = (N ## _count__ <= 256) && (S); \ + if (N ## _count__ == 0) { \ + N ## _count__ = 1; \ + } \ + id N ## _scopedbuffer__ [N ## _is_stack__ ? N ## _count__ : 1]; \ + if (N ## _is_stack__ && (Z)) { \ + memset(N ## _scopedbuffer__, 0, N ## _count__ * sizeof(id)); \ + } \ + size_t N ## _unused__; \ + id * __attribute__((cleanup(_scoped_id_array_cleanup))) N ## _mallocbuffer__ = N ## _is_stack__ ? NULL : (id *)_CFCreateArrayStorage(N ## _count__, (Z), & N ## _unused__); \ + id * N = N ## _is_stack__ ? N ## _scopedbuffer__ : N ## _mallocbuffer__; \ + do {} while (0) + +// These macros create an array that is 1) either stack or buffer allocated, depending on size, and 2) automatically cleaned up at the end of the lexical scope it is declared in. +#define scoped_id_array(N, C) _scoped_id_array(N, C, false, true) +#define scoped_and_zeroed_id_array(N, C) _scoped_id_array(N, C, true, true) + +#define scoped_heap_id_array(N, C) _scoped_id_array(N, C, false, false) + +// This macro either returns the buffer while simultaneously passing responsibility for freeing it to the caller, or it returns NULL if the buffer exists on the stack, and therefore can't pass ownership. +#define try_adopt_scoped_id_array(N) (N ## _mallocbuffer__ ? ({id *tmp = N ## _mallocbuffer__; N ## _mallocbuffer__ = NULL; tmp;}) : NULL) + +CF_INLINE void _scoped_id_array_cleanup(id _Nonnull * _Nullable * _Nonnull mallocedbuffer) { + // Maybe be NULL, but free(NULL) is well defined as a no-op. + free(*mallocedbuffer); +} +#endif + +// Define NS_DIRECT / NS_DIRECT_MEMBERS Internally for CoreFoundation. + +#ifndef NS_DIRECT + #if __has_attribute(objc_direct) + #define NS_DIRECT __attribute__((objc_direct)) + #else + #define NS_DIRECT + #endif +#endif + +#ifndef NS_DIRECT_MEMBERS + #if __has_attribute(objc_direct_members) + #define NS_DIRECT_MEMBERS __attribute__((objc_direct_members)) + #else + #define NS_DIRECT_MEMBERS + #endif +#endif + #endif /* ! __COREFOUNDATION_FORFOUNDATIONONLY__ */ diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h index 64f8ee5892..233b6f8775 100644 --- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h +++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h @@ -29,6 +29,7 @@ #include #include #include +#include #if TARGET_OS_WIN32 #define NOMINMAX @@ -90,7 +91,6 @@ _CF_EXPORT_SCOPE_BEGIN CF_PRIVATE Boolean __CFAllocatorRespectsHintZeroWhenAllocating(CFAllocatorRef _Nullable allocator); -static CFOptionFlags _CFAllocatorHintZeroWhenAllocating = 1; CF_CROSS_PLATFORM_EXPORT Boolean _CFCalendarGetNextWeekend(CFCalendarRef calendar, _CFCalendarWeekendRange *range); CF_CROSS_PLATFORM_EXPORT void _CFCalendarEnumerateDates(CFCalendarRef calendar, CFDateRef start, CFDateComponentsRef matchingComponents, CFOptionFlags opts, void (^block)(CFDateRef _Nullable, Boolean, Boolean*)); diff --git a/CoreFoundation/CMakeLists.txt b/CoreFoundation/CMakeLists.txt index 103b5d8a6b..6a71496c40 100644 --- a/CoreFoundation/CMakeLists.txt +++ b/CoreFoundation/CMakeLists.txt @@ -60,6 +60,7 @@ if(NOT "${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") add_compile_options($<$:-fconstant-cfstrings>) add_compile_options($<$:-fdollars-in-identifiers>) add_compile_options($<$:-fno-common>) + add_compile_options($<$:-Werror=implicit-function-declaration>) endif() if(CF_DEPLOYMENT_SWIFT) @@ -146,8 +147,10 @@ add_framework(CoreFoundation Locale.subproj/CFDateIntervalFormatter.h Locale.subproj/CFDateInterval.h Locale.subproj/CFICULogging.h + Locale.subproj/CFListFormatter.h Locale.subproj/CFLocaleInternal.h Locale.subproj/CFLocale_Private.h + Locale.subproj/CFRelativeDateTimeFormatter.h # NumberDate NumberDate.subproj/CFBigNumber.h NumberDate.subproj/CFNumber_Private.h @@ -172,6 +175,7 @@ add_framework(CoreFoundation String.subproj/CFRegularExpression.h String.subproj/CFRunArray.h String.subproj/CFString_Internal.h + String.subproj/CFString_Private.h String.subproj/CFStringDefaultEncoding.h String.subproj/CFStringLocalizedFormattingInternal.h # StringEncodings @@ -299,10 +303,12 @@ add_framework(CoreFoundation Locale.subproj/CFDateFormatter.c Locale.subproj/CFDateIntervalFormatter.c Locale.subproj/CFDateInterval.c + Locale.subproj/CFListFormatter.c Locale.subproj/CFLocale.c Locale.subproj/CFLocaleIdentifier.c Locale.subproj/CFLocaleKeys.c Locale.subproj/CFNumberFormatter.c + Locale.subproj/CFRelativeDateTimeFormatter.c # NumberData NumberDate.subproj/CFBigNumber.c NumberDate.subproj/CFDate.c diff --git a/CoreFoundation/CharacterSets/CFCharacterSetBitmaps.bitmap b/CoreFoundation/CharacterSets/CFCharacterSetBitmaps.bitmap index fcb505139b..981b31d542 100644 Binary files a/CoreFoundation/CharacterSets/CFCharacterSetBitmaps.bitmap and b/CoreFoundation/CharacterSets/CFCharacterSetBitmaps.bitmap differ diff --git a/CoreFoundation/CharacterSets/CFUniCharPropertyDatabase.data b/CoreFoundation/CharacterSets/CFUniCharPropertyDatabase.data index 561761d01b..14a5a7131a 100644 Binary files a/CoreFoundation/CharacterSets/CFUniCharPropertyDatabase.data and b/CoreFoundation/CharacterSets/CFUniCharPropertyDatabase.data differ diff --git a/CoreFoundation/CharacterSets/CFUnicodeData-B.mapping b/CoreFoundation/CharacterSets/CFUnicodeData-B.mapping index 5d775d94fb..b4c4eec62e 100644 Binary files a/CoreFoundation/CharacterSets/CFUnicodeData-B.mapping and b/CoreFoundation/CharacterSets/CFUnicodeData-B.mapping differ diff --git a/CoreFoundation/CharacterSets/CFUnicodeData-L.mapping b/CoreFoundation/CharacterSets/CFUnicodeData-L.mapping index 681809fd75..e7aef97fb1 100644 Binary files a/CoreFoundation/CharacterSets/CFUnicodeData-L.mapping and b/CoreFoundation/CharacterSets/CFUnicodeData-L.mapping differ diff --git a/CoreFoundation/Collections.subproj/CFArray.c b/CoreFoundation/Collections.subproj/CFArray.c index 1b6d00dbbb..97a4bb2131 100644 --- a/CoreFoundation/Collections.subproj/CFArray.c +++ b/CoreFoundation/Collections.subproj/CFArray.c @@ -967,7 +967,7 @@ void CFArraySortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunct BOOL result; result = CF_OBJC_CALLV((NSMutableArray *)array, isKindOfClass:[NSMutableArray class]); immutable = !result; - } else if (CF_IS_SWIFT(CFArrayGetTypeID(), array)) { + } else if (CF_IS_SWIFT(_kCFRuntimeIDCFArray, array)) { #if DEPLOYMENT_RUNTIME_SWIFT Boolean result = __CFSwiftBridge.NSArray.isSubclassOfNSMutableArray(array); immutable = !result; diff --git a/CoreFoundation/Collections.subproj/CFBag.c b/CoreFoundation/Collections.subproj/CFBag.c index 8aecb4cd67..fcf1bcb625 100644 --- a/CoreFoundation/Collections.subproj/CFBag.c +++ b/CoreFoundation/Collections.subproj/CFBag.c @@ -193,6 +193,12 @@ CFIndex CFBagGetCount(CFBagRef hc) { return CFBasicHashGetCount((CFBasicHashRef)hc); } +// This function is for Foundation's benefit; no one else should use it. +CF_EXPORT CFIndex _CFBagGetUniqueCount(CFBagRef hc) { + __CFGenericValidateType(hc, CFBagGetTypeID()); + return CFBasicHashGetUsedBucketCount((CFBasicHashRef)hc); +} + CFIndex CFBagGetCountOfValue(CFBagRef hc, const void *key) { __CFGenericValidateType(hc, CFBagGetTypeID()); return CFBasicHashGetCountOfKey((CFBasicHashRef)hc, (uintptr_t)key); diff --git a/CoreFoundation/Collections.subproj/CFBasicHash.c b/CoreFoundation/Collections.subproj/CFBasicHash.c index 7bac403806..baf1e7b089 100644 --- a/CoreFoundation/Collections.subproj/CFBasicHash.c +++ b/CoreFoundation/Collections.subproj/CFBasicHash.c @@ -380,6 +380,23 @@ static inline void *CFBasicHashGetPtrAtIndex(int32_t i) __attribute__((no_saniti return CFBasicHashCallBackPtrs[i]; } +CF_PRIVATE CFBasicHashCallbacks __CFBasicHashGetCallbacks(CFTypeRef cf) { + CFBasicHashRef ht = (CFBasicHashRef)cf; + CFBasicHashCallbacks callbacks = { + .retainValue = CFBasicHashGetPtrAtIndex(ht->bits.__vret), + .retainKey = CFBasicHashGetPtrAtIndex(ht->bits.__kret), + .releaseValue = CFBasicHashGetPtrAtIndex(ht->bits.__vrel), + .releaseKey = CFBasicHashGetPtrAtIndex(ht->bits.__krel), + .equateValues = CFBasicHashGetPtrAtIndex(ht->bits.__vequ), + .equateKeys = CFBasicHashGetPtrAtIndex(ht->bits.__kequ), + .hashKey = CFBasicHashGetPtrAtIndex(ht->bits.__khas), + .getIndirectKey = CFBasicHashGetPtrAtIndex(ht->bits.__kget), + .copyValueDescription = CFBasicHashGetPtrAtIndex(ht->bits.__vdes), + .copyKeyDescription = CFBasicHashGetPtrAtIndex(ht->bits.__kdes), + }; + return callbacks; +} + CF_PRIVATE Boolean CFBasicHashHasStrongValues(CFConstBasicHashRef ht) { #if TARGET_OS_OSX return ht->bits.strong_values ? true : false; @@ -937,6 +954,10 @@ CF_PRIVATE CFIndex CFBasicHashGetCount(CFConstBasicHashRef ht) { return (CFIndex)ht->bits.used_buckets; } +CF_PRIVATE CFIndex CFBasicHashGetUsedBucketCount(CFConstBasicHashRef ht) { + return (CFIndex)ht->bits.used_buckets; +} + CF_PRIVATE CFIndex CFBasicHashGetCountOfKey(CFConstBasicHashRef ht, uintptr_t stack_key) { if (__CFBasicHashSubABZero == stack_key || __CFBasicHashSubABOne == stack_key) { return 0L; @@ -1452,8 +1473,8 @@ CF_PRIVATE size_t CFBasicHashGetSize(CFConstBasicHashRef ht, Boolean total) { if (ht->bits.keys_offset) size += sizeof(CFBasicHashValue *); if (ht->bits.counts_offset) size += sizeof(void *); if (__CFBasicHashHasHashCache(ht)) size += sizeof(uintptr_t *); -#if ENABLE_MEMORY_COUNTERS || ENABLE_DTRACE_PROBES if (total) { +#if ENABLE_MEMORY_COUNTERS || ENABLE_DTRACE_PROBES CFIndex num_buckets = __CFBasicHashTableSizes[ht->bits.num_buckets_idx]; if (0 < num_buckets) { size += malloc_size(__CFBasicHashGetValues(ht)); @@ -1461,10 +1482,10 @@ CF_PRIVATE size_t CFBasicHashGetSize(CFConstBasicHashRef ht, Boolean total) { if (ht->bits.counts_offset) size += malloc_size(__CFBasicHashGetCounts(ht)); if (__CFBasicHashHasHashCache(ht)) size += malloc_size(__CFBasicHashGetHashes(ht)); } - } #else - (void)total; + (void)total; #endif + } return size; } diff --git a/CoreFoundation/Collections.subproj/CFBasicHash.h b/CoreFoundation/Collections.subproj/CFBasicHash.h index 43083bcbf4..988dc9f167 100644 --- a/CoreFoundation/Collections.subproj/CFBasicHash.h +++ b/CoreFoundation/Collections.subproj/CFBasicHash.h @@ -96,6 +96,7 @@ CFIndex CFBasicHashGetCapacity(CFConstBasicHashRef ht); void CFBasicHashSetCapacity(CFBasicHashRef ht, CFIndex capacity); CFIndex CFBasicHashGetCount(CFConstBasicHashRef ht); +CFIndex CFBasicHashGetUsedBucketCount(CFConstBasicHashRef ht); CFBasicHashBucket CFBasicHashGetBucket(CFConstBasicHashRef ht, CFIndex idx); CFBasicHashBucket CFBasicHashFindBucket(CFConstBasicHashRef ht, uintptr_t stack_key); CFIndex CFBasicHashGetCountOfKey(CFConstBasicHashRef ht, uintptr_t stack_key); @@ -123,6 +124,7 @@ CFStringRef CFBasicHashCopyDescription(CFConstBasicHashRef ht, Boolean detailed, CFTypeID CFBasicHashGetTypeID(void); +extern CFBasicHashCallbacks __CFBasicHashGetCallbacks(CFTypeRef cf); extern Boolean __CFBasicHashEqual(CFTypeRef cf1, CFTypeRef cf2); extern CFHashCode __CFBasicHashHash(CFTypeRef cf); extern CFStringRef __CFBasicHashCopyDescription(CFTypeRef cf); diff --git a/CoreFoundation/Collections.subproj/CFData.c b/CoreFoundation/Collections.subproj/CFData.c index fb5ebc86cf..a8eaade00c 100644 --- a/CoreFoundation/Collections.subproj/CFData.c +++ b/CoreFoundation/Collections.subproj/CFData.c @@ -15,8 +15,9 @@ #include "CFRuntime_Internal.h" #include -#if DEPLOYMENT_RUNTIME_SWIFT + +#if DEPLOYMENT_RUNTIME_SWIFT DECLARE_STATIC_CLASS_REF(NSMutableData); static const void *_NSMutableData = STATIC_CLASS_REF(NSMutableData); static Boolean _CFDataShouldBridgeToSwift(CFTypeID type, CFDataRef data); @@ -207,15 +208,14 @@ static void __CFDataHandleOutOfMemory(CFTypeRef obj, CFIndex numBytes) CLANG_ANA HALT; } -#if defined(DEBUG) -CF_INLINE void __CFDataValidateRange(CFDataRef data, CFRange range, const char *func) { - CFAssert2(0 <= range.location && range.location <= __CFDataLength(data), __kCFLogAssertion, "%s(): range.location index (%ld) out of bounds", func, range.location); - CFAssert2(0 <= range.length, __kCFLogAssertion, "%s(): length (%ld) cannot be less than zero", func, range.length); - CFAssert2(range.location + range.length <= __CFDataLength(data), __kCFLogAssertion, "%s(): ending index (%ld) out of bounds", func, range.location + range.length); +#define FAUX_HALT_MSG(msg) fprintf(stderr, "%s\n", msg) + +CF_INLINE void __CFDataValidateRange(CFDataRef data, CFRange range) { + if (range.location < 0) FAUX_HALT_MSG("range.location out of range (<0)"); + if (range.location > __CFDataLength(data)) FAUX_HALT_MSG("range.location out of range (>len)"); + if (range.length < 0) FAUX_HALT_MSG("length cannot be less than zero"); + if (range.location + range.length > __CFDataLength(data)) FAUX_HALT_MSG("ending index out of bounds"); } -#else -#define __CFDataValidateRange(a,r,f) -#endif static Boolean __CFDataEqual(CFTypeRef cf1, CFTypeRef cf2) { CFDataRef data1 = (CFDataRef)cf1; @@ -527,7 +527,7 @@ uint8_t *CFDataGetMutableBytePtr(CFMutableDataRef data) { void CFDataGetBytes(CFDataRef data, CFRange range, uint8_t *buffer) { CF_OBJC_FUNCDISPATCHV(CFDataGetTypeID(), void, (NSData *)data, getBytes:(void *)buffer range:NSMakeRange(range.location, range.length)); CF_SWIFT_NSDATA_FUNCDISPATCHV(_kCFRuntimeIDCFData, void, data, NSData.getBytes, range, buffer); - __CFDataValidateRange(data, range, __PRETTY_FUNCTION__); + __CFDataValidateRange(data, range); memmove(buffer, _CFDataGetBytePtrNonObjC(data) + range.location, range.length); } @@ -624,10 +624,10 @@ void CFDataDeleteBytes(CFMutableDataRef data, CFRange range) { } void CFDataReplaceBytes(CFMutableDataRef data, CFRange range, const uint8_t *newBytes, CFIndex newBytesLength) { - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFData, void, (NSMutableData *)data, replaceBytesInRange:NSMakeRange(range.location, range.length) withBytes:(const void *)newBytes length:(NSUInteger)newBytesLength); + CF_OBJC_FUNCDISPATCHV(CFDataGetTypeID(), void, (NSMutableData *)data, replaceBytesInRange:NSMakeRange(range.location, range.length) withBytes:(const void *)newBytes length:(NSUInteger)newBytesLength); CF_SWIFT_NSDATA_FUNCDISPATCHV(_kCFRuntimeIDCFData, void, data, NSData.replaceBytes, range, newBytes, newBytesLength); __CFGenericValidateType(data, CFDataGetTypeID()); - __CFDataValidateRange(data, range, __PRETTY_FUNCTION__); + __CFDataValidateRange(data, range); CFAssert1(__CFDataIsMutable(data), __kCFLogAssertion, "%s(): data is immutable", __PRETTY_FUNCTION__); CFAssert2(0 <= newBytesLength, __kCFLogAssertion, "%s(): newLength (%ld) cannot be less than zero", __PRETTY_FUNCTION__, newBytesLength); @@ -860,12 +860,11 @@ CFRange CFDataFind(CFDataRef data, CFDataRef dataToFind, CFRange searchRange, CF // No objc dispatch __CFGenericValidateType(data, CFDataGetTypeID()); __CFGenericValidateType(dataToFind, CFDataGetTypeID()); - __CFDataValidateRange(data, searchRange, __PRETTY_FUNCTION__); + __CFDataValidateRange(data, searchRange); return _CFDataFindBytes(data, dataToFind, searchRange, compareOptions); } -#undef __CFDataValidateRange #undef INLINE_BYTES_THRESHOLD #undef CFDATA_MAX_SIZE #undef REVERSE_BUFFER diff --git a/CoreFoundation/Collections.subproj/CFDictionary.c b/CoreFoundation/Collections.subproj/CFDictionary.c index 11b49768ea..d2ba0899b0 100644 --- a/CoreFoundation/Collections.subproj/CFDictionary.c +++ b/CoreFoundation/Collections.subproj/CFDictionary.c @@ -20,6 +20,31 @@ const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks = {0, __CFTypeColle const CFDictionaryKeyCallBacks kCFCopyStringDictionaryKeyCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual}; +CF_PRIVATE CFDictionaryKeyCallBacks __CFDictionaryGetKeyCallbacks(CFSetRef hc) { + CFBasicHashCallbacks hashCallbacks = __CFBasicHashGetCallbacks(hc); + CFDictionaryKeyCallBacks keyCallbacks = { + .version = 0, + .retain = (CFSetRetainCallBack)hashCallbacks.retainKey, + .release = (CFSetReleaseCallBack)hashCallbacks.releaseKey, + .equal = (CFSetEqualCallBack)hashCallbacks.equateKeys, + .hash = (CFSetHashCallBack)hashCallbacks.hashKey, + .copyDescription = (CFSetCopyDescriptionCallBack)hashCallbacks.copyKeyDescription + }; + return keyCallbacks; +} + +CF_PRIVATE CFDictionaryValueCallBacks __CFDictionaryGetValueCallbacks(CFSetRef hc) { + CFBasicHashCallbacks hashCallbacks = __CFBasicHashGetCallbacks(hc); + CFDictionaryValueCallBacks valueCallbacks = { + .version = 0, + .retain = (CFSetRetainCallBack)hashCallbacks.retainValue, + .release = (CFSetReleaseCallBack)hashCallbacks.releaseValue, + .equal = (CFSetEqualCallBack)hashCallbacks.equateValues, + .copyDescription = (CFSetCopyDescriptionCallBack)hashCallbacks.copyValueDescription + }; + return valueCallbacks; +} + static Boolean __CFDictionaryEqual(CFTypeRef cf1, CFTypeRef cf2) { return __CFBasicHashEqual((CFBasicHashRef)cf1, (CFBasicHashRef)cf2); } @@ -72,13 +97,6 @@ static CFBasicHashRef __CFDictionaryCreateGeneric(CFAllocatorRef allocator, cons } CF_PRIVATE CFDictionaryRef __CFDictionaryCreateTransfer(CFAllocatorRef allocator, void const **klist, void const **vlist, CFIndex numValues) { -#if !DEPLOYMENT_RUNTIME_SWIFT - CF_PRIVATE CFDictionaryRef __NSCFDictionaryCreateTransfer(CFAllocatorRef allocator, const id *klist, const id *vlist, CFIndex numValues); - CFDictionaryRef nsResult = __NSCFDictionaryCreateTransfer(allocator, (const id *)klist, (const id *)vlist, numValues); - if (nsResult) { - return nsResult; - } -#endif CFTypeID typeID = _kCFRuntimeIDCFDictionary; CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); CFOptionFlags flags = kCFBasicHashLinearHashing | kCFBasicHashHasKeys; // kCFBasicHashExponentialHashing @@ -109,13 +127,6 @@ CF_PRIVATE CFDictionaryRef __CFDictionaryCreateTransfer(CFAllocatorRef allocator } CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, void const **klist, void const **vlist, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) { -#if !DEPLOYMENT_RUNTIME_SWIFT - CF_PRIVATE CFDictionaryRef __NSCFDictionaryCreate(CFAllocatorRef allocator, void const **klist, void const **vlist, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks); - CFDictionaryRef nsResult = __NSCFDictionaryCreate(allocator, klist, vlist, numValues, keyCallBacks, valueCallBacks); - if (nsResult) { - return nsResult; - } -#endif CFTypeID typeID = _kCFRuntimeIDCFDictionary; CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%ld) cannot be less than zero", __PRETTY_FUNCTION__, numValues); CFBasicHashRef ht = __CFDictionaryCreateGeneric(allocator, keyCallBacks, valueCallBacks, true); @@ -131,13 +142,6 @@ CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, void const **klist, } CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) { -#if !DEPLOYMENT_RUNTIME_SWIFT - CF_PRIVATE CFMutableDictionaryRef __NSCFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks); - CFMutableDictionaryRef nsResult = __NSCFDictionaryCreateMutable(allocator, capacity, keyCallBacks, valueCallBacks); - if (nsResult) { - return nsResult; - } -#endif CFTypeID typeID = _kCFRuntimeIDCFDictionary; CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%ld) cannot be less than zero", __PRETTY_FUNCTION__, capacity); CFBasicHashRef ht = __CFDictionaryCreateGeneric(allocator, keyCallBacks, valueCallBacks, true); @@ -297,7 +301,7 @@ void CFDictionaryApplyFunction(CFDictionaryRef hc, CFDictionaryApplierFunction a } CF_PRIVATE void CFDictionaryApply(CFDictionaryRef hc, void (^block)(const void *key, const void *value, Boolean *stop)) { - CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFDictionary, void, (CFSwiftRef)hc, NSDictionary.enumerateKeysAndObjectsWithOptions, 0, block); + CF_SWIFT_FUNCDISPATCHV(CFDictionaryGetTypeID(), void, (CFSwiftRef)hc, NSDictionary.enumerateKeysAndObjectsWithOptions, 0, block); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFDictionary, void, (NSDictionary *)hc, enumerateKeysAndObjectsWithOptions:0 usingBlock:(void (^ _Nonnull)(id _Nonnull, id _Nonnull, BOOL * _Nonnull))block); __CFGenericValidateType(hc, CFDictionaryGetTypeID()); CFBasicHashApply((CFBasicHashRef)hc, ^(CFBasicHashBucket bkt) { diff --git a/CoreFoundation/Collections.subproj/CFSet.c b/CoreFoundation/Collections.subproj/CFSet.c index e151a1be38..2300583432 100644 --- a/CoreFoundation/Collections.subproj/CFSet.c +++ b/CoreFoundation/Collections.subproj/CFSet.c @@ -18,6 +18,19 @@ const CFSetCallBacks kCFTypeSetCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; const CFSetCallBacks kCFCopyStringSetCallBacks = {0, __CFStringCollectionCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +CF_PRIVATE CFSetCallBacks __CFSetGetCallbacks(CFSetRef hc) { + CFBasicHashCallbacks hashCallbacks = __CFBasicHashGetCallbacks(hc); + CFSetCallBacks setCallbacks = { + .version = 0, + .retain = (CFSetRetainCallBack)hashCallbacks.retainKey, + .release = (CFSetReleaseCallBack)hashCallbacks.releaseKey, + .equal = (CFSetEqualCallBack)hashCallbacks.equateKeys, + .hash = (CFSetHashCallBack)hashCallbacks.hashKey, + .copyDescription = (CFSetCopyDescriptionCallBack)hashCallbacks.copyKeyDescription + }; + return setCallbacks; +} + static Boolean __CFSetEqual(CFTypeRef cf1, CFTypeRef cf2) { return __CFBasicHashEqual((CFBasicHashRef)cf1, (CFBasicHashRef)cf2); } diff --git a/CoreFoundation/Collections.subproj/CFStorage.c b/CoreFoundation/Collections.subproj/CFStorage.c index de1fb64168..74948ebae4 100644 --- a/CoreFoundation/Collections.subproj/CFStorage.c +++ b/CoreFoundation/Collections.subproj/CFStorage.c @@ -189,7 +189,10 @@ CF_INLINE CFRange __CFStorageConvertValuesToByteRange(ConstCFStorageRef storage, #pragma mark Node reference counting and freezing CF_INLINE CFStorageNode *__CFStorageRetainNode(CFStorageNode *node) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" if (node->refCount > 0) OSAtomicIncrement32((int32_t *)&node->refCount); +#pragma GCC diagnostic pop return node; } @@ -203,7 +206,10 @@ static void __CFStorageDeallocateNode(CFStorageRef storage, CFStorageNode *node) CF_INLINE void __CFStorageReleaseNode(CFStorageRef storage, CFStorageNode * _Nonnull node) { if (node->refCount > 0) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" uint32_t newRefCount = OSAtomicDecrement32((int32_t *)&node->refCount); +#pragma GCC diagnostic pop if (newRefCount == 0) { __CFStorageDeallocateNode(storage, node); } @@ -1011,15 +1017,19 @@ static bool __CFStorageEnumerateNodesInByteRangeWithBlock(CFStorageRef storage, } #endif } else { - if (overlaps[0].length > 0) { - stop = stop || __CFStorageEnumerateNodesInByteRangeWithBlock(storage, children[0], globalOffsetOfNode + offsets[0], CFRangeMake(overlaps[0].location - offsets[0], overlaps[0].length), concurrencyToken, applier); - } - if (overlaps[1].length > 0) { - stop = stop || __CFStorageEnumerateNodesInByteRangeWithBlock(storage, children[1], globalOffsetOfNode + offsets[1], CFRangeMake(overlaps[1].location - offsets[1], overlaps[1].length), concurrencyToken, applier); - } - if (overlaps[2].length > 0) { - stop = stop || __CFStorageEnumerateNodesInByteRangeWithBlock(storage, children[2], globalOffsetOfNode + offsets[2], CFRangeMake(overlaps[2].location - offsets[2], overlaps[2].length), concurrencyToken, applier); - } + // The analyzer can't reason that children[x] == NULL => lengths[x] == 0 => overlaps[x].length == 0 + if (overlaps[0].length > 0) { + _CLANG_ANALYZER_ASSERT(children[0]); + stop = stop || __CFStorageEnumerateNodesInByteRangeWithBlock(storage, children[0], globalOffsetOfNode + offsets[0], CFRangeMake(overlaps[0].location - offsets[0], overlaps[0].length), concurrencyToken, applier); + } + if (overlaps[1].length > 0) { + _CLANG_ANALYZER_ASSERT(children[1]); + stop = stop || __CFStorageEnumerateNodesInByteRangeWithBlock(storage, children[1], globalOffsetOfNode + offsets[1], CFRangeMake(overlaps[1].location - offsets[1], overlaps[1].length), concurrencyToken, applier); + } + if (overlaps[2].length > 0) { + _CLANG_ANALYZER_ASSERT(children[2]); + stop = stop || __CFStorageEnumerateNodesInByteRangeWithBlock(storage, children[2], globalOffsetOfNode + offsets[2], CFRangeMake(overlaps[2].location - offsets[2], overlaps[2].length), concurrencyToken, applier); + } } } return stop; diff --git a/CoreFoundation/Collections.subproj/CFTree.c b/CoreFoundation/Collections.subproj/CFTree.c index 5af295bb05..ba6a73fdd4 100644 --- a/CoreFoundation/Collections.subproj/CFTree.c +++ b/CoreFoundation/Collections.subproj/CFTree.c @@ -135,8 +135,8 @@ CFTreeRef CFTreeCreate(CFAllocatorRef allocator, const CFTreeContext *context) { } CFIndex CFTreeGetChildCount(CFTreeRef tree) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); SInt32 cnt = 0; - __CFGenericValidateType(tree, CFTreeGetTypeID()); tree = tree->_child; while (NULL != tree) { cnt++; @@ -146,22 +146,22 @@ CFIndex CFTreeGetChildCount(CFTreeRef tree) { } CFTreeRef CFTreeGetParent(CFTreeRef tree) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); return tree->_parent; } CFTreeRef CFTreeGetNextSibling(CFTreeRef tree) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); return tree->_sibling; } CFTreeRef CFTreeGetFirstChild(CFTreeRef tree) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); return tree->_child; } CFTreeRef CFTreeFindRoot(CFTreeRef tree) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); while (NULL != tree->_parent) { tree = tree->_parent; } @@ -170,7 +170,7 @@ CFTreeRef CFTreeFindRoot(CFTreeRef tree) { void CFTreeGetContext(CFTreeRef tree, CFTreeContext *context) { const struct __CFTreeCallBacks *cb; - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); cb = __CFTreeGetCallBacks(tree); context->version = 0; @@ -184,6 +184,7 @@ void CFTreeGetContext(CFTreeRef tree, CFTreeContext *context) { } void CFTreeSetContext(CFTreeRef tree, const CFTreeContext *context) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); uint32_t newtype, oldtype = __CFTreeGetCallBacksType(tree); struct __CFTreeCallBacks *oldcb = (struct __CFTreeCallBacks *)__CFTreeGetCallBacks(tree); struct __CFTreeCallBacks *newcb; @@ -263,7 +264,7 @@ CFTreeRef CFTreeFind(CFTreeRef tree, const void *info) { #endif CFTreeRef CFTreeGetChildAtIndex(CFTreeRef tree, CFIndex idx) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); tree = tree->_child; while (NULL != tree) { if (0 == idx) return tree; @@ -274,7 +275,7 @@ CFTreeRef CFTreeGetChildAtIndex(CFTreeRef tree, CFIndex idx) { } void CFTreeGetChildren(CFTreeRef tree, CFTreeRef *children) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); tree = tree->_child; while (NULL != tree) { *children++ = tree; @@ -283,7 +284,7 @@ void CFTreeGetChildren(CFTreeRef tree, CFTreeRef *children) { } void CFTreeApplyFunctionToChildren(CFTreeRef tree, CFTreeApplierFunction applier, void *context) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); CFAssert1(NULL != applier, __kCFLogAssertion, "%s(): pointer to applier function may not be NULL", __PRETTY_FUNCTION__); tree = tree->_child; while (NULL != tree) { @@ -293,7 +294,7 @@ void CFTreeApplyFunctionToChildren(CFTreeRef tree, CFTreeApplierFunction applier } void CFTreePrependChild(CFTreeRef tree, CFTreeRef newChild) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); __CFGenericValidateType(newChild, CFTreeGetTypeID()); CFAssert1(NULL == newChild->_parent, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); CFAssert1(NULL == newChild->_sibling, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); @@ -307,14 +308,16 @@ void CFTreePrependChild(CFTreeRef tree, CFTreeRef newChild) { } void CFTreeAppendChild(CFTreeRef tree, CFTreeRef newChild) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); - __CFGenericValidateType(newChild, CFTreeGetTypeID()); + CFAllocatorRef allocator; + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, newChild); CFAssert1(NULL == newChild->_parent, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); CFAssert1(NULL == newChild->_sibling, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); if (newChild->_parent) { HALT; } CFRetain(newChild); + allocator = CFGetAllocator(tree); *((void **)&newChild->_parent) = tree; newChild->_sibling = NULL; if (!tree->_child) { @@ -326,12 +329,14 @@ void CFTreeAppendChild(CFTreeRef tree, CFTreeRef newChild) { } void CFTreeInsertSibling(CFTreeRef tree, CFTreeRef newSibling) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); - __CFGenericValidateType(newSibling, CFTreeGetTypeID()); + CFAllocatorRef allocator; + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, newSibling); CFAssert1(NULL != tree->_parent, __kCFLogAssertion, "%s(): tree must have a parent", __PRETTY_FUNCTION__); CFAssert1(NULL == newSibling->_parent, __kCFLogAssertion, "%s(): must remove newSibling from previous parent first", __PRETTY_FUNCTION__); CFAssert1(NULL == newSibling->_sibling, __kCFLogAssertion, "%s(): must remove newSibling from previous parent first", __PRETTY_FUNCTION__); CFRetain(newSibling); + allocator = CFGetAllocator(tree); *((void **)&newSibling->_parent) = tree->_parent; *((void **)&newSibling->_sibling) = tree->_sibling; *((void **)&tree->_sibling) = newSibling; @@ -343,7 +348,7 @@ void CFTreeInsertSibling(CFTreeRef tree, CFTreeRef newSibling) { } void CFTreeRemove(CFTreeRef tree) { - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); if (NULL != tree->_parent) { if (tree == tree->_parent->_child) { *((void **)&tree->_parent->_child) = tree->_sibling; @@ -370,7 +375,7 @@ void CFTreeRemove(CFTreeRef tree) { void CFTreeRemoveAllChildren(CFTreeRef tree) { CFTreeRef nextChild; - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); nextChild = tree->_child; tree->_child = NULL; tree->_rightmostChild = NULL; @@ -396,7 +401,7 @@ static CFComparisonResult __CFTreeCompareValues(const void *v1, const void *v2, void CFTreeSortChildren(CFTreeRef tree, CFComparatorFunction comparator, void *context) { CFIndex children; - __CFGenericValidateType(tree, CFTreeGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFTree, tree); CFAssert1(NULL != comparator, __kCFLogAssertion, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__); children = CFTreeGetChildCount(tree); if (1 < children) { diff --git a/CoreFoundation/Error.subproj/CFError.c b/CoreFoundation/Error.subproj/CFError.c index ab48eb42a4..7ff93ce459 100644 --- a/CoreFoundation/Error.subproj/CFError.c +++ b/CoreFoundation/Error.subproj/CFError.c @@ -172,6 +172,8 @@ static CFDictionaryRef _CFErrorCreateEmptyDictionary(CFAllocatorRef allocator) { #if DEPLOYMENT_TARGET_OBJC if (_CFAllocatorIsSystemDefault(allocator)) { return (CFDictionaryRef)CFRetain(__NSDictionary0__); + } else { + return CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } #endif diff --git a/CoreFoundation/Locale.subproj/CFCalendar.c b/CoreFoundation/Locale.subproj/CFCalendar.c index 8169570c36..b4f3a2b313 100644 --- a/CoreFoundation/Locale.subproj/CFCalendar.c +++ b/CoreFoundation/Locale.subproj/CFCalendar.c @@ -32,6 +32,16 @@ enum { #define ICU_LOG(FMT, ...) do { } while (0) +#define MIN_CALENDAR_TIME -211845067200.0 // Julian day 0 (-4713-01-01 12:00:00 +0000) in CFAbsoluteTime. +#define MAX_CALENDAR_TIME 15927175497600.0 // 50000-01-01 00:00:00 +0000, smaller than the max time ICU supported. +#define __CFCalendarValidateAndCapTimeRange(at) do { \ + if (at < MIN_CALENDAR_TIME || at > MAX_CALENDAR_TIME) { \ + os_log_error(_CFOSLog(), "CFAbsoluteTime %lf exceeds calendar calculation range.", at); \ + if (at < MIN_CALENDAR_TIME) { at = MIN_CALENDAR_TIME; } \ + else { at = MAX_CALENDAR_TIME; } \ + } \ +} while (0) + extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale); #define MIN_TIMEZONE_UDATE -2177452800000.0 // 1901-01-01 00:00:00 +0000 @@ -606,7 +616,6 @@ static void __CFCalendarApplyUserSettingsFromLocale(CFCalendarRef calendar, CFLo } } - static bool _CFCalendarInitialize(CFCalendarRef calendar, CFAllocatorRef allocator, CFStringRef identifier, CFTimeZoneRef tz, CFLocaleRef locale, CFIndex firstDayOfWeek, CFIndex minDaysInFirstWeek, CFDateRef gregorianStartDate) { ICU_LOG(" // CFCalendarCreateWithIdentifier enter\n"); if (allocator == NULL) allocator = __CFGetDefaultAllocator(); @@ -632,7 +641,7 @@ static bool _CFCalendarInitialize(CFCalendarRef calendar, CFAllocatorRef allocat else if (CFEqual(kCFCalendarIdentifierIslamicUmmAlQura, identifier)) canonicalIdent = kCFCalendarIdentifierIslamicUmmAlQura; if (!canonicalIdent) ICU_LOG(" // CFCalendarCreateWithIdentifier exit NULL 1\n"); if (!canonicalIdent) return false; - + calendar->_identifier = (CFStringRef)CFRetain(canonicalIdent); calendar->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : CFRetain(CFLocaleGetSystem()); calendar->_tz = tz ? CFRetain(tz) : CFTimeZoneCopyDefault(); @@ -954,11 +963,37 @@ static UCalendarDateFields __CFCalendarGetICUFieldCode(CFCalendarUnit unit) { return (UCalendarDateFields)UCAL_FIELD_ERROR; } +static CFCalendarUnit __CFCalendarUnitFromICUDateFields(UCalendarDateFields field) { + switch (field) { + case UCAL_ERA: return kCFCalendarUnitEra; + case UCAL_YEAR: return kCFCalendarUnitYear; + case UCAL_MONTH: return kCFCalendarUnitMonth; + case UCAL_WEEK_OF_YEAR: return kCFCalendarUnitWeekOfYear; + case UCAL_WEEK_OF_MONTH: return kCFCalendarUnitWeekOfMonth; + case UCAL_DATE: return kCFCalendarUnitDay; // same value as UCAL_DAY_OF_MONTH + case UCAL_DAY_OF_YEAR: return kCFCalendarUnitDay; + case UCAL_DAY_OF_WEEK: return kCFCalendarUnitWeekday; + case UCAL_DAY_OF_WEEK_IN_MONTH: return kCFCalendarUnitWeekdayOrdinal; + case UCAL_HOUR: return kCFCalendarUnitHour; + case UCAL_HOUR_OF_DAY: return kCFCalendarUnitHour; + case UCAL_MINUTE: return kCFCalendarUnitMinute; + case UCAL_SECOND: return kCFCalendarUnitSecond; + case UCAL_ZONE_OFFSET: return kCFCalendarUnitTimeZone; + case UCAL_YEAR_WOY: return kCFCalendarUnitYearForWeekOfYear; + case UCAL_IS_LEAP_MONTH: return kCFCalendarUnitLeapMonth; + case UCAL_EXTENDED_YEAR: return kCFCalendarUnitYear; + } + return 0; +} + static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) { switch (ch) { case 'G': return UCAL_ERA; case 'y': return UCAL_YEAR; + case 'U': return UCAL_YEAR; + case 'r': return UCAL_YEAR; case 'M': return UCAL_MONTH; + case 'L': return UCAL_MONTH; case 'l': return UCAL_IS_LEAP_MONTH; case 'd': return UCAL_DAY_OF_MONTH; case 'h': return UCAL_HOUR; @@ -971,6 +1006,7 @@ static UCalendarDateFields __CFCalendarGetICUFieldCodeFromChar(char ch) { case 'W': return UCAL_WEEK_OF_MONTH; case 'Y': return UCAL_YEAR_WOY; case 'E': return UCAL_DAY_OF_WEEK; + case 'c': return UCAL_DAY_OF_WEEK; case 'D': return UCAL_DAY_OF_YEAR; case 'F': return UCAL_DAY_OF_WEEK_IN_MONTH; case 'a': return UCAL_AM_PM; @@ -1037,27 +1073,6 @@ static const char *__CFCalendarGetUnitName(CFCalendarUnit unit) { return "???"; } -static CFCalendarUnit __CFCalendarGetCalendarUnitFromChar(char ch) { - switch (ch) { - case 'G': return kCFCalendarUnitEra; - case 'y': return kCFCalendarUnitYear; - case 'Q': return kCFCalendarUnitQuarter; - case 'M': return kCFCalendarUnitMonth; - case 'l': return kCFCalendarUnitMonth; - case 'd': return kCFCalendarUnitDay; - case 'H': return kCFCalendarUnitHour; - case 'm': return kCFCalendarUnitMinute; - case 's': return kCFCalendarUnitSecond; - case '^': return kCFCalendarUnitWeek_Deprecated; - case 'w': return kCFCalendarUnitWeekOfYear; - case 'W': return kCFCalendarUnitWeekOfMonth; - case 'Y': return kCFCalendarUnitYearForWeekOfYear; - case 'E': return kCFCalendarUnitWeekday; - case 'F': return kCFCalendarUnitWeekdayOrdinal; - case '#': return kCFCalendarUnitNanosecond; - } - return (CFCalendarUnit)-1; -} #pragma GCC diagnostic pop CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { @@ -1116,7 +1131,7 @@ CFRange CFCalendarGetMinimumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit u CFRange CFCalendarGetMaximumRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit) { ICU_LOG(" // CFCalendarGetMaximumRangeOfUnit enter (%s)\n", __CFCalendarGetUnitName(unit)); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, CFRange, (NSCalendar *)calendar, _maximumRangeOfUnit:(NSCalendarUnit)unit); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, (NSCalendar *)calendar, _maximumRangeOfUnit:(NSCalendarUnit)unit); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); switch (unit) { #pragma GCC diagnostic push @@ -1289,6 +1304,11 @@ Boolean _CFCalendarAddComponentsV(CFCalendarRef calendar, /* inout */ CFAbsolute __cficu_ucal_clear(calendar->_cal); double startingInt; double startingFrac = modf(*atp, &startingInt); + if (startingFrac < 0) { + // `modf` returns negative integral and fractional parts when `*atp` is negative. In this case, we would wrongly turn the time backwards by adding the negative fractional part back after we're done with wrapping in `__CFCalendarAdd` below. To avoid this, ensure that `startingFrac` is always positive: subseconds do not contribute to the wrapping of a second, so they should always be additive to the time ahead. + startingFrac += 1.0; + startingInt -= 1.0; + } UDate udate = (startingInt + kCFAbsoluteTimeIntervalSince1970) * 1000.0; __cficu_ucal_setMillis(calendar->_cal, udate, &status); int32_t nanosecond = 0; @@ -1373,7 +1393,7 @@ Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsolu va_list args; va_start(args, componentDesc); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, Boolean, (NSCalendar *)calendar, _composeAbsoluteTime:atp :(const unsigned char *)componentDesc :args); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, (NSCalendar *)calendar, _composeAbsoluteTime:atp :(const unsigned char *)componentDesc :args); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); int32_t idx, cnt = strlen((char *)componentDesc); STACK_BUFFER_DECL(int32_t, vector, cnt); @@ -1386,10 +1406,11 @@ Boolean CFCalendarComposeAbsoluteTime(CFCalendarRef calendar, /* out */ CFAbsolu } Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime at, const char *componentDesc, ...) { + __CFCalendarValidateAndCapTimeRange(at); va_list args; va_start(args, componentDesc); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, Boolean, (NSCalendar *)calendar, _decomposeAbsoluteTime:at :(const unsigned char *)componentDesc :args); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, (NSCalendar *)calendar, _decomposeAbsoluteTime:at :(const unsigned char *)componentDesc :args); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); int32_t idx, cnt = strlen((char *)componentDesc); STACK_BUFFER_DECL(int32_t *, vector, cnt); @@ -1402,10 +1423,11 @@ Boolean CFCalendarDecomposeAbsoluteTime(CFCalendarRef calendar, CFAbsoluteTime a } Boolean CFCalendarAddComponents(CFCalendarRef calendar, /* inout */ CFAbsoluteTime *atp, CFOptionFlags options, const char *componentDesc, ...) { + __CFCalendarValidateAndCapTimeRange(*atp); va_list args; va_start(args, componentDesc); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, Boolean, (NSCalendar *)calendar, _addComponents:atp :options :(const unsigned char *)componentDesc :args); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, (NSCalendar *)calendar, _addComponents:atp :options :(const unsigned char *)componentDesc :args); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); int32_t idx, cnt = strlen((char *)componentDesc); STACK_BUFFER_DECL(int32_t, vector, cnt); @@ -1453,10 +1475,13 @@ CF_PRIVATE CFDateRef _CFCalendarCreateDateByAddingValueOfUnitToDate(CFCalendarRe } Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime startingAT, CFAbsoluteTime resultAT, CFOptionFlags options, const char *componentDesc, ...) { + __CFCalendarValidateAndCapTimeRange(startingAT); + __CFCalendarValidateAndCapTimeRange(resultAT); + va_list args; va_start(args, componentDesc); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, Boolean, (NSCalendar *)calendar, _diffComponents:startingAT :resultAT :options :(const unsigned char *)componentDesc :args); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, (NSCalendar *)calendar, _diffComponents:startingAT :resultAT :options :(const unsigned char *)componentDesc :args); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); int32_t idx, cnt = strlen((char *)componentDesc); STACK_BUFFER_DECL(int32_t *, vector, cnt); @@ -1470,8 +1495,9 @@ Boolean CFCalendarGetComponentDifference(CFCalendarRef calendar, CFAbsoluteTime } Boolean CFCalendarGetTimeRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit unit, CFAbsoluteTime at, CFAbsoluteTime *startp, CFTimeInterval *tip) { + __CFCalendarValidateAndCapTimeRange(at); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, Boolean, (NSCalendar *)calendar, _rangeOfUnit:(NSCalendarUnit)unit startTime:startp interval:tip forAT:at); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), Boolean, (NSCalendar *)calendar, _rangeOfUnit:(NSCalendarUnit)unit startTime:startp interval:tip forAT:at); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); const CFTimeInterval inf_ti = 4398046511104.0; @@ -2925,6 +2951,26 @@ CF_PRIVATE CFDateRef _CFCalendarCreateDateByAddingDateComponentsToDate(CFAllocat return NULL; } +CFCalendarUnit _CFCalendarGetUnitsFromDateFormat(CFStringRef format) { + CFCalendarUnit units = 0; + CFIndex cnt = CFStringGetLength(format); + for (CFIndex i = 0; i < cnt; ++i) { + UniChar c = CFStringGetCharacterAtIndex(format, i); +#if __HAS_APPLE_ICU__ + UDateFormatField fmt_field = udat_patternCharToDateFormatField(c); + UCalendarDateFields ucal_field = udat_toCalendarDateField(fmt_field); +#else + UCalendarDateFields ucal_field = __CFCalendarGetICUFieldCodeFromChar(c); +#endif + CFCalendarUnit unit = __CFCalendarUnitFromICUDateFields(ucal_field); + if (unit) { + units |= unit; + } + } + + return units; +} + #pragma mark - #if 0 @@ -2955,15 +3001,17 @@ C: for range, allowed in 10.6 #endif CFRange CFCalendarGetRangeOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { + __CFCalendarValidateAndCapTimeRange(at); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, CFRange, (NSCalendar *)calendar, _rangeOfUnit:(NSCalendarUnit)smallerUnit inUnit:(NSCalendarUnit)biggerUnit forAT:at); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFRange, (NSCalendar *)calendar, _rangeOfUnit:(NSCalendarUnit)smallerUnit inUnit:(NSCalendarUnit)biggerUnit forAT:at); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); return __CFCalendarGetRangeOfUnit3(calendar, smallerUnit, biggerUnit, at); } CFIndex CFCalendarGetOrdinalityOfUnit(CFCalendarRef calendar, CFCalendarUnit smallerUnit, CFCalendarUnit biggerUnit, CFAbsoluteTime at) { + __CFCalendarValidateAndCapTimeRange(at); // Note: We do not toll-free bridge for Swift - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFCalendar, CFIndex, (NSCalendar *)calendar, _ordinalityOfUnit:(NSCalendarUnit)smallerUnit inUnit:(NSCalendarUnit)biggerUnit forAT:at); + CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, (NSCalendar *)calendar, _ordinalityOfUnit:(NSCalendarUnit)smallerUnit inUnit:(NSCalendarUnit)biggerUnit forAT:at); __CFGenericValidateType(calendar, CFCalendarGetTypeID()); return __CFCalendarGetOrdinalityOfUnit3(calendar, smallerUnit, biggerUnit, at); } diff --git a/CoreFoundation/Locale.subproj/CFCalendar_Enumerate.c b/CoreFoundation/Locale.subproj/CFCalendar_Enumerate.c index e7251664ef..39df0384ed 100644 --- a/CoreFoundation/Locale.subproj/CFCalendar_Enumerate.c +++ b/CoreFoundation/Locale.subproj/CFCalendar_Enumerate.c @@ -1,7 +1,7 @@ /* CFCalendar_Enumerate.c - Copyright (c) 2004-2018, Apple Inc. and the Swift project authors + Copyright (c) 2004-2019, Apple Inc. and the Swift project authors - Portions Copyright (c) 2014-2018, Apple Inc. and the Swift project authors + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors Licensed under Apache License v2.0 with Runtime Library Exception See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors @@ -1455,7 +1455,7 @@ static CFDateRef _Nullable _CFCalendarCreateBumpedDateUpToNextHigherUnitInCompon // W5 28 29 30 31 25 26 27 28 // // Consider searching for `WoM == 1` when searchingDate is *in* W1 of January. Because we're looking to advance to next month, we could simply add a month, right? - // Adding a month from Monday, January 1st lands us on Thursday, February 1st; from Thursday, January 2nd we get Friday, February 2nd, etc. Note though that for January 4th, 5th, and 6th, adding a month lands us in **W2** of February! + // Adding a month from Monday, January 1st lands us on Thursday, February 1st; from Tuesday, January 2nd we get Friday, February 2nd, etc. Note though that for January 4th, 5th, and 6th, adding a month lands us in **W2** of February! // This means that if we continue searching forward from there, we'll have completely skipped W1 of February as a candidate week, and search forward until we hit W1 of March. This is incorrect. // // What we really want is to skip to the _start_ of February and search from there -- if we undershoot, we can always keep looking. diff --git a/CoreFoundation/Locale.subproj/CFCalendar_Internal.h b/CoreFoundation/Locale.subproj/CFCalendar_Internal.h index fd932699bb..3a80eade7c 100644 --- a/CoreFoundation/Locale.subproj/CFCalendar_Internal.h +++ b/CoreFoundation/Locale.subproj/CFCalendar_Internal.h @@ -91,6 +91,8 @@ CF_ENUM(CFOptionFlags) { CF_PRIVATE void __CFCalendarSetupCal(CFCalendarRef calendar); CF_PRIVATE void __CFCalendarZapCal(CFCalendarRef calendar); +CF_CROSS_PLATFORM_EXPORT Boolean _CFCalendarInitWithIdentifier(CFCalendarRef calendar, CFStringRef identifier); + CF_PRIVATE CFCalendarRef _CFCalendarCreateCopy(CFAllocatorRef allocator, CFCalendarRef calendar); CF_CROSS_PLATFORM_EXPORT Boolean _CFCalendarComposeAbsoluteTimeV(CFCalendarRef calendar, /* out */ CFAbsoluteTime *atp, const char *componentDesc, int32_t *vector, int32_t count); diff --git a/CoreFoundation/Locale.subproj/CFDateComponents.c b/CoreFoundation/Locale.subproj/CFDateComponents.c index 41f976701f..19bf0fd08a 100644 --- a/CoreFoundation/Locale.subproj/CFDateComponents.c +++ b/CoreFoundation/Locale.subproj/CFDateComponents.c @@ -1,7 +1,7 @@ /* CFDateComponents.c - Copyright (c) 2004-2017, Apple Inc. and the Swift project authors + Copyright (c) 2004-2019, Apple Inc. and the Swift project authors - Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors Licensed under Apache License v2.0 with Runtime Library Exception See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors @@ -14,9 +14,7 @@ #include "CFDateComponents.h" #include "CFInternal.h" #include "CFCalendar_Internal.h" - -/* Runtime setup */ -static CFTypeID __kCFDateComponentsTypeID = _kCFRuntimeNotATypeID; +#include "CFRuntime_Internal.h" static Boolean __CFDateComponentsEqual(CFTypeRef cf1, CFTypeRef cf2) { assert(NULL != cf1); @@ -153,11 +151,7 @@ const CFRuntimeClass __CFDateComponentsClass = { }; CFTypeID CFDateComponentsGetTypeID(void) { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - __kCFDateComponentsTypeID = _CFRuntimeRegisterClass(&__CFDateComponentsClass); - }); - return __kCFDateComponentsTypeID; + return _kCFRuntimeIDCFDateComponents; } /* End Runtime setup */ diff --git a/CoreFoundation/Locale.subproj/CFDateComponents.h b/CoreFoundation/Locale.subproj/CFDateComponents.h index c70a32c3c7..0a5cf7233e 100644 --- a/CoreFoundation/Locale.subproj/CFDateComponents.h +++ b/CoreFoundation/Locale.subproj/CFDateComponents.h @@ -1,7 +1,7 @@ /* CFDateComponents.h - Copyright (c) 2004-2017, Apple Inc. and the Swift project authors + Copyright (c) 2004-2019, Apple Inc. and the Swift project authors - Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors Licensed under Apache License v2.0 with Runtime Library Exception See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors diff --git a/CoreFoundation/Locale.subproj/CFDateFormatter.c b/CoreFoundation/Locale.subproj/CFDateFormatter.c index 97e70ab2ef..d167a47255 100644 --- a/CoreFoundation/Locale.subproj/CFDateFormatter.c +++ b/CoreFoundation/Locale.subproj/CFDateFormatter.c @@ -100,16 +100,17 @@ static Boolean useTemplatePatternGenerator(CFLocaleRef locale, void(^work)(UDate static void _CFDateFormatterStripAMPMIndicators(UniChar **bpat, int32_t *bpatlen, CFIndex bufferSize) { //scan + CFCharacterSetRef whitespaceChars = CFCharacterSetGetPredefined(kCFCharacterSetWhitespace); for (CFIndex idx = 0; idx < *bpatlen; idx++) { - if (((*bpat)[idx] == 'a') || ((*bpat)[idx] == 'b') || ((*bpat)[idx] == 'B') || ((*bpat)[idx] == 'C')) { - + UniChar c = (*bpat)[idx]; + if (c == 'a' || c == 'b' || c == 'B' || c == 'C') { //back up - while ((*bpat)[idx - 1] == ' ') { + while (CFCharacterSetIsCharacterMember(whitespaceChars, (*bpat)[idx - 1])) { idx--; } - + //shift - for (; (*bpat)[idx] == ' ' || (*bpat)[idx] == 'a' || (*bpat)[idx] == 'b' || (*bpat)[idx] == 'B' || (*bpat)[idx] == 'C'; idx++) { + for (c = (*bpat)[idx]; c == 'a' || c == 'b' || c == 'B' || c == 'C' || CFCharacterSetIsCharacterMember(whitespaceChars, c); idx++, c = (*bpat)[idx]) { for (CFIndex shiftIdx = idx; shiftIdx < *bpatlen && shiftIdx + 1 < bufferSize; shiftIdx++) { (*bpat)[shiftIdx] = (*bpat)[shiftIdx + 1]; } @@ -2173,4 +2174,54 @@ CFTypeRef CFDateFormatterCopyProperty(CFDateFormatterRef formatter, CFStringRef return NULL; } +CFStringRef _CFDateFormatterCreateSkeletonFromTemplate(CFStringRef tmplateString, CFLocaleRef locale, UErrorCode *outErrorCode) { + CFIndex const tmpltLen = CFStringGetLength(tmplateString); + if (tmpltLen == 0) { + if (outErrorCode) { + *outErrorCode = U_ILLEGAL_ARGUMENT_ERROR; + } + return NULL; + } + + __block CFStringRef result = NULL; + Boolean success = useTemplatePatternGenerator(locale, ^(UDateTimePatternGenerator *ptg) { +#define BUFFER_SIZE 768 + + SAFE_STACK_BUFFER_DECL(UChar, ubuffer, tmpltLen, BUFFER_SIZE); + UChar const *ustr = (UChar *)CFStringGetCharactersPtr(tmplateString); + if (ustr == NULL) { + CFStringGetCharacters(tmplateString, CFRangeMake(0, tmpltLen), (UniChar *)ubuffer); + ustr = ubuffer; + } + + UChar skel[BUFFER_SIZE] = {0}; + int32_t patlen = tmpltLen; + UErrorCode status = U_ZERO_ERROR; + int32_t skelLen = __cficu_udatpg_getSkeleton(ptg, ustr, patlen, skel, sizeof(skel) / sizeof(skel[0]), &status); + if (U_SUCCESS(status)) { + result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, skel, skelLen); + } else if (status == U_BUFFER_OVERFLOW_ERROR) { + UChar *largerBuffer = calloc(skelLen + 1, sizeof(UChar)); + skelLen = __cficu_udatpg_getSkeleton(ptg, ustr, patlen, skel, sizeof(skel) / sizeof(skel[0]), &status); + if (U_SUCCESS(status)) { + result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, largerBuffer, skelLen); + } else { + if (outErrorCode) { + *outErrorCode = status; + } + } + free(largerBuffer); + } else { + *outErrorCode = status; + } + + SAFE_STACK_BUFFER_CLEANUP(ubuffer); + }); + if (!success && result != NULL) { + CFRelease(result); + result = NULL; + } + + return result; +} diff --git a/CoreFoundation/Locale.subproj/CFDateInterval.c b/CoreFoundation/Locale.subproj/CFDateInterval.c index 7b5d61e140..00aaf201c3 100644 --- a/CoreFoundation/Locale.subproj/CFDateInterval.c +++ b/CoreFoundation/Locale.subproj/CFDateInterval.c @@ -1,7 +1,7 @@ /* CFDateInterval.c - Copyright (c) 2017-2018, Apple Inc. and the Swift project authors + Copyright (c) 2017-2019, Apple Inc. and the Swift project authors - Portions Copyright (c) 2017-2018, Apple Inc. and the Swift project authors + Portions Copyright (c) 2017-2019, Apple Inc. and the Swift project authors Licensed under Apache License v2.0 with Runtime Library Exception See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors diff --git a/CoreFoundation/Locale.subproj/CFDateInterval.h b/CoreFoundation/Locale.subproj/CFDateInterval.h index 81b8c80117..920963763d 100644 --- a/CoreFoundation/Locale.subproj/CFDateInterval.h +++ b/CoreFoundation/Locale.subproj/CFDateInterval.h @@ -1,7 +1,7 @@ /* CFDateInterval.h - Copyright (c) 2004-2017, Apple Inc. and the Swift project authors + Copyright (c) 2004-2019, Apple Inc. and the Swift project authors - Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors Licensed under Apache License v2.0 with Runtime Library Exception See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors diff --git a/CoreFoundation/Locale.subproj/CFICULogging.h b/CoreFoundation/Locale.subproj/CFICULogging.h index eb613283b2..d521debf79 100644 --- a/CoreFoundation/Locale.subproj/CFICULogging.h +++ b/CoreFoundation/Locale.subproj/CFICULogging.h @@ -110,4 +110,8 @@ #define __cficu_ureldatefmt_formatNumeric ureldatefmt_formatNumeric #define __cficu_ureldatefmt_format ureldatefmt_format #define __cficu_ureldatefmt_close ureldatefmt_close +// ulistfmt +#define __cficu_ulistfmt_open ulistfmt_open +#define __cficu_ulistfmt_close ulistfmt_close +#define __cficu_ulistfmt_format ulistfmt_format #endif diff --git a/CoreFoundation/Locale.subproj/CFListFormatter.c b/CoreFoundation/Locale.subproj/CFListFormatter.c new file mode 100644 index 0000000000..39eb218a86 --- /dev/null +++ b/CoreFoundation/Locale.subproj/CFListFormatter.c @@ -0,0 +1,155 @@ +/* CFListFormatter.h + Copyright (c) 2018-2019, Apple Inc. and the Swift project authors + + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + */ + +#import "CFListFormatter.h" + +#import "CFICULogging.h" +#import "CFInternal.h" +#import "CFRuntime_Internal.h" +#import + +#define BUFFER_SIZE 256 +#define RESULT_BUFFER_SIZE 768 + +struct __CFListFormatter { + CFRuntimeBase _base; + CFLocaleRef _locale; +}; + +static void __CFListFormatterDeallocate(CFTypeRef cf) { + assert(cf != NULL); + CFListFormatterRef formatter = (CFListFormatterRef)cf; + if (formatter->_locale) { CFRelease(formatter->_locale); } +} + +static CFStringRef __CFListFormatterCopyDescription(CFTypeRef cf) { + assert(cf != NULL); + CFListFormatterRef formatter = (CFListFormatterRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("[%p]"), formatter, CFGetAllocator(formatter)); +} + +CFTypeID _CFListFormatterGetTypeID(void) { + return _kCFRuntimeIDCFListFormatter; +} + +const CFRuntimeClass __CFListFormatterClass = { + 0, + "CFListFormatter", + NULL, // init + NULL, // copy + __CFListFormatterDeallocate, + NULL, // equal + NULL, // hash + NULL, // copy formatting desc + __CFListFormatterCopyDescription +}; + +CFListFormatterRef _CFListFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale) { + assert(allocator != NULL); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + + assert(locale != NULL); + __CFGenericValidateType(locale, CFLocaleGetTypeID()); + + size_t size = sizeof(struct __CFListFormatter) - sizeof(CFRuntimeBase); + struct __CFListFormatter *memory = (struct __CFListFormatter *)_CFRuntimeCreateInstance(allocator, _CFListFormatterGetTypeID(), size, NULL); + if (memory == NULL) { + return NULL; + } + + memory->_locale = CFRetain(locale); + + return memory; +} + +CFStringRef _CFListFormatterCreateStringByJoiningStrings(CFAllocatorRef allocator, const CFListFormatterRef formatter, const CFArrayRef strings) { + CFAssert1(strings != NULL, __kCFLogAssertion, "%s(): strings should not be NULL", __PRETTY_FUNCTION__); + if (strings == NULL) { + return NULL; + } + + CFIndex const count = CFArrayGetCount(strings); + if (!count) { + return CFSTR(""); + } + + CFLocaleRef locale = formatter->_locale; + CFAssert1(locale != NULL, __kCFLogAssertion, "%s(): locale should not be NULL", __PRETTY_FUNCTION__); + + UChar** ucharStrings = malloc(sizeof(UChar *) * count); + int32_t* uStringLengths = malloc(sizeof(int32_t) * count); + bool *needsFree = calloc(count, sizeof(bool)); + + CFStringRef string; + for (int i = 0; i < count; ++i) { + string = CFArrayGetValueAtIndex(strings, i); + CFIndex const len = CFStringGetLength(string); + UChar const *ucharString = CFStringGetCharactersPtr(string); + if (ucharString == NULL) { + UChar *ubuffer = malloc(sizeof(UChar) * len); + CFStringGetCharacters(string, CFRangeMake(0, len), ubuffer); + ucharString = ubuffer; + needsFree[i] = true; + } + + uStringLengths[i] = len; + ucharStrings[i] = (UChar *)ucharString; + } + + UErrorCode status = U_ZERO_ERROR; + CFStringRef localeName = CFLocaleGetIdentifier(locale); + char const *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); + char buffer[BUFFER_SIZE]; + if (NULL == cstr && CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) { + cstr = buffer; + } + + UListFormatter *fmt = __cficu_ulistfmt_open(cstr, &status); + void (^cleanUp)(void) = ^{ + if (fmt) { + __cficu_ulistfmt_close(fmt); + } + for (int i = 0; i < count; ++i) { + UChar *ucharString = ucharStrings[i]; + if (ucharString && needsFree[i]) { + free(ucharString); + } + } + free(needsFree); + free(ucharStrings); + free(uStringLengths); + }; + + if (U_FAILURE(status)) { + cleanUp(); + return NULL; + } + + CFAssert1(fmt != NULL, __kCFLogAssertion, "%s(): UListFormatter should not be NULL", __PRETTY_FUNCTION__); + + CFStringRef result = NULL; + UChar resultBuffer[RESULT_BUFFER_SIZE + 1]; + status = U_ZERO_ERROR; + int32_t length = __cficu_ulistfmt_format(fmt, (const UChar **)ucharStrings, uStringLengths, count, resultBuffer, RESULT_BUFFER_SIZE, &status); + if (U_SUCCESS(status)) { + result = CFStringCreateWithCharacters(allocator, resultBuffer, length); + } else if (status == U_BUFFER_OVERFLOW_ERROR || length > count) { + status = U_ZERO_ERROR; + UChar *largeBuffer = malloc(sizeof(UChar) * (length + 1)); + length = __cficu_ulistfmt_format(fmt, (const UChar **)ucharStrings, uStringLengths, count, largeBuffer, length + 1, &status); + if (U_SUCCESS(status)) { + result = CFStringCreateWithCharacters(allocator, largeBuffer, length); + } + free(largeBuffer); + } + + cleanUp(); + + return result; +} diff --git a/CoreFoundation/Locale.subproj/CFListFormatter.h b/CoreFoundation/Locale.subproj/CFListFormatter.h new file mode 100644 index 0000000000..fffa436eaa --- /dev/null +++ b/CoreFoundation/Locale.subproj/CFListFormatter.h @@ -0,0 +1,32 @@ +/* CFListFormatter.h + Copyright (c) 2018-2019, Apple Inc. and the Swift project authors + + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + */ + +#ifndef __COREFOUNDATION_CFLISTFORMATTER_h +#define __COREFOUNDATION_CFLISTFORMATTER_h + +#include +#include + +CF_IMPLICIT_BRIDGING_ENABLED +CF_EXTERN_C_BEGIN +CF_ASSUME_NONNULL_BEGIN + +typedef struct CF_BRIDGED_TYPE(id) __CFListFormatter *CFListFormatterRef; + +CF_EXPORT +CFTypeID _CFListFormatterGetTypeID(void); + +CFListFormatterRef _Nullable _CFListFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale); +CFStringRef _Nullable _CFListFormatterCreateStringByJoiningStrings(CFAllocatorRef allocator, CFListFormatterRef formatter, const CFArrayRef strings); + +CF_ASSUME_NONNULL_END +CF_EXTERN_C_END +CF_IMPLICIT_BRIDGING_DISABLED + +#endif // __COREFOUNDATION_CFLISTFORMATTER_h diff --git a/CoreFoundation/Locale.subproj/CFLocale.c b/CoreFoundation/Locale.subproj/CFLocale.c index 8ba30ad516..05d26765d8 100644 --- a/CoreFoundation/Locale.subproj/CFLocale.c +++ b/CoreFoundation/Locale.subproj/CFLocale.c @@ -65,6 +65,7 @@ CF_PRIVATE CFCalendarRef _CFCalendarCreateCoWWithIdentifier(CFStringRef identifi CONST_STRING_DECL(kCFLocaleCurrentLocaleDidChangeNotification, "kCFLocaleCurrentLocaleDidChangeNotification") +CF_PRIVATE void __CFLocalePrefsChanged(CFNotificationCenterRef, void *, CFStringRef, const void *, CFDictionaryRef); static const char * const kCalendarKeyword = "calendar"; static const char * const kCollationKeyword = "collation"; @@ -150,17 +151,26 @@ struct __CFLocale { _Atomic(CFMutableDictionaryRef) _cache; CFDictionaryRef _prefs; CFLock_t _lock; - Boolean _nullLocale; + // True if this locale is **NOT** one of the "special" languages that + // requires special handing during case mapping: + // - "az": Azerbaijani + // - "lt": Lithuanian + // - "tr": Turkish + // - "nl": Dutch + // - "el": Greek + // See `CFUniCharMapCaseTo` + // See https://www.unicode.org/Public/UNIDATA/SpecialCasing.txt + Boolean _doesNotRequireSpecialCaseHandling; }; -CF_PRIVATE Boolean __CFLocaleGetNullLocale(struct __CFLocale *locale) { - CF_OBJC_FUNCDISPATCHV(CFLocaleGetTypeID(), Boolean, (NSLocale *)locale, _nullLocale); - return locale->_nullLocale; +CF_PRIVATE Boolean __CFLocaleGetDoesNotRequireSpecialCaseHandling(struct __CFLocale *locale) { + CF_OBJC_FUNCDISPATCHV(CFLocaleGetTypeID(), Boolean, (NSLocale *)locale, _doesNotRequireSpecialCaseHandling); + return locale->_doesNotRequireSpecialCaseHandling; } -CF_PRIVATE void __CFLocaleSetNullLocale(struct __CFLocale *locale) { - CF_OBJC_FUNCDISPATCHV(CFLocaleGetTypeID(), void, (NSLocale *)locale, _setNullLocale); - locale->_nullLocale = true; +CF_PRIVATE void __CFLocaleSetDoesNotRequireSpecialCaseHandling(struct __CFLocale *locale) { + CF_OBJC_FUNCDISPATCHV(CFLocaleGetTypeID(), void, (NSLocale *)locale, _setDoesNotRequireSpecialCaseHandling); + locale->_doesNotRequireSpecialCaseHandling = true; } /* Flag bits */ @@ -294,11 +304,30 @@ extern CFDictionaryRef __CFXPreferencesCopyCurrentApplicationStateWithDeadlockAv static _Atomic(CFLocaleRef) _CFLocaleCurrent_ = NULL; CF_INLINE CFLocaleRef _cachedCurrentLocale() { - return atomic_load_explicit(&_CFLocaleCurrent_, memory_order_relaxed); + return atomic_load(&_CFLocaleCurrent_); } -static void _setCachedCurrentLocale(CFLocaleRef newLocale) { - atomic_store(&_CFLocaleCurrent_, newLocale); //no release, cached locales are immortal +// Returns true if `newLocale` is made immortal. +static Boolean _setCachedCurrentLocaleAndMakeImmortal(CFLocaleRef newLocale) { + Boolean success; + if (newLocale) { + CFLocaleRef cachedLocale = NULL; + success = atomic_compare_exchange_strong(&_CFLocaleCurrent_, &cachedLocale, newLocale); + if (success) { +#if !DEPLOYMENT_RUNTIME_SWIFT + __CFRuntimeSetRC((CFTypeRef)newLocale, 0); +#else + // Swift does not support immortal objects yet; add an unbalanced retain instead. + CFRetain((CFTypeRef)newLocale); +#endif + + } + } else { + success = false; + atomic_store(&_CFLocaleCurrent_, newLocale); + } + + return success; } @@ -310,7 +339,6 @@ static void _setCachedCurrentLocale(CFLocaleRef newLocale) { #define FALLBACK_LOCALE_NAME CFSTR("en_US") #endif -#if TARGET_OS_MAC || TARGET_OS_WIN32 static CFStringRef _CFLocaleCopyLocaleIdentifierByAddingLikelySubtags(CFStringRef localeID) { if (!localeID) { @@ -539,11 +567,8 @@ CFStringRef _CFLocaleCreateLocaleIdentiferByReplacingLanguageCodeAndScriptCode(C } return localeID; } -#endif -#if TARGET_OS_MAC || TARGET_OS_WIN32 static CFArrayRef _CFLocaleCopyPreferredLanguagesFromPrefs(CFArrayRef languagesArray); -#endif /// Creates a new locale identifier by identifying the most preferred localization (using `availableLocalizations` and `preferredLanguages`) and then creating a locale based on the most preferred localization, while retaining any relevant attributes from `preferredLocaleID`, e.g. if `availableLocalizations` is `[ "en", "fr", "de" ]`, `preferredLanguages` is `[ "ar-AE", "en-AE" ]`, `preferredLocaleID` is `ar_AE@numbers=arab;calendar=islamic-civil`, it will return `en_AE@calendar=islamic-civil`, i.e. the language will be matched to `en` since that’s the only available localization that matches, `calendar` will be retained since it’s language-agnostic, but `numbers` will be discarded because the `arab` numbering system is not valid for `en`. static CFStringRef _CFLocaleCreateLocaleIdentifierForAvailableLocalizations(CFArrayRef availableLocalizations, CFArrayRef preferredLanguages, CFStringRef preferredLocaleID, CFArrayRef *outCanonicalizedPreferredLanguages) { @@ -593,6 +618,34 @@ static CFStringRef _CFLocaleCreateLocaleIdentifierForAvailableLocalizations(CFAr return result; } +CFLocaleRef _CFLocaleCreateLikeCurrentWithBundleLocalizations(CFArrayRef availableLocalizations, Boolean allowsMixedLocalizations) { + CFLocaleRef locale = NULL; + + if (allowsMixedLocalizations) { + locale = _CFLocaleCopyPreferred(); + } else { + CFArrayRef preferredLanguages = CFLocaleCopyPreferredLanguages(); + CFStringRef preferredLocaleID = CFPreferencesCopyAppValue(CFSTR("AppleLocale"), kCFPreferencesCurrentApplication); + + CFStringRef identifier = _CFLocaleCreateLocaleIdentifierForAvailableLocalizations(availableLocalizations, preferredLanguages, preferredLocaleID, NULL); + if (identifier) { + locale = CFLocaleCreate(kCFAllocatorSystemDefault, identifier); + } + + if (identifier) { + CFRelease(identifier); + } + if (preferredLocaleID) { + CFRelease(preferredLocaleID); + } + if (preferredLanguages) { + CFRelease(preferredLanguages); + } + } + + return locale; +} + static CFLocaleRef _CFLocaleCopyCurrentGuts(CFStringRef name, Boolean useCache, CFDictionaryRef overridePrefs, Boolean disableBundleMatching) { /* NOTE: calling any CFPreferences function, or any function which calls into a CFPreferences function, *except* for __CFXPreferencesCopyCurrentApplicationStateWithDeadlockAvoidance (and accepting backstop values if its outparam is false), will deadlock. This is because CFPreferences calls os_log_*, which calls -descriptionWithLocale:, which calls CFLocaleCopyCurrent. @@ -639,7 +692,7 @@ static CFLocaleRef _CFLocaleCopyCurrentGuts(CFStringRef name, Boolean useCache, ident = NULL; } else { // We'll replace what's in the cache with ident below. - _setCachedCurrentLocale(NULL); + _setCachedCurrentLocaleAndMakeImmortal(NULL); cached = NULL; } } @@ -663,15 +716,7 @@ static CFLocaleRef _CFLocaleCopyCurrentGuts(CFStringRef name, Boolean useCache, if (ident) CFRelease(ident); return NULL; } - if (useCache) { - // Make this object immortal: -#if !DEPLOYMENT_RUNTIME_SWIFT - __CFRuntimeSetRC((CFTypeRef)locale, 0); -#else - // Swift does not support immortal objects yet; add an unbalanced retain instead. - CFRetain((CFTypeRef)locale); -#endif - } + __CFLocaleSetType(locale, __kCFLocaleUser); if (!ident) { @@ -691,16 +736,18 @@ static CFLocaleRef _CFLocaleCopyCurrentGuts(CFStringRef name, Boolean useCache, locale->_identifier = ident; locale->_prefs = prefs; locale->_lock = CFLockInit; - locale->_nullLocale = false; + locale->_doesNotRequireSpecialCaseHandling = false; if (useCache) { - if (NULL == _cachedCurrentLocale()) { - _setCachedCurrentLocale(locale); + Boolean success = _setCachedCurrentLocaleAndMakeImmortal(locale); + if (success) { + // useCache is enabled, the locale is made immortal. + // The clang analyzer doesn't know about __CFRuntimeSetRC, though, so it sees overwriting locale below as a leak. + _CLANG_ANALYZER_IGNORE_RETAIN(locale); + } else { + // We already have a cached locale. Release the newly created one before overwriting it below. + CFRelease(locale); } - - // useCache is enabled, the locale is made immortal. - // The clang analyzer doesn't know about __CFRuntimeSetRC, though, so it sees overwriting locale below as a leak. - _CLANG_ANALYZER_IGNORE_RETAIN(locale); locale = (struct __CFLocale *)_cachedCurrentLocale(); } return locale; @@ -835,7 +882,7 @@ static CFLocaleRef _CFLocaleCreateCopyGuts(CFAllocatorRef allocator, CFLocaleRef CFDictionaryRef prefs = __CFLocaleGetPrefs(locale); loc->_prefs = prefs ? CFRetain(prefs) : NULL; loc->_lock = CFLockInit; - loc->_nullLocale = locale->_nullLocale; + loc->_doesNotRequireSpecialCaseHandling = locale->_doesNotRequireSpecialCaseHandling; return (CFLocaleRef)loc; } @@ -1169,7 +1216,6 @@ _CFLocaleCalendarDirection _CFLocaleGetCalendarDirection(void) { #endif } -#if TARGET_OS_MAC || TARGET_OS_WIN32 static CFArrayRef _CFLocaleCopyPreferredLanguagesFromPrefs(CFArrayRef languagesArray) { CFMutableArrayRef newArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); if (languagesArray && (CFArrayGetTypeID() == CFGetTypeID(languagesArray))) { @@ -1186,10 +1232,8 @@ static CFArrayRef _CFLocaleCopyPreferredLanguagesFromPrefs(CFArrayRef languagesA } return newArray; } -#endif static CFArrayRef __CFLocaleCopyPreferredLanguagesForCurrentUser(Boolean forCurrentUser) { -#if TARGET_OS_MAC || TARGET_OS_WIN32 CFArrayRef languagesArray = NULL; if (forCurrentUser) { languagesArray = (CFArrayRef)CFPreferencesCopyValue(CFSTR("AppleLanguages"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); @@ -1199,9 +1243,6 @@ static CFArrayRef __CFLocaleCopyPreferredLanguagesForCurrentUser(Boolean forCurr CFArrayRef result = _CFLocaleCopyPreferredLanguagesFromPrefs(languagesArray); if (languagesArray) CFRelease(languagesArray); return result; -#else - return CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); -#endif } CFArrayRef CFLocaleCopyPreferredLanguages(void) { @@ -1570,7 +1611,7 @@ static void __CFLocaleGetPreferencesForMeasurementSystem(UMeasurementSystem meas } #endif -#if (U_ICU_VERSION_MAJOR_NUM > 54 || !defined(CF_OPEN_SOURCE)) && TARGET_OS_MAC +#if U_ICU_VERSION_MAJOR_NUM > 54 || (DEPLOYMENT_RUNTIME_OBJC && TARGET_OS_MAC) static bool _CFLocaleGetTemperatureUnitForPreferences(CFTypeRef temperaturePref, bool *outCelsius) { if (temperaturePref) { if (CFEqual(temperaturePref, kCFLocaleTemperatureUnitCelsius)) { @@ -1585,7 +1626,7 @@ static bool _CFLocaleGetTemperatureUnitForPreferences(CFTypeRef temperaturePref, } #endif -#if (U_ICU_VERSION_MAJOR_NUM > 54) || (!defined(CF_OPEN_SOURCE) && TARGET_OS_MAC) +#if U_ICU_VERSION_MAJOR_NUM > 54 || (DEPLOYMENT_RUNTIME_OBJC && TARGET_OS_MAC) static CFStringRef _CFLocaleGetTemperatureUnitName(bool celsius) { return celsius? kCFLocaleTemperatureUnitCelsius: kCFLocaleTemperatureUnitFahrenheit; } diff --git a/CoreFoundation/Locale.subproj/CFLocaleIdentifier.c b/CoreFoundation/Locale.subproj/CFLocaleIdentifier.c index ac48113fc6..56f04db25f 100644 --- a/CoreFoundation/Locale.subproj/CFLocaleIdentifier.c +++ b/CoreFoundation/Locale.subproj/CFLocaleIdentifier.c @@ -1980,6 +1980,7 @@ Boolean CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(CFStringRef localeI languagelength = strlen(searchString); // in case it got truncated icuStatus = U_ZERO_ERROR; componentLength = uloc_getScript( localeCString, componentString, sizeof(componentString), &icuStatus ); + Boolean foundScript = false; if ( U_FAILURE(icuStatus) || componentLength == 0 ) { icuStatus = U_ZERO_ERROR; componentLength = uloc_getCountry( localeCString, componentString, sizeof(componentString), &icuStatus ); @@ -1990,7 +1991,9 @@ Boolean CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(CFStringRef localeI componentLength = 0; } } - } + } else { + foundScript = true; + } // Append whichever other component we first found if (componentLength > 0) { @@ -2000,8 +2003,11 @@ Boolean CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(CFStringRef localeI // Search foundEntryPtr = (const LocaleToLegacyCodes *)bsearch( &searchEntry, localeToLegacyCodes, kNumLocaleToLegacyCodes, sizeof(LocaleToLegacyCodes), CompareLocaleToLegacyCodesEntries ); - if (foundEntryPtr == NULL && (int32_t) strlen(searchString) > languagelength) { - // truncate to language al;one and try again + + // Do not try fallback if string encoding is requested AND a script is present in the passed-in locale since the script might affect the string encoding: + BOOL lookingForScript = foundScript && stringEncoding != NULL; + if (foundEntryPtr == NULL && (int32_t) strlen(searchString) > languagelength && !lookingForScript) { + // Otherwise truncate to language alone and try again searchString[languagelength] = 0; foundEntryPtr = (const LocaleToLegacyCodes *)bsearch( &searchEntry, localeToLegacyCodes, kNumLocaleToLegacyCodes, sizeof(LocaleToLegacyCodes), CompareLocaleToLegacyCodesEntries ); } diff --git a/CoreFoundation/Locale.subproj/CFNumberFormatter.c b/CoreFoundation/Locale.subproj/CFNumberFormatter.c index 7f109b9d11..adfba4a0f0 100644 --- a/CoreFoundation/Locale.subproj/CFNumberFormatter.c +++ b/CoreFoundation/Locale.subproj/CFNumberFormatter.c @@ -125,11 +125,9 @@ CFNumberFormatterRef CFNumberFormatterCreate(CFAllocatorRef allocator, CFLocaleR return NULL; } if (kCFNumberFormatterNoStyle == style) { - UChar ubuff[1]; status = U_ZERO_ERROR; - ubuff[0] = '#'; - - __cficu_unum_applyPattern(memory->_nf, false, ubuff, 1, NULL, &status); + UChar pound = '#'; + __cficu_unum_applyPattern(memory->_nf, false, £, 1, NULL, &status); __cficu_unum_setAttribute(memory->_nf, UNUM_MAX_INTEGER_DIGITS, 42); __cficu_unum_setAttribute(memory->_nf, UNUM_MAX_FRACTION_DIGITS, 0); } @@ -303,9 +301,8 @@ static void __CFNumberFormatterApplySymbolPrefs(const void *key, const void *val __cficu_unum_setSymbol(formatter->_nf, sym, item_ustr, item_cnt, &status); } else { - UChar ubuff[1]; - ubuff[0] = '#'; - __cficu_unum_setSymbol(formatter->_nf, sym, ubuff, 1, &status); + UChar empty = 0; + __cficu_unum_setSymbol(formatter->_nf, sym, &empty, 0, &status); } } } diff --git a/CoreFoundation/Locale.subproj/CFRelativeDateTimeFormatter.c b/CoreFoundation/Locale.subproj/CFRelativeDateTimeFormatter.c new file mode 100644 index 0000000000..73cc321e3a --- /dev/null +++ b/CoreFoundation/Locale.subproj/CFRelativeDateTimeFormatter.c @@ -0,0 +1,190 @@ +/* CFRelativeDateTimeFormatter.c + Copyright (c) 2018-2019, Apple Inc. and the Swift project authors + + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + Responsibility: I-Ting Liu + */ + +#import "CFRelativeDateTimeFormatter.h" + +#import +#import "CFICULogging.h" +#import "CFInternal.h" +#import "CFRuntime_Internal.h" + +struct __CFRelativeDateTimeFormatter { + CFRuntimeBase _base; + CFRelativeDateTimeFormatterStyle _style; + CFRelativeDateTimeFormatterUnitsStyle _unitsStyle; + CFLocaleRef _locale; + CFRelativeDateTimeFormattingContext _formattingContext; +}; + +static UDateRelativeDateTimeFormatterStyle icuRelativeDateTimeStyleFromUnitsStyle(CFRelativeDateTimeFormatterStyle style) { + switch (style) { + case CFRelativeDateTimeFormatterUnitsStyleSpellOut: + case CFRelativeDateTimeFormatterUnitsStyleFull: + return UDAT_STYLE_LONG; + case CFRelativeDateTimeFormatterUnitsStyleShort: + return UDAT_STYLE_SHORT; + case CFRelativeDateTimeFormatterUnitsStyleAbbreviated: + return UDAT_STYLE_NARROW; + default: + HALT_MSG("Invalid CFRelativeDateTimeFormatterStyle"); + } +} + +static URelativeDateTimeUnit icuRelativeDateTimeUnitFromCFUnit(CFCalendarUnit unit) { + switch (unit) { + case kCFCalendarUnitYear: + return UDAT_REL_UNIT_YEAR; + case kCFCalendarUnitMonth: + return UDAT_REL_UNIT_MONTH; + case kCFCalendarUnitWeekOfMonth: + return UDAT_REL_UNIT_WEEK; + case kCFCalendarUnitDay: + return UDAT_REL_UNIT_DAY; + case kCFCalendarUnitHour: + return UDAT_REL_UNIT_HOUR; + case kCFCalendarUnitMinute: + return UDAT_REL_UNIT_MINUTE; + case kCFCalendarUnitSecond: + return UDAT_REL_UNIT_SECOND; + default: + HALT_MSG("Invalid CFCalendarUnit"); + } +} + +static UDisplayContext icuFormattingContextFromCFContext(CFRelativeDateTimeFormattingContext context) { + switch (context) { + case CFRelativeDateTimeFormattingContextUnknown: + case CFRelativeDateTimeFormattingContextDynamic: + return UDISPCTX_CAPITALIZATION_NONE; + case CFRelativeDateTimeFormattingContextStandalone: + return UDISPCTX_CAPITALIZATION_FOR_STANDALONE; + case CFRelativeDateTimeFormattingContextListItem: + return UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU; + case CFRelativeDateTimeFormattingContextBeginningOfSentence: + return UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE; + case CFRelativeDateTimeFormattingContextMiddleOfSentence: + return UDISPCTX_CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE; + default: + HALT_MSG("Invalid CFRelativeDateTimeFormattingContext"); + } +} + +static void __CFRelativeDateTimeFormatterDeallocate(CFTypeRef cf) { + assert(cf != NULL); + CFRelativeDateTimeFormatterRef formatter = (CFRelativeDateTimeFormatterRef)cf; + if (formatter->_locale) { CFRelease(formatter->_locale); } +} + +static CFStringRef __CFRelativeDateTimeFormatterCopyDescription(CFTypeRef cf) { + assert(cf != NULL); + CFRelativeDateTimeFormatterRef formatter = (CFRelativeDateTimeFormatterRef)cf; + return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("[%p]"), formatter, CFGetAllocator(formatter)); +} + +const CFRuntimeClass __CFRelativeDateTimeFormatterClass = { + 0, + "CFRelativeDateTimeFormatter", + NULL, // init + NULL, // copy + __CFRelativeDateTimeFormatterDeallocate, + NULL, // equal + NULL, // hash + NULL, // copy formatting desc + __CFRelativeDateTimeFormatterCopyDescription +}; + +CFTypeID _CFRelativeDateTimeFormatterGetTypeID(void) { + return _kCFRuntimeIDCFRelativeDateTimeFormatter; +} + +CFRelativeDateTimeFormatterRef _CFRelativeDateTimeFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFRelativeDateTimeFormatterUnitsStyle unitsStyle, CFRelativeDateTimeFormatterStyle style, CFRelativeDateTimeFormattingContext formattingContext) { + if (allocator == NULL) { allocator = __CFGetDefaultAllocator(); } + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + + CFAssert1(locale != NULL, __kCFLogAssertion, "%s(): locale cannot be NULL", __PRETTY_FUNCTION__); + __CFGenericValidateType(locale, CFLocaleGetTypeID()); + + size_t size = sizeof(struct __CFRelativeDateTimeFormatter) - sizeof(CFRuntimeBase); + struct __CFRelativeDateTimeFormatter *formatter; + formatter = (struct __CFRelativeDateTimeFormatter*)_CFRuntimeCreateInstance(allocator, _CFRelativeDateTimeFormatterGetTypeID(), size, NULL); + if (formatter == NULL) { return NULL; } + + formatter->_locale = CFRetain(locale); + formatter->_formattingContext = formattingContext; + formatter->_unitsStyle = unitsStyle; + formatter->_style = style; + + return formatter; +} + +CFStringRef _CFRelativeDateTimeFormatterCreateStringWithCalendarUnit(CFAllocatorRef allocator, CFRelativeDateTimeFormatterRef formatter, CFCalendarUnit unit, double offset) { + CFLocaleRef locale = formatter->_locale; + CFStringRef localeIdentifier = CFLocaleGetIdentifier(locale); + char buffer[ULOC_FULLNAME_CAPACITY]; + const char *cLocaleIdentifier = CFStringGetCStringPtr(localeIdentifier, kCFStringEncodingASCII); + if (NULL == cLocaleIdentifier) { + if (CFStringGetCString(localeIdentifier, buffer, ULOC_FULLNAME_CAPACITY, kCFStringEncodingASCII)) { + cLocaleIdentifier = buffer; + } + } + + UNumberFormat *numFmt = NULL; + CFRelativeDateTimeFormatterUnitsStyle unitsStyle = formatter->_unitsStyle; + if (unitsStyle == CFRelativeDateTimeFormatterUnitsStyleSpellOut) { + UErrorCode status = U_ZERO_ERROR; + numFmt = __cficu_unum_open(UNUM_SPELLOUT, NULL, 0, cLocaleIdentifier, NULL, &status); + if (U_FAILURE(status)) { + if (numFmt) { + __cficu_unum_close(numFmt); + } + return NULL; + } + } + + UDateRelativeDateTimeFormatterStyle style = icuRelativeDateTimeStyleFromUnitsStyle(unitsStyle); + + UErrorCode status = U_ZERO_ERROR; + UDisplayContext context = icuFormattingContextFromCFContext(formatter->_formattingContext); + // This takes over the ownership of numFmt, so there's not need to close numFmt. + URelativeDateTimeFormatter *fmt = __cficu_ureldatefmt_open(cLocaleIdentifier, numFmt, style, context, &status); + if (U_FAILURE(status)) { + if (fmt) { + __cficu_ureldatefmt_close(fmt); + } + return NULL; + } + + URelativeDateTimeUnit icuUnit = icuRelativeDateTimeUnitFromCFUnit(unit); + + int32_t len = 0; + const int32_t RESULT_BUFFER_SIZE = 128; + UChar result[RESULT_BUFFER_SIZE + 1]; + status = U_ZERO_ERROR; + CFRelativeDateTimeFormatterStyle dateTimeStyle = formatter->_style; + switch (dateTimeStyle) { + case CFRelativeDateTimeFormatterStyleNumeric: + len = __cficu_ureldatefmt_formatNumeric(fmt, offset, icuUnit, result, RESULT_BUFFER_SIZE, &status); + break; + case CFRelativeDateTimeFormatterStyleNamed: + len = __cficu_ureldatefmt_format(fmt, offset, icuUnit, result, RESULT_BUFFER_SIZE, &status); + break; + default: + HALT_MSG("Invalid CFRelativeDateTimeFormatterStyle"); + } + + CFAssert1(fmt != NULL, __kCFLogAssertion, "%s(): URelativeDateTimeFormatter should not be NULL", __PRETTY_FUNCTION__); + __cficu_ureldatefmt_close(fmt); + + if (U_FAILURE(status)){ return NULL; } + + if (allocator == NULL) { allocator = __CFGetDefaultAllocator(); } + return CFStringCreateWithCharacters(allocator, result, len); +} + diff --git a/CoreFoundation/Locale.subproj/CFRelativeDateTimeFormatter.h b/CoreFoundation/Locale.subproj/CFRelativeDateTimeFormatter.h new file mode 100644 index 0000000000..2997648663 --- /dev/null +++ b/CoreFoundation/Locale.subproj/CFRelativeDateTimeFormatter.h @@ -0,0 +1,61 @@ +/* CFRelativeDateTimeFormatter.h + Copyright (c) 2018-2019, Apple Inc. and the Swift project authors + + Portions Copyright (c) 2014-2019, Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + */ + +#ifndef __COREFOUNDATION_CFRELATIVEDATETIMEFORMATTER_h +#define __COREFOUNDATION_CFRELATIVEDATETIMEFORMATTER_h + +#include +#include +#include + +CF_IMPLICIT_BRIDGING_ENABLED +CF_EXTERN_C_BEGIN +CF_ASSUME_NONNULL_BEGIN + +// Values must match NSRelativeDateTimeFormatterUnitsStyle. +typedef CF_ENUM(CFIndex, CFRelativeDateTimeFormatterUnitsStyle) { + CFRelativeDateTimeFormatterUnitsStyleFull = 0, // "2 months ago" + CFRelativeDateTimeFormatterUnitsStyleSpellOut, // "two months ago" + CFRelativeDateTimeFormatterUnitsStyleShort, // "2 mo. ago" + CFRelativeDateTimeFormatterUnitsStyleAbbreviated, // "2 mo. ago"; might give different results in languages other than English +} API_AVAILABLE(macosx(10.15), ios(13.0), watchos(6.0), tvos(13.0)); + +// Values must match NSRelativeDateTimeFormatterStyle. +typedef CF_ENUM(CFIndex, CFRelativeDateTimeFormatterStyle) { + CFRelativeDateTimeFormatterStyleNumeric = 0, // "1 day ago", "2 days ago", "1 week ago", "in 1 week" + CFRelativeDateTimeFormatterStyleNamed, // “yesterday”, "2 days ago", "last week", "next week"; falls back to the numeric style if no name is available +} API_AVAILABLE(macosx(10.15), ios(13.0), watchos(6.0), tvos(13.0)); + +/* Values must match NSFormattingContext. + */ +typedef CF_ENUM(CFIndex, CFRelativeDateTimeFormattingContext) { + CFRelativeDateTimeFormattingContextUnknown = 0, + CFRelativeDateTimeFormattingContextDynamic, + CFRelativeDateTimeFormattingContextStandalone, + CFRelativeDateTimeFormattingContextListItem, + CFRelativeDateTimeFormattingContextBeginningOfSentence, + CFRelativeDateTimeFormattingContextMiddleOfSentence, +} API_AVAILABLE(macosx(10.15), ios(13.0), watchos(6.0), tvos(13.0)); + +typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRelativeDateTimeFormatter *CFRelativeDateTimeFormatterRef; + +CF_EXPORT +CFTypeID _CFRelativeDateTimeFormatterGetTypeID(void); + +CF_EXPORT +CFRelativeDateTimeFormatterRef _Nullable _CFRelativeDateTimeFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFRelativeDateTimeFormatterUnitsStyle unitsStyle, CFRelativeDateTimeFormatterStyle style, CFRelativeDateTimeFormattingContext formattingContext); + +CF_EXPORT +CFStringRef _Nullable _CFRelativeDateTimeFormatterCreateStringWithCalendarUnit(CFAllocatorRef allocator, CFRelativeDateTimeFormatterRef formatter, CFCalendarUnit unit, CFTimeInterval offset); + +CF_ASSUME_NONNULL_END +CF_EXTERN_C_END +CF_IMPLICIT_BRIDGING_DISABLED + +#endif // __COREFOUNDATION_CFRELATIVEDATETIMEFORMATTER_h diff --git a/CoreFoundation/NumberDate.subproj/CFDate.c b/CoreFoundation/NumberDate.subproj/CFDate.c index 07f04aa016..e6abb43cb2 100644 --- a/CoreFoundation/NumberDate.subproj/CFDate.c +++ b/CoreFoundation/NumberDate.subproj/CFDate.c @@ -172,7 +172,7 @@ CF_PRIVATE void __CFDateInitialize(void) { __CF1_TSRRate = 1.0 / __CFTSRRate; #elif TARGET_OS_WIN32 // We are using QueryUnbiasedInterruptTimePrecise as time source. - // It returns result in system time units of 100 nanoseconds. + // It returns result in system time units of 100 nanoseconds. // To get seconds we need to divide the value by 1e7 (10000000). __CFTSRRate = 1.0e7; __CF1_TSRRate = 1.0 / __CFTSRRate; diff --git a/CoreFoundation/NumberDate.subproj/CFNumber.c b/CoreFoundation/NumberDate.subproj/CFNumber.c index a74361560d..35fd87c8d2 100644 --- a/CoreFoundation/NumberDate.subproj/CFNumber.c +++ b/CoreFoundation/NumberDate.subproj/CFNumber.c @@ -74,6 +74,8 @@ static const uint8_t __CFNumberCanonicalTypeIndex[] = { #if TARGET_OS_WIN32 +#define isnan(A) _isnan(A) +#define isinf(A) !_finite(A) #define copysign(A, B) _copysign(A, B) #endif @@ -85,12 +87,12 @@ struct __CFBoolean { DECLARE_STATIC_CLASS_REF(__NSCFBoolean); -static _CF_CONSTANT_OBJECT_BACKING struct __CFBoolean __kCFBooleanTrue = { +_CF_CONSTANT_OBJECT_BACKING struct __CFBoolean __kCFBooleanTrue = { INIT_CFRUNTIME_BASE_WITH_CLASS(__NSCFBoolean, _kCFRuntimeIDCFBoolean) }; const CFBooleanRef kCFBooleanTrue = &__kCFBooleanTrue; -static _CF_CONSTANT_OBJECT_BACKING struct __CFBoolean __kCFBooleanFalse = { +_CF_CONSTANT_OBJECT_BACKING struct __CFBoolean __kCFBooleanFalse = { INIT_CFRUNTIME_BASE_WITH_CLASS(__NSCFBoolean, _kCFRuntimeIDCFBoolean) }; const CFBooleanRef kCFBooleanFalse = &__kCFBooleanFalse; @@ -1150,7 +1152,10 @@ static CFNumberRef _CFNumberCreate(CFAllocatorRef allocator, CFNumberType type, // Forcing the type AFTER it was cached would cause a race condition with other // threads pulling the number object out of the cache and using it. __CFRuntimeSetNumberType(result, (uint8_t)kCFNumberSInt32Type); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" if (OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)result, (void *volatile *)&__CFNumberCache[valToBeCached - MinCachedInt])) { +#pragma GCC diagnostic pop CFRetain(result); } else { // Did not cache the number object, put original type back. @@ -1167,10 +1172,18 @@ CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType type, const vo } CFNumberType CFNumberGetType(CFNumberRef number) { - CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFNumber, CFNumberType, (NSNumber *)number, _cfNumberType); CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFNumber, CFNumberType, (CFSwiftRef)number, NSNumber._cfNumberGetType); - __CFAssertIsNumber(number); - CFNumberType type = __CFNumberGetType(number); + CFNumberType type; +#if DEPLOYMENT_RUNTIME_OBJC + if (CF_IS_OBJC(_kCFRuntimeIDCFNumber, (const void *)number)) { + type = (CFNumberType)[(NSNumber *)number _cfNumberType]; + } else { +#endif + __CFAssertIsNumber(number); + type = __CFNumberGetType(number); +#if DEPLOYMENT_RUNTIME_OBJC + } +#endif if (kCFNumberSInt128Type == type) type = kCFNumberSInt64Type; // must hide this type, since it is not public return type; } diff --git a/CoreFoundation/Parsing.subproj/CFBinaryPList.c b/CoreFoundation/Parsing.subproj/CFBinaryPList.c index 445eeca773..54a4877c0b 100644 --- a/CoreFoundation/Parsing.subproj/CFBinaryPList.c +++ b/CoreFoundation/Parsing.subproj/CFBinaryPList.c @@ -29,6 +29,7 @@ #include #include "CFInternal.h" #include "CFRuntime_Internal.h" +#include "CFPropertyList_Internal.h" #if !TARGET_OS_WASI #include @@ -125,6 +126,7 @@ CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFKeyedArchiverUID, uid); return uid->_value; } @@ -660,12 +662,11 @@ CFIndex __CFBinaryPlistWriteToStreamWithOptions(CFPropertyListRef plist, CFTypeR CF_PRIVATE CFMutableDataRef _CFDataCreateFixedMutableWithBuffer(CFAllocatorRef allocator, CFIndex capacity, const uint8_t *bytes, CFAllocatorRef bytesDeallocator); -CF_PRIVATE CFDataRef __CFBinaryPlistCreateDataUsingExternalBufferAllocator(CFPropertyListRef plist, CFAllocatorAllocateCallBack allocateBufferCallback, CFAllocatorDeallocateCallBack deallocateBufferCallback, uint64_t estimate, CFOptionFlags options, void *allocInfo, CFErrorRef *error) { +CF_PRIVATE CFDataRef __CFBinaryPlistCreateDataUsingExternalBufferAllocator(CFPropertyListRef plist, uint64_t estimate, CFOptionFlags options, CFAllocatorRef (^allocatorCreator)(CFIndex bufferSize), CFErrorRef *error) { CFIndex size = __CFBinaryPlistWriteOrPresize(plist, NULL, estimate, options, true, error); CFDataRef result = NULL; if (size > 0) { - CFAllocatorContext context = {0, allocInfo, NULL, NULL, NULL, allocateBufferCallback, NULL, deallocateBufferCallback, NULL}; - CFAllocatorRef allocator = CFAllocatorCreate(kCFAllocatorSystemDefault, &context); + CFAllocatorRef allocator = allocatorCreator(size); if (allocator) { void *buffer = CFAllocatorAllocate(allocator, size, 0); if (buffer) { @@ -712,11 +713,11 @@ CF_INLINE uint64_t _getSizedInt(const uint8_t *data, uint8_t valSize) { if (valSize == 1) { return (uint64_t)*data; } else if (valSize == 2) { - return (uint64_t)unaligned_load16be(data); + return (uint64_t)_CFUnalignedLoad16BE(data); } else if (valSize == 4) { - return (uint64_t)unaligned_load32be(data); + return (uint64_t)_CFUnalignedLoad32BE(data); } else if (valSize == 8) { - return unaligned_load64be(data); + return _CFUnalignedLoad64BE(data); } // Compatibility with existing archives, including anything with a non-power-of-2 @@ -1164,6 +1165,19 @@ extern CFArrayRef __CFArrayCreateTransfer(CFAllocatorRef allocator, const void * CF_PRIVATE void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator, CFSetRef currentKeys, CFSetRef *theseKeys, CFSetRef *nextKeys); CF_PRIVATE bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFSetRef keyPaths, CFPropertyListRef *outPlist, CFTypeID *outPlistTypeID) { + + // NOTE: Bailing out here will cause us to attempt to parse + // as XML (which will fail) then as a OpenSTEP plist + // the final error string is less than helpful: + // "Unexpected character b at line 1". + // It would be nice to actually be more descriptive but that + // would require a more scaffolding. + if (curDepth > _CFPropertyListMaxRecursionDepth()) { + // Bail before we get so far into the stack that we run out of space. + // Emit an `os_log_fault` to relay the issue to the debugger and to track how common this case may be. + os_log_fault(_CFOSLog(), "Too many nested arrays or dictionaries"); + FAIL_FALSE; + } if (objects && outPlist) { *outPlist = CFDictionaryGetValue(objects, (const void *)(uintptr_t)startOffset); @@ -1486,7 +1500,7 @@ CF_PRIVATE bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, ui if (databytes + objectsRangeEnd < extent) FAIL_FALSE; byte_cnt = check_size_t_mul(arrayCount, sizeof(CFPropertyListRef), &err); if (CF_NO_ERROR != err) FAIL_FALSE; - STACK_BUFFER_DECL(CFPropertyListRef, buffer, arrayCount <= 256 ? arrayCount : 1); + STACK_BUFFER_DECL(CFPropertyListRef, buffer, (arrayCount > 0 && arrayCount <= 256) ? arrayCount : 1); if (outPlist) { list = (arrayCount <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, byte_cnt, 0); if (!list) FAIL_FALSE; @@ -1718,6 +1732,12 @@ CF_PRIVATE bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, ui } if (list) { *((void **)list + idx) = (void *)pl; +#if __clang_analyzer__ + // The static analyzer can't reason that we're always looping through this an even number of times. It thinks list[idx + halfDictionaryCount] below will be uninitialized. + if (idx % 2 == 0) { + *((void **)list + halfDictionaryCount) = NULL; + } +#endif } ptr += trailer->_objectRefSize; } diff --git a/CoreFoundation/Parsing.subproj/CFPropertyList.c b/CoreFoundation/Parsing.subproj/CFPropertyList.c index 72198d548c..1a40299052 100644 --- a/CoreFoundation/Parsing.subproj/CFPropertyList.c +++ b/CoreFoundation/Parsing.subproj/CFPropertyList.c @@ -10,6 +10,7 @@ #include #include +#include "CFPropertyList_Internal.h" #include #include #include @@ -141,26 +142,29 @@ static CFStringRef __copyErrorDebugDescription(CFErrorRef error) { // don't allow _CFKeyedArchiverUID here #define __CFAssertIsPList(cf) CFAssert2(CFGetTypeID(cf) == CFStringGetTypeID() || CFGetTypeID(cf) == CFArrayGetTypeID() || CFGetTypeID(cf) == CFBooleanGetTypeID() || CFGetTypeID(cf) == CFNumberGetTypeID() || CFGetTypeID(cf) == CFDictionaryGetTypeID() || CFGetTypeID(cf) == CFDateGetTypeID() || CFGetTypeID(cf) == CFDataGetTypeID(), __kCFLogAssertion, "%s(): %p not of a property list type", __PRETTY_FUNCTION__, cf); -struct context { +typedef struct context { bool answer; - CFMutableSetRef set; CFPropertyListFormat format; - CFStringRef *error; -}; + CFMutableSetRef _Nullable set; + CFStringRef * _Nullable error; + CFMutableBagRef _Nullable referenceBag; /* Only used for non-bplists */ +} ___CFPropertyListIsValidContext; + +typedef ___CFPropertyListIsValidContext * __CFPropertyListIsValidContext; -static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format, CFStringRef *error); +static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, __CFPropertyListIsValidContext _Nonnull context); static void __CFPropertyListIsArrayPlistAux(const void *value, void *context) { - struct context *ctx = (struct context *)context; + __CFPropertyListIsValidContext ctx = context; if (!ctx->answer) return; if (!value && ctx->error && !*(ctx->error)) { *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list arrays cannot contain NULL")); } - ctx->answer = value && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format, ctx->error); + ctx->answer = value && __CFPropertyListIsValidAux(value, true, ctx); } static void __CFPropertyListIsDictPlistAux(const void *key, const void *value, void *context) { - struct context *ctx = (struct context *)context; + __CFPropertyListIsValidContext ctx = context; if (!ctx->answer) return; if (!key && ctx->error && !*(ctx->error)) *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list dictionaries cannot contain NULL keys")); if (!value && ctx->error && !*(ctx->error)) *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list dictionaries cannot contain NULL values")); @@ -169,56 +173,110 @@ static void __CFPropertyListIsDictPlistAux(const void *key, const void *value, v *(ctx->error) = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("property list dictionaries may only have keys which are CFStrings, not '%@'"), desc); CFRelease(desc); } - ctx->answer = key && value && (_kCFRuntimeIDCFString == CFGetTypeID(key)) && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format, ctx->error); + ctx->answer = key && value && (_kCFRuntimeIDCFString == CFGetTypeID(key)) && __CFPropertyListIsValidAux(value, true, ctx); } -static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format, CFStringRef *error) { +static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, __CFPropertyListIsValidContext _Nonnull context) { + CFAssert1(context != NULL, __kCFLogAssertion, "%s(): context cannot be null", __PRETTY_FUNCTION__); + if (!plist) { - if (error) *error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain NULL")); - return false; + if (context->error) *context->error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain NULL")); + return false; } + CFTypeID type = CFGetTypeID(plist); if (_kCFRuntimeIDCFString == type) return true; if (_kCFRuntimeIDCFData == type) return true; - if (kCFPropertyListOpenStepFormat != format) { - if (_kCFRuntimeIDCFBoolean == type) return true; - if (_kCFRuntimeIDCFNumber == type) return true; - if (_kCFRuntimeIDCFDate == type) return true; - if (_CFKeyedArchiverUIDGetTypeID() == type) return true; - } - if (!recursive && _kCFRuntimeIDCFArray == type) return true; - if (!recursive && _kCFRuntimeIDCFDictionary == type) return true; - - // at any one invocation of this function, set should contain the objects in the "path" down to this object. For the outermost invocation it can be NULL. - Boolean createdSet = false; - if (!set) { - set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + if (kCFPropertyListOpenStepFormat != context->format) { + if (_kCFRuntimeIDCFBoolean == type) return true; + if (_kCFRuntimeIDCFNumber == type) return true; + if (_kCFRuntimeIDCFDate == type) return true; + if (_CFKeyedArchiverUIDGetTypeID() == type) return true; + } + + if (!recursive && _kCFRuntimeIDCFArray == type) { + return true; + } + if (!recursive && _kCFRuntimeIDCFDictionary == type) { + return true; + } + + // At any one invocation of this function, referenceBag should contain the objects in the "path" down to this object. + // For the outermost invocation it can be NULL. + bool createdSet = false; + if (!context->set) { + context->set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); createdSet = true; - } else if (CFSetContainsValue(set, plist)) { - if (error) *error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain recursive container references")); - return false; + } else if (CFSetContainsValue(context->set, plist)) { + if (context->error) *context->error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain recursive container references")); + return false; + } + + // It's valid for bplists to contain references to other collections + // consider this: + // + // C = @[ @"a", @"a", @"a" ]; + // B = @[ c, c, c ]; + // A = @[ b, b, b ]; + // + // But this can cause an explosion when converting those endlessly + // recursive collections to XML or OpenSTEP plists. We already have + // a max recursion depth limit, if we exceed it consider the plist invalid. + bool createdRefBag = false; + bool const limitCollectionDepth = context->format != kCFPropertyListBinaryFormat_v1_0; + + if (limitCollectionDepth) { + if (!context->referenceBag) { + CFBagCallBacks callbacks = kCFTypeBagCallBacks; + callbacks.equal = NULL; + callbacks.hash = NULL; + context->referenceBag = CFBagCreateMutable(kCFAllocatorDefault, 0, &callbacks); + createdRefBag = true; + } else if (CFBagGetCountOfValue(context->referenceBag, plist) > _CFPropertyListMaxRecursionWidth()) { + if (context->error) *context->error = (CFStringRef)CFRetain(CFSTR("Too many nested arrays or dictionaries please use kCFPropertyListBinaryFormat_v1_0 instead which supports references")); + return false; + } } - Boolean result = false; + + bool result = false; if (_kCFRuntimeIDCFArray == type) { - struct context ctx = {true, set, format, error}; - CFSetAddValue(set, plist); - CFArrayApplyFunction((CFArrayRef)plist, CFRangeMake(0, CFArrayGetCount((CFArrayRef)plist)), __CFPropertyListIsArrayPlistAux, &ctx); - CFSetRemoveValue(set, plist); - result = ctx.answer; + CFIndex const arrayCount = CFArrayGetCount((CFArrayRef)plist); + // only arrays larger than zero could have cyclic references + if (arrayCount > 0 && limitCollectionDepth) { + CFBagAddValue(context->referenceBag, plist); + } + + CFSetAddValue(context->set, plist); + CFArrayApplyFunction((CFArrayRef)plist, CFRangeMake(0, arrayCount), __CFPropertyListIsArrayPlistAux, context); + CFSetRemoveValue(context->set, plist); + result = context->answer; } else if (_kCFRuntimeIDCFDictionary == type) { - struct context ctx = {true, set, format, error}; - CFSetAddValue(set, plist); - CFDictionaryApplyFunction((CFDictionaryRef)plist, __CFPropertyListIsDictPlistAux, &ctx); - CFSetRemoveValue(set, plist); - result = ctx.answer; - } else if (error) { + CFIndex const dictCount = CFDictionaryGetCount((CFDictionaryRef)plist); + // only dictionaries larger than zero could have cyclic references + if (dictCount > 0 && limitCollectionDepth) { + CFBagAddValue(context->referenceBag, plist); + } + + CFSetAddValue(context->set, plist); + CFDictionaryApplyFunction((CFDictionaryRef)plist, __CFPropertyListIsDictPlistAux, context); + CFSetRemoveValue(context->set, plist); + result = context->answer; + } else if (context->error) { CFStringRef desc = CFCopyTypeIDDescription(type); - *error = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("property lists cannot contain objects of type '%@'"), desc); + *context->error = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("property lists cannot contain objects of type '%@'"), desc); CFRelease(desc); } - + if (createdSet) { - CFRelease(set); + CFAssert1(context->set != NULL, __kCFLogAssertion, "%s(): context->set cannot be null", __PRETTY_FUNCTION__); + CFRelease(context->set); + context->set = NULL; + } + + if (createdRefBag) { + CFAssert1(context->referenceBag != NULL, __kCFLogAssertion, "%s(): context->referenceBag cannot be null", __PRETTY_FUNCTION__); + CFRelease(context->referenceBag); + context->referenceBag = NULL; } return result; @@ -226,7 +284,15 @@ static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, static Boolean _CFPropertyListIsValidWithErrorString(CFPropertyListRef plist, CFPropertyListFormat format, CFStringRef *error) { CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__); - return __CFPropertyListIsValidAux(plist, true, NULL, format, error); + + ___CFPropertyListIsValidContext context = { + .answer = true, + .format = format, + .error = error, + .referenceBag = NULL + }; + + return (Boolean)__CFPropertyListIsValidAux(plist, true, &context); } #pragma mark - @@ -708,22 +774,21 @@ CFDataRef CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRe return _CFPropertyListCreateXMLData(allocator, propertyList, true); } -CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) { +CF_EXPORT CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) { return _CFPropertyListCreateXMLData(allocator, propertyList, false); } Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format) { + // The first is only there for testing when building with `-DDEBUG` + // See the actual implementation in the `else` below CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__); - return __CFPropertyListIsValidAux(plist, true, NULL, format, NULL); -#if defined(DEBUG) - CFStringRef error = NULL; - bool result = __CFPropertyListIsValidAux(plist, true, NULL, format, &error); - if (error) { - CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): %@"), error); - CFRelease(error); - } - return result; -#endif + ___CFPropertyListIsValidContext context = { + .answer = true, + .format = format, + .error = NULL, + .referenceBag = NULL + }; + return __CFPropertyListIsValidAux(plist, true, &context); } // ======================================================================== @@ -734,8 +799,7 @@ Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat form // ------------------------- Reading plists ------------------ // -static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo); -static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out); +static Boolean parseXMLElement(_CFXMLPlistParseInfo * _Nonnull pInfo, Boolean * _Nullable isKey, CFTypeRef * _Nullable out, size_t curDepth); // warning: doesn't have a good idea of Unicode line separators static UInt32 lineNumber(_CFXMLPlistParseInfo *pInfo) { @@ -826,8 +890,6 @@ static void skipDTD(_CFXMLPlistParseInfo *pInfo) { return; } - // *Sigh* Must parse in-line DTD - skipInlineDTD(pInfo); if (pInfo->error) return; skipWhitespace(pInfo); if (pInfo->error) return; @@ -842,74 +904,19 @@ static void skipDTD(_CFXMLPlistParseInfo *pInfo) { } } -static void skipPERef(_CFXMLPlistParseInfo *pInfo) { - const char *p = pInfo->curr; - while (p < pInfo->end) { - if (*p == ';') { - pInfo->curr = p+1; - return; - } - p ++; - } - pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo)); -} - -// First character should be just past '[' -static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo) { - while (!pInfo->error && pInfo->curr < pInfo->end) { - UniChar ch; - skipWhitespace(pInfo); - ch = *pInfo->curr; - if (ch == '%') { - pInfo->curr ++; - skipPERef(pInfo); - } else if (ch == '<') { - pInfo->curr ++; - if (pInfo->curr >= pInfo->end) { - pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD")); - return; - } - ch = *(pInfo->curr); - if (ch == '?') { - pInfo->curr ++; - skipXMLProcessingInstruction(pInfo); - } else if (ch == '!') { - if (pInfo->curr + 2 < pInfo->end && (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-')) { - pInfo->curr += 3; - skipXMLComment(pInfo); - } else { - // Skip the myriad of DTD declarations of the form "" - pInfo->curr ++; // Past both '<' and '!' - while (pInfo->curr < pInfo->end) { - if (*(pInfo->curr) == '>') break; - pInfo->curr ++; - } - if (*(pInfo->curr) != '>') { - pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD")); - return; - } - pInfo->curr ++; - } - } else { - pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo)); - return; - } - } else if (ch == ']') { - pInfo->curr ++; - return; - } else { - pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo)); - return; - } - } - if (!pInfo->error) { - pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD")); - } -} - // content ::== (element | CharData | Reference | CDSect | PI | Comment)* // In the context of a plist, CharData, Reference and CDSect are not legal (they all resolve to strings). Skipping whitespace, then, the next character should be '<'. From there, we figure out which of the three remaining cases we have (element, PI, or Comment). -static Boolean getContentObject(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out) { +static Boolean getContentObject(_CFXMLPlistParseInfo * _Nonnull pInfo, Boolean * _Nullable isKey, CFTypeRef * _Nullable out, size_t curDepth) { + if (curDepth > _CFPropertyListMaxRecursionDepth()) { + // Bail before we get so far into the stack that we run out of space. + // + // In bplist parsing we don't have the ability to set the error message + // descriptively like we do here, so in bplists we use an `os_log_fault` + // here we can just set `pInfo->error` see rdar://65835843 + pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Too many nested arrays or dictionaries, failing on line %d"), lineNumber(pInfo)); + return false; + } + if (isKey) *isKey = false; while (!pInfo->error && pInfo->curr < pInfo->end) { skipWhitespace(pInfo); @@ -938,7 +945,8 @@ static Boolean getContentObject(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFT return false; } if (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-') { - pInfo->curr += 2; + // skip `--` and set the cursor 1 past the two dashes + pInfo->curr += 3; skipXMLComment(pInfo); } else { pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); @@ -951,7 +959,7 @@ static Boolean getContentObject(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFT return false; default: // Should be an element - return parseXMLElement(pInfo, isKey, out); + return parseXMLElement(pInfo, isKey, out, curDepth); } } // Do not set the error string here; if it wasn't already set by one of the recursive parsing calls, the caller will quickly detect the failure (b/c pInfo->curr >= pInfo->end) and provide a more useful one of the form "end tag for not found" @@ -1262,15 +1270,15 @@ static Boolean checkForCloseTag(_CFXMLPlistParseInfo *pInfo, const char *tag, CF } // pInfo should be set to the first content character of the -static Boolean parsePListTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { +static Boolean parsePListTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out, size_t curDepth) { CFTypeRef result = NULL; - if (!getContentObject(pInfo, NULL, &result)) { + if (!getContentObject(pInfo, NULL, &result, curDepth)) { if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty plist tag")); return false; } const char *save = pInfo->curr; // Save this in case the next step fails CFTypeRef tmp = NULL; - if (getContentObject(pInfo, NULL, &tmp)) { + if (getContentObject(pInfo, NULL, &tmp, curDepth)) { // Got an extra object __CFPListRelease(tmp, pInfo->allocator); __CFPListRelease(result, pInfo->allocator); @@ -1356,17 +1364,16 @@ CF_PRIVATE void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator, CF *nextKeys = outNextKeys; } -static Boolean parseArrayTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { +static Boolean parseArrayTag(_CFXMLPlistParseInfo * _Nonnull pInfo, CFTypeRef * _Nullable out, size_t curDepth) { CFTypeRef tmp = NULL; - if (pInfo->skip) { - Boolean result = getContentObject(pInfo, NULL, &tmp); + Boolean result = getContentObject(pInfo, NULL, &tmp, curDepth); while (result) { if (tmp) { // Shouldn't happen (if skipping, all content values should be null), but just in case __CFPListRelease(tmp, pInfo->allocator); } - result = getContentObject(pInfo, NULL, &tmp); + result = getContentObject(pInfo, NULL, &tmp, curDepth); } if (pInfo->error) { @@ -1396,7 +1403,7 @@ static Boolean parseArrayTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { count++; pInfo->keyPaths = newKeyPaths; } - result = getContentObject(pInfo, NULL, &tmp); + result = getContentObject(pInfo, NULL, &tmp, curDepth); if (keys) { pInfo->keyPaths = oldKeyPaths; pInfo->skip = false; @@ -1416,7 +1423,7 @@ static Boolean parseArrayTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { count++; pInfo->keyPaths = newKeyPaths; } - result = getContentObject(pInfo, NULL, &tmp); + result = getContentObject(pInfo, NULL, &tmp, curDepth); if (keys) { // reset after getting object pInfo->keyPaths = oldKeyPaths; @@ -1436,6 +1443,7 @@ static Boolean parseArrayTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { __CFPListRelease(array, pInfo->allocator); return false; } + if (-1 == allowImmutableCollections) { checkImmutableCollections(); } @@ -1450,19 +1458,19 @@ static Boolean parseArrayTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { return true; } -static Boolean parseDictTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { +static Boolean parseDictTag(_CFXMLPlistParseInfo * _Nonnull pInfo, CFTypeRef * _Nullable out, size_t curDepth) { Boolean gotKey; Boolean result; CFTypeRef key = NULL, value = NULL; if (pInfo->skip) { - result = getContentObject(pInfo, &gotKey, &key); + result = getContentObject(pInfo, &gotKey, &key, curDepth); while (result) { if (!gotKey) { if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Found non-key inside at line %d"), lineNumber(pInfo)); return false; } - result = getContentObject(pInfo, NULL, &value); + result = getContentObject(pInfo, NULL, &value, curDepth); if (!result) { if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Value missing for key inside at line %d"), lineNumber(pInfo)); return false; @@ -1472,7 +1480,7 @@ static Boolean parseDictTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { key = NULL; __CFPListRelease(value, pInfo->allocator); value = NULL; - result = getContentObject(pInfo, &gotKey, &key); + result = getContentObject(pInfo, &gotKey, &key, curDepth); } if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) { *out = NULL; @@ -1488,7 +1496,7 @@ static Boolean parseDictTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { CFMutableDictionaryRef dict = NULL; - result = getContentObject(pInfo, &gotKey, &key); + result = getContentObject(pInfo, &gotKey, &key, curDepth); while (result && key) { if (!gotKey) { if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Found non-key inside at line %d"), lineNumber(pInfo)); @@ -1503,7 +1511,7 @@ static Boolean parseDictTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { if (!CFSetContainsValue(theseKeyPaths, key)) pInfo->skip = true; pInfo->keyPaths = nextKeyPaths; } - result = getContentObject(pInfo, NULL, &value); + result = getContentObject(pInfo, NULL, &value, curDepth); if (theseKeyPaths) { pInfo->keyPaths = oldKeyPaths; pInfo->skip = false; @@ -1530,12 +1538,16 @@ static Boolean parseDictTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { key = NULL; __CFPListRelease(value, pInfo->allocator); value = NULL; - - result = getContentObject(pInfo, &gotKey, &key); + result = getContentObject(pInfo, &gotKey, &key, curDepth); } __CFPListRelease(nextKeyPaths, pInfo->allocator); __CFPListRelease(theseKeyPaths, pInfo->allocator); + + if (pInfo->error) { // getContentObject encountered a parse error + __CFPListRelease(dict, pInfo->allocator); + return false; + } if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) { if (NULL == dict) { @@ -1992,7 +2004,7 @@ static Boolean parseIntegerTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { #undef GET_CH // Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<' -static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out) { +static Boolean parseXMLElement(_CFXMLPlistParseInfo * _Nonnull pInfo, Boolean * _Nullable isKey, CFTypeRef * _Nullable out, size_t curDepth) { const char *marker = pInfo->curr; int markerLength = -1; Boolean isEmpty; @@ -2080,7 +2092,7 @@ static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTy pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty plist tag")); return false; } - return parsePListTag(pInfo, out); + return parsePListTag(pInfo, out, curDepth); case ARRAY_IX: if (isEmpty) { if (pInfo->skip) { @@ -2094,7 +2106,7 @@ static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTy } return true; } else { - return parseArrayTag(pInfo, out); + return parseArrayTag(pInfo, out, curDepth + 1); } case DICT_IX: if (isEmpty) { @@ -2109,7 +2121,7 @@ static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTy } return true; } else { - return parseDictTag(pInfo, out); + return parseDictTag(pInfo, out, curDepth + 1); } case KEY_IX: case STRING_IX: @@ -2218,8 +2230,8 @@ static Boolean parseXMLPropertyList(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) // Comment or DTD ++ pInfo->curr; if (pInfo->curr+1 < pInfo->end && *pInfo->curr == '-' && *(pInfo->curr+1) == '-') { - // Comment - pInfo->curr += 2; + // skip `--` and set the cursor 1 past the two dashes + pInfo->curr += 3; skipXMLComment(pInfo); } else { skipDTD(pInfo); @@ -2230,7 +2242,7 @@ static Boolean parseXMLPropertyList(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) skipXMLProcessingInstruction(pInfo); } else { // Tag or malformed - return parseXMLElement(pInfo, NULL, out); + return parseXMLElement(pInfo, NULL, out, 0); // Note we do not verify that there was only one element, so a file that has garbage after the first element will nonetheless successfully parse } } @@ -2516,7 +2528,9 @@ static Boolean _CFPropertyListCreateWithData(CFAllocatorRef allocator, CFDataRef __savePlistData(data, option); #endif - // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser + // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser. + // It would be lovely to be able to not just ignore the error and + // have the error message actually relay issues with bplists. if (doBinary && __CFTryParseBinaryPlist(allocator, data, (option&kCFPropertyListMutabilityMask), out, NULL)) { if (format) *format = kCFPropertyListBinaryFormat_v1_0; return true; @@ -2635,7 +2649,7 @@ CFTypeRef _CFPropertyListCreateFromXMLString(CFAllocatorRef allocator, CFStringR CF_PRIVATE bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFSetRef keyPaths, CFPropertyListRef *plist, CFTypeID *outPlistTypeID); // Returns a subset of the property list, only including the key paths in the CFSet. -Boolean _CFPropertyListCreateFiltered(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFSetRef keyPaths, CFPropertyListRef *value, CFErrorRef *error) { +bool _CFPropertyListCreateFiltered(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFSetRef keyPaths, CFPropertyListRef *value, CFErrorRef *error) { if (!keyPaths || !data) { return false; @@ -2646,7 +2660,7 @@ Boolean _CFPropertyListCreateFiltered(CFAllocatorRef allocator, CFDataRef data, uint64_t offset; const uint8_t *databytes = CFDataGetBytePtr(data); uint64_t datalen = CFDataGetLength(data); - Boolean success = false; + bool success = false; CFTypeRef out = NULL; // First check to see if it is a binary property list @@ -2684,7 +2698,7 @@ Boolean _CFPropertyListCreateFiltered(CFAllocatorRef allocator, CFDataRef data, @param error If an error occurs, will be set to a valid CFErrorRef. It is the caller's responsibility to release this value. @return True if the key is found, false otherwise. */ -Boolean _CFPropertyListCreateSingleValue(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFStringRef keyPath, CFPropertyListRef *value, CFErrorRef *error) { +bool _CFPropertyListCreateSingleValue(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFStringRef keyPath, CFPropertyListRef *value, CFErrorRef *error) { if (!keyPath || CFStringGetLength(keyPath) == 0) { return false; @@ -2695,7 +2709,7 @@ Boolean _CFPropertyListCreateSingleValue(CFAllocatorRef allocator, CFDataRef dat uint64_t offset; const uint8_t *databytes = CFDataGetBytePtr(data); uint64_t datalen = CFDataGetLength(data); - Boolean success = false; + bool success = false; // First check to see if it is a binary property list if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) { diff --git a/CoreFoundation/Parsing.subproj/CFPropertyList_Internal.h b/CoreFoundation/Parsing.subproj/CFPropertyList_Internal.h new file mode 100644 index 0000000000..76f247c672 --- /dev/null +++ b/CoreFoundation/Parsing.subproj/CFPropertyList_Internal.h @@ -0,0 +1,39 @@ +/* CFPropertyList_Internal.h + Copyright (c) 2020, Apple Inc. and the Swift project authors + + Portions Copyright (c) 2020, Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +*/ + +#if !defined(__COREFOUNDATION_CFPROPERTYLIST_INTERNAL__) +#define __COREFOUNDATION_CFPROPERTYLIST_INTERNAL 1 + +#include "CFBase.h" + +/// Limit for the max recursion depth to avoid unbounded stack explosion when +/// parsing a crafted plist during validation of an object graph and during reading. +/// For macOS use maximum recusrion limit of `512` is used. +/// For iPhone, tvOS, and Watches use `128` due to the memory limitations. +/// +/// rdar://61207578 ([Ward CFPropertyList audit, Low] unbounded recursion (binary and plain plists)) +/// rdar://61529878 ([Ward CFPropertyList audit, Medium] Plist exponential growth DoS) +CF_INLINE size_t _CFPropertyListMaxRecursionDepth() { + /// Depth that won't hit stack limits on any platform +#if !TARGET_OS_IOS && !TARGET_OS_ANDROID + return 512; +#else + return 128; +#endif +} + +/// Limit for the width of collection references when the format we're writing out doesn't natively support references like bplists do +/// +/// rdar://61529878 ([Ward CFPropertyList audit, Medium] Plist exponential growth DoS) +CF_INLINE size_t _CFPropertyListMaxRecursionWidth() { + // For now let's start with a resonable value that during testing allows many common cases but prevents very "wide" references to the same collections + return _CFPropertyListMaxRecursionDepth() * 3; +} + +#endif /*! __COREFOUNDATION_CFPROPERTYLIST_INTERNAL__ */ diff --git a/CoreFoundation/PlugIn.subproj/CFBundle.c b/CoreFoundation/PlugIn.subproj/CFBundle.c index c4182d7d6a..064fe5f4ae 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle.c @@ -8,6 +8,7 @@ Responsibility: Tony Parker */ +#include #include "CFBundle_Internal.h" #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #if defined(BINARY_SUPPORT_DYLD) @@ -48,8 +50,6 @@ #endif -static void _CFBundleUnloadScheduledBundles(void); - #define LOG_BUNDLE_LOAD 0 // Public CFBundle Info plist keys @@ -60,6 +60,9 @@ CONST_STRING_DECL(kCFBundleVersionKey, "CFBundleVersion") CONST_STRING_DECL(kCFBundleDevelopmentRegionKey, "CFBundleDevelopmentRegion") CONST_STRING_DECL(kCFBundleLocalizationsKey, "CFBundleLocalizations") +// Private CFBundle key, used to mean 'I support the same localizations as CoreFoundation' +CONST_STRING_DECL(_kCFBundleUseAppleLocalizationsKey, "_CFBundleUseAppleLocalizations") + // Private CFBundle Info plist keys, possible candidates for public constants CONST_STRING_DECL(_kCFBundleAllowMixedLocalizationsKey, "CFBundleAllowMixedLocalizations") CONST_STRING_DECL(_kCFBundleSupportedPlatformsKey, "CFBundleSupportedPlatforms") @@ -124,8 +127,6 @@ static _CFMutex CFBundleGlobalDataLock = _CF_MUTEX_STATIC_INITIALIZER; static CFMutableDictionaryRef _bundlesByIdentifier = NULL; static CFMutableDictionaryRef _bundlesByURL = NULL; static CFMutableArrayRef _allBundles = NULL; -static CFMutableSetRef _bundlesToUnload = NULL; -static Boolean _scheduledBundlesAreUnloading = false; static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean doFinalProcessing, Boolean unique, Boolean addToTables); static void _CFBundleEnsureBundlesUpToDateWithHint(CFStringRef hint); @@ -216,16 +217,47 @@ static Boolean _useUnsafeUnretainedTables(void) { } #endif +#pragma mark - +#pragma mark Utilities + +// Create an absolute URL after creating a URL with the supplied URLString, baseURL. +// This is used to resolve symbolic links in the path (For example, for wrapped bundles) +CF_PRIVATE CFURLRef _CFURLCreateResolvedDirectoryWithString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) { + CFURLRef relativeURL = CFURLCreateWithString(allocator, URLString, baseURL); + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(relativeURL); + CFRelease(relativeURL); + CFStringRef absolutePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + CFRelease(absoluteURL); + + char absolutePathCString[PATH_MAX]; + Boolean success = CFStringGetFileSystemRepresentation(absolutePath, absolutePathCString, PATH_MAX); + CFRelease(absolutePath); + if (success) { + int fd = open(absolutePathCString, O_RDONLY); + if (fd > 0) { + char resolvedPathCString[PATH_MAX]; + if (_CFGetPathFromFileDescriptor(fd, resolvedPathCString)) { + os_log_error(_CFBundleResourceLogger(), "Unable to resolve directory (%d)", errno); + close(fd); + } else { + close(fd); + CFStringRef resolvedPath = CFStringCreateWithFileSystemRepresentation(allocator, resolvedPathCString); + CFURLRef result = CFURLCreateWithFileSystemPath(allocator, resolvedPath, PLATFORM_PATH_STYLE, true); + CFRelease(resolvedPath); + return result; + } + } + } + + return NULL; +} + #pragma mark - #pragma mark Bundle Tables -static void _CFBundleAddToTables(CFBundleRef bundle) { +static void _CFBundleAddToTablesLocked(CFBundleRef bundle, CFStringRef bundleID) { if (bundle->_isUnique) return; - - CFStringRef bundleID = CFBundleGetIdentifier(bundle); - _CFMutexLock(&CFBundleGlobalDataLock); - // Add to the _allBundles list if (!_allBundles) { CFArrayCallBacks callbacks = kCFTypeArrayCallBacks; @@ -272,7 +304,17 @@ static void _CFBundleAddToTables(CFBundleRef bundle) { // We've encountered a bundle with this ID already. // Output some additional info here. It may not be an error (adding a newer version of a bundle is supported), so use os_log_debug. - os_log_debug(_CFBundleResourceLogger(), "More than one bundle with the same identifier has been added: %{public}@", bundlesWithThisID); + if (os_log_debug_enabled(_CFBundleResourceLogger())) { + CFMutableArrayRef bundleList = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + for (CFIndex i = 0; i < CFArrayGetCount(bundlesWithThisID); i++) { + CFBundleRef bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, i); + CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Bundle %p at %@"), bundle, bundle->_url); + CFArrayAppendValue(bundleList, desc); + CFRelease(desc); + } + os_log_debug(_CFBundleResourceLogger(), "More than one bundle with the same identifier has been added: %{public}@", bundleList); + CFRelease(bundleList); + } } else { CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; nonRetainingArrayCallbacks.retain = NULL; @@ -283,7 +325,6 @@ static void _CFBundleAddToTables(CFBundleRef bundle) { CFRelease(bundlesWithThisID); } } - _CFMutexUnlock(&CFBundleGlobalDataLock); } static void _CFBundleRemoveFromTables(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef bundleID) { @@ -322,9 +363,8 @@ static void _CFBundleRemoveFromTables(CFBundleRef bundle, CFURLRef bundleURL, CF #endif } -static CFBundleRef _CFBundleGetFromTables(CFStringRef bundleID) { +static CFBundleRef _CFBundleGetFromTablesLocked(CFStringRef bundleID) { CFBundleRef result = NULL, bundle; - _CFMutexLock(&CFBundleGlobalDataLock); if (_bundlesByIdentifier && bundleID) { // Note that this array is maintained in descending order by version number CFArrayRef bundlesWithThisID = (CFArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); @@ -341,11 +381,17 @@ static CFBundleRef _CFBundleGetFromTables(CFStringRef bundleID) { } } } + return result; +} + +static CFBundleRef _CFBundleGetFromTables(CFStringRef bundleID) { + _CFMutexLock(&CFBundleGlobalDataLock); + CFBundleRef result = _CFBundleGetFromTablesLocked(bundleID); _CFMutexUnlock(&CFBundleGlobalDataLock); return result; } -static CFBundleRef _CFBundleCopyFromTablesForURL(CFURLRef url) { +static CFBundleRef _CFBundleCopyFromTablesForURLLocked(CFURLRef url) { /* If you're curious why this doesn't consult the main bundle URL, consider the case where you have a directory structure like this: @@ -359,23 +405,28 @@ static CFBundleRef _CFBundleCopyFromTablesForURL(CFURLRef url) { Since the main bundle is not part of the bundle tables, we can support this scenario by having the _bundlesByURL data structure hold the bundle for URL "/S/L/F/Foo.framework/Foo" and _mainBundle (in CFBundle_Main.c) hold the bundle for URL "/S/L/F/Foo.framework/food". */ CFBundleRef result = NULL; - _CFMutexLock(&CFBundleGlobalDataLock); if (_bundlesByURL) result = (CFBundleRef)CFDictionaryGetValue(_bundlesByURL, url); if (result && !result->_url) { result = NULL; CFDictionaryRemoveValue(_bundlesByURL, url); } if (result) CFRetain(result); + return result; +} + +static CFBundleRef _CFBundleCopyFromTablesForURL(CFURLRef url) { + _CFMutexLock(&CFBundleGlobalDataLock); + CFBundleRef result = _CFBundleCopyFromTablesForURLLocked(url); _CFMutexUnlock(&CFBundleGlobalDataLock); return result; } #pragma mark - -CF_PRIVATE uint8_t _CFBundleEffectiveLayoutVersion(CFBundleRef bundle) { - uint8_t localVersion = bundle->_version; +CF_PRIVATE _CFBundleVersion _CFBundleEffectiveLayoutVersion(CFBundleRef bundle) { + _CFBundleVersion localVersion = bundle->_version; // exclude type 0 bundles with no binary (or CFM binary) and no Info.plist, since they give too many false positives - if (0 == localVersion) { + if (_CFBundleVersionOldStyleResources == localVersion) { CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); if (!infoDict || 0 == CFDictionaryGetCount(infoDict)) { #if defined(BINARY_SUPPORT_DYLD) @@ -383,20 +434,20 @@ CF_PRIVATE uint8_t _CFBundleEffectiveLayoutVersion(CFBundleRef bundle) { if (executableURL) { if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); if (bundle->_binaryType == __CFBundleCFMBinary || bundle->_binaryType == __CFBundleUnreadableBinary) { - localVersion = 4; + localVersion = _CFBundleVersionNotABundle; } else { bundle->_resourceData._executableLacksResourceFork = true; } CFRelease(executableURL); } else { - localVersion = 4; + localVersion = _CFBundleVersionNotABundle; } #else CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); if (executableURL) { CFRelease(executableURL); } else { - localVersion = 4; + localVersion = _CFBundleVersionNotABundle; } #endif /* BINARY_SUPPORT_DYLD */ } @@ -408,8 +459,8 @@ CFBundleRef _CFBundleCreateIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef // It is assumed that users of this SPI do not want this bundle to persist forever. CFBundleRef bundle = _CFBundleCreateUnique(allocator, url); if (bundle) { - uint8_t localVersion = _CFBundleEffectiveLayoutVersion(bundle); - if (3 == localVersion || 4 == localVersion) { + _CFBundleVersion localVersion = _CFBundleEffectiveLayoutVersion(bundle); + if (_CFBundleVersionFlat == localVersion || _CFBundleVersionNotABundle == localVersion) { CFRelease(bundle); bundle = NULL; } @@ -429,7 +480,7 @@ CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) { CFBundleRef _CFBundleGetMainBundleIfLooksLikeBundle(void) { CFBundleRef mainBundle = CFBundleGetMainBundle(); - if (mainBundle && (3 == mainBundle->_version || 4 == mainBundle->_version)) mainBundle = NULL; + if (mainBundle && (_CFBundleVersionFlat == mainBundle->_version || _CFBundleVersionNotABundle == mainBundle->_version)) mainBundle = NULL; return mainBundle; } @@ -485,7 +536,7 @@ CF_EXPORT void _CFBundleFlushBundleCaches(CFBundleRef bundle) { __CFUnlock(&bundle->_lock); } -CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { +static CFBundleRef _CFBundleGetBundleWithIdentifier(CFStringRef bundleID, void *frameworkHint) { CFBundleRef result = NULL; if (bundleID) { CFBundleRef main = CFBundleGetMainBundle(); @@ -503,20 +554,8 @@ CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { #if TARGET_OS_MAC if (!result) { // Try to create the bundle for the caller and try again - void *p = __builtin_return_address(0); - if (p) { - CFStringRef imagePath = _CFBundleCopyLoadedImagePathForPointer(p); - // If the pointer is in Foundation, we were called by NSBundle and we should look one more frame up the stack for a hint - if (imagePath && CFStringHasSuffix(imagePath, CFSTR("/Foundation"))) { - CFRelease(imagePath); - // Reset to NULL in case p is null below, that will make us fall back through the right path - imagePath = NULL; - p = __builtin_return_address(1); - if (p) { - imagePath = _CFBundleCopyLoadedImagePathForPointer(p); - } - } - + if (frameworkHint) { + CFStringRef imagePath = _CFBundleCopyLoadedImagePathForPointer(frameworkHint); if (imagePath) { // As this is a fast-path check, we don't want to be aggressive about assuming that the executable URL that we may have received from DYLD via _CFBundleCopyLoadedImagePathForPointer should be turned into a framework URL. If we do, then it is possible that an executable located inside a framework bundle which does not normally link that framework will cause us to load it unintentionally (31165928). // For example: @@ -555,6 +594,15 @@ CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { return result; } +CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { + // Use the frame that called this as a hint + return _CFBundleGetBundleWithIdentifier(bundleID, __builtin_return_address(0)); +} + +CFBundleRef _CFBundleGetBundleWithIdentifierWithHint(CFStringRef bundleID, void *pointer) { + return _CFBundleGetBundleWithIdentifier(bundleID, pointer); +} + static CFStringRef __CFBundleCopyDescription(CFTypeRef cf) { char buff[CFMaxPathSize]; CFStringRef path = NULL, binaryType = NULL, retval = NULL; @@ -612,9 +660,7 @@ static void __CFBundleDeallocate(CFTypeRef cf) { if (bundle->_executablePath) CFRelease(bundle->_executablePath); if (bundle->_developmentRegion) CFRelease(bundle->_developmentRegion); if (bundle->_infoPlistUrl) CFRelease(bundle->_infoPlistUrl); - if (bundle->_stringTable) CFRelease(bundle->_stringTable); - if (bundle->_bundleBasePath) CFRelease(bundle->_bundleBasePath); if (bundle->_queryTable) CFRelease(bundle->_queryTable); @@ -667,31 +713,86 @@ CFBundleRef _CFBundleGetExistingBundleWithBundleURL(CFURLRef bundleURL) { return bundle; } +static Boolean _CFBundlesHaveEquivalentURL(CFBundleRef b1, CFBundleRef b2) { + CFStringRef path1 = b1->_bundleBasePath; + CFStringRef path2 = b2->_bundleBasePath; + char path1CString[PATH_MAX]; + char path2CString[PATH_MAX]; + + Boolean result = false; + if (CFStringGetFileSystemRepresentation(path1, path1CString, PATH_MAX) && + CFStringGetFileSystemRepresentation(path2, path2CString, PATH_MAX)) { + int fd1 = open(path1CString, O_RDONLY); + int fd2 = open(path2CString, O_RDONLY); + + if (fd1 > 0 && fd2 > 0) { + struct stat statbuf1; + struct stat statbuf2; + + if (fstat(fd1, &statbuf1) == 0 && + fstat(fd2, &statbuf2) == 0 && + statbuf1.st_dev == statbuf2.st_dev && + statbuf1.st_ino == statbuf2.st_ino) + { + result = true; + } + } + + if (fd1 > 0) close(fd1); + if (fd2 > 0) close(fd2); + } + + return result; +} + +/* + The combinations of boolean arguments to this function are as follows: + + doFinal | unique | addToTables + ------- | ------ | ----------- + true false true // CFBundleCreate + true true false // _CFBundleCreateUnique + false false false // _CFBundleCreateMain + false false true // _CFBundleEnsureBundleExistsForImagePath + + n.b. + (unique && addToTables) is an invalid configuration and this function will assert. + (unique && doFinalProcessing) does nothing but there is no assert; doFinalProcessing is ignored + This is because doFinalProcessing is primarily about initializing plugins, but unique bundles cannot be plugins. + + */ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean doFinalProcessing, Boolean unique, Boolean addToTables) { - CFBundleRef bundle = NULL; char buff[CFMaxPathSize]; - Boolean exists = false; - SInt32 mode = 0; CFURLRef newURL = NULL; - uint8_t localVersion = 0; - if (!CFURLGetFileSystemRepresentation(bundleURL, true, (uint8_t *)buff, CFMaxPathSize)) return NULL; - newURL = CFURLCreateFromFileSystemRepresentation(allocator, (uint8_t *)buff, strlen(buff), true); if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); // Don't go searching for the URL in the tables if the bundle is unique or the main bundle (addToTables == false) if (!unique && addToTables) { - bundle = _CFBundleCopyFromTablesForURL(newURL); - if (bundle) { + CFBundleRef existingBundle = _CFBundleCopyFromTablesForURL(newURL); + if (existingBundle) { CFRelease(newURL); - return bundle; + return existingBundle; } } - localVersion = _CFBundleGetBundleVersionForURL(newURL); - if (localVersion == 3) { + + // This section of _CFBundleCreate touches the disk, so it should be done outside the lock. This avoids blocking threads which are just attempting to get an existing bundle on file system access, potentially from a lower-piority thread. + _CFBundleVersion localVersion = _CFBundleGetBundleVersionForURL(newURL); + if (_CFBundleVersionFlat == localVersion) { + Boolean exists = false; + SInt32 mode = 0; SInt32 res = _CFGetPathProperties(allocator, (char *)buff, &exists, &mode, NULL, NULL, NULL, NULL); +#if TARGET_OS_WIN32 + if (!(res == 0 && exists && ((mode & S_IFMT) == S_IFDIR))) { + // 2nd chance at finding a bundle path - remove the last path component (e.g., mybundle.resources) and try again + CFURLRef shorterPath = CFURLCreateCopyDeletingLastPathComponent(allocator, newURL); + CFRelease(newURL); + newURL = shorterPath; + res = _CFGetFileProperties(allocator, newURL, &exists, &mode, NULL, NULL, NULL, NULL); + } +#endif if (res == 0) { if (!exists || ((mode & S_IFMT) != S_IFDIR)) { CFRelease(newURL); @@ -703,7 +804,7 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, } } - bundle = (CFBundleRef)_CFRuntimeCreateInstance(allocator, CFBundleGetTypeID(), sizeof(struct __CFBundle) - sizeof(CFRuntimeBase), NULL); + CFBundleRef bundle = (CFBundleRef)_CFRuntimeCreateInstance(allocator, CFBundleGetTypeID(), sizeof(struct __CFBundle) - sizeof(CFRuntimeBase), NULL); if (!bundle) { CFRelease(newURL); return NULL; @@ -730,7 +831,7 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, bundle->_isUnique = unique; -#if TARGET_OS_MAC +#if TARGET_OS_OSX if (!__CFgetenv("CFBundleDisableStringsSharing") && (strncmp(buff, "/System/Library/Frameworks", 26) == 0) && (strncmp(buff + strlen(buff) - 10, ".framework", 10) == 0)) bundle->_sharesStringsFiles = true; @@ -747,24 +848,80 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, bundle->_additionalResourceLock = CFLockInit; - CFBundleGetInfoDictionary(bundle); - + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFStringRef bundleID = CFBundleGetIdentifier(bundle); + // Do this so that we can use the dispatch_once on the ivar of this bundle safely +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" OSMemoryBarrier(); +#pragma GCC diagnostic pop + + // We cannot add to tables for unique bundles. Return unique bundle results here without heading into the section below where we take a lock. + if (unique) { + assert(!addToTables); + return bundle; + } + + // Locked after here for the global tables + _CFMutexLock(&CFBundleGlobalDataLock); + + // Check to see if we already have a bundle with the same identifier or URL. Someone may have raced us to this point. + CFBundleRef existingBundleWithSameURL = _CFBundleCopyFromTablesForURLLocked(bundle->_url); + if (existingBundleWithSameURL) { + // Deallocating a bundle takes the lock when _useUnsafeUnretainedTables returns true, so release the lock first. + _CFMutexUnlock(&CFBundleGlobalDataLock); + CFRelease(bundle); + + return existingBundleWithSameURL; + } + + // It's unfortunate, but valid, for two different bundles to have the same identifier. However, if they also have the same URL (resolving symlinks), then we can return the original one because they are really the same bundle after all. + CFBundleRef existingBundleWithSameIdentifier = _CFBundleGetFromTablesLocked(bundleID); + if (existingBundleWithSameIdentifier && _CFBundlesHaveEquivalentURL(bundle, existingBundleWithSameIdentifier)) { + // Deallocating a bundle takes the lock when _useUnsafeUnretainedTables returns true, so release the lock first. + _CFMutexUnlock(&CFBundleGlobalDataLock); + CFRelease(bundle); + return (CFBundleRef)CFRetain(existingBundleWithSameIdentifier); + } + + if (doFinalProcessing) { + // Initializing the tables for the list of plugins and initializing the tables for the list of bundles are inherently tied together, because plugins have references back to bundles. + // In order to preserve safety, we take both locks. Always in the order of CFBundleGlobalDataLock -> CFPlugInGlobalDataLock. - // _CFBundleCreateUnique will never load plugins - if (!unique && doFinalProcessing) { // We have to do this before adding the bundle to the tables, otherwise another thread coming along and fetching the bundle will get the bundle ref but the factories for PlugIn have not yet been registered. - _CFBundleInitPlugIn(bundle); + CFBundleRef duplicateFactoryBundle = NULL; + if (!_CFBundleInitPlugIn(bundle, infoDict, &duplicateFactoryBundle)) { + // At this point the most likely explanation is that we have the same factory ID but two different bundles. There is no way to really proceed safely here because the plugin system can only deal with one factory ID -> bundle pairing. + // Get some info for error logging + CFDictionaryRef factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey); + if (factoryDict && CFGetTypeID(factoryDict) != CFDictionaryGetTypeID()) factoryDict = NULL; + os_log_error(_CFBundleLoadingLogger(), "More than one bundle with the same factory UUID detected: %{public}@ in %{public}@ and %{public}@", factoryDict, bundle, duplicateFactoryBundle); + + // Deallocating a bundle takes the lock when _useUnsafeUnretainedTables returns true, so release the lock first. + _CFMutexUnlock(&CFBundleGlobalDataLock); + + // Release the allocated instance + CFRelease(bundle); + + // Release the duplicate factory bundle + if (duplicateFactoryBundle) CFRelease(duplicateFactoryBundle); + return NULL; + } } if (addToTables) { - _CFBundleAddToTables(bundle); + _CFBundleAddToTablesLocked(bundle, bundleID); } + + _CFMutexUnlock(&CFBundleGlobalDataLock); if (doFinalProcessing) { + // Outside of the lock, handle dynamic registration + _CFPlugInHandleDynamicRegistration(bundle); + } - + return bundle; } @@ -819,6 +976,7 @@ CFArrayRef CFBundleCreateBundlesFromDirectory(CFAllocatorRef alloc, CFURLRef dir } CFURLRef CFBundleCopyBundleURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); if (bundle->_url) CFRetain(bundle->_url); return bundle->_url; } @@ -834,6 +992,7 @@ UInt32 CFBundleGetVersionNumber(CFBundleRef bundle) { } CFStringRef CFBundleGetDevelopmentRegion(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); dispatch_once(&bundle->_developmentRegionCalculated, ^{ CFStringRef devRegion = NULL; CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); @@ -863,14 +1022,19 @@ Boolean _CFBundleGetStringsFilesShared(CFBundleRef bundle) { } CF_EXPORT CFURLRef CFBundleCopySupportFilesDirectoryURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFURLRef bundleURL = bundle->_url; - uint8_t version = bundle->_version; + _CFBundleVersion version = bundle->_version; CFURLRef result = NULL; if (bundleURL) { - if (1 == version) { + if (_CFBundleVersionOldStyleSupportFiles == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, bundleURL); - } else if (2 == version) { + } else if (_CFBundleVersionContentsResources == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, bundleURL); + } else if (_CFBundleVersionWrappedContentsResources == version) { + result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedSupportFilesURLFromBase2, bundleURL); + } else if (_CFBundleVersionWrappedFlat == version) { + result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedSupportFilesURLFromBase3, bundleURL); } else { result = (CFURLRef)CFRetain(bundleURL); } @@ -878,15 +1042,19 @@ CF_EXPORT CFURLRef CFBundleCopySupportFilesDirectoryURL(CFBundleRef bundle) { return result; } -CF_PRIVATE CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFURLRef bundleURL, uint8_t version) { +CF_PRIVATE CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFURLRef bundleURL, _CFBundleVersion version) { CFURLRef result = NULL; if (bundleURL) { - if (0 == version) { + if (_CFBundleVersionOldStyleResources == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, bundleURL); - } else if (1 == version) { + } else if (_CFBundleVersionOldStyleSupportFiles == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase1, bundleURL); - } else if (2 == version) { + } else if (_CFBundleVersionContentsResources == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase2, bundleURL); + } else if (_CFBundleVersionWrappedContentsResources == version) { + result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedResourcesURLFromBase2, bundleURL); + } else if (_CFBundleVersionWrappedFlat == version) { + result = _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrappedResourcesURLFromBase3, bundleURL); } else { result = (CFURLRef)CFRetain(bundleURL); } @@ -895,18 +1063,25 @@ CF_PRIVATE CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFURLRef bundl } CF_EXPORT CFURLRef CFBundleCopyResourcesDirectoryURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); return _CFBundleCopyResourcesDirectoryURLInDirectory(bundle->_url, bundle->_version); } -CF_PRIVATE CFURLRef _CFBundleCopyAppStoreReceiptURLInDirectory(CFURLRef bundleURL, uint8_t version) { +CF_PRIVATE CFURLRef _CFBundleCopyAppStoreReceiptURLInDirectory(CFURLRef bundleURL, _CFBundleVersion version) { CFURLRef result = NULL; if (bundleURL) { - if (0 == version) { + if (_CFBundleVersionOldStyleResources == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase0, bundleURL); - } else if (1 == version) { + } else if (_CFBundleVersionOldStyleSupportFiles == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase1, bundleURL); - } else if (2 == version) { + } else if (_CFBundleVersionContentsResources == version) { result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase2, bundleURL); + } else if (_CFBundleVersionFlat == version) { + result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase0, bundleURL); + } else if (_CFBundleVersionWrappedContentsResources == version) { + result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrappedAppStoreReceiptURLFromBase2, bundleURL); + } else if (_CFBundleVersionWrappedFlat == version) { + result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrappedAppStoreReceiptURLFromBase3, bundleURL); } } return result; @@ -916,6 +1091,27 @@ CFURLRef _CFBundleCopyAppStoreReceiptURL(CFBundleRef bundle) { return _CFBundleCopyAppStoreReceiptURLInDirectory(bundle->_url, bundle->_version); } +CF_EXPORT CFURLRef _CFBundleCopyWrappedBundleURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); + _CFBundleVersion version = bundle->_version; + if (version == _CFBundleVersionWrappedContentsResources || version == _CFBundleVersionWrappedFlat) { + return _CFURLCreateResolvedDirectoryWithString(kCFAllocatorSystemDefault, _CFBundleWrapperLinkName, bundle->_url); + } else { + return NULL; + } +} + +CF_EXPORT CFURLRef _CFBundleCopyWrapperContainerURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); + _CFBundleVersion version = bundle->_version; + if (version == _CFBundleVersionWrappedContentsResources || version == _CFBundleVersionWrappedFlat) { + // This is a directory, not a symlink + return CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrapperDirectoryName, bundle->_url); + } else { + return NULL; + } +} + CF_CROSS_PLATFORM_EXPORT CFStringRef _CFBundleCopyExecutablePath(CFBundleRef bundle) { return _CFBundleCopyExecutableName(bundle, NULL, NULL); } @@ -959,10 +1155,12 @@ CF_PRIVATE CFStringRef _CFBundleCopyExecutableName(CFBundleRef bundle, CFURLRef } Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); return bundle->_isLoaded; } CFBundleExecutableType CFBundleGetExecutableType(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFBundleExecutableType result = kCFBundleOtherExecutableType; CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); @@ -1101,6 +1299,7 @@ CFErrorRef _CFBundleCreateError(CFAllocatorRef allocator, CFBundleRef bundle, CF #pragma mark - Boolean _CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); Boolean result = false; CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); @@ -1125,30 +1324,13 @@ Boolean _CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, Boolean forceG if (bundle->_isLoaded) { _CFMutexUnlock(&(bundle->_bundleLoadingLock)); - // Remove from the scheduled unload set if we are there. - _CFMutexLock(&CFBundleGlobalDataLock); - if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); - _CFMutexUnlock(&CFBundleGlobalDataLock); + _CFPlugInUnscheduleForUnloading(bundle); return true; } - - // Unload bundles scheduled for unloading - if (!_scheduledBundlesAreUnloading) { - _CFMutexUnlock(&(bundle->_bundleLoadingLock)); - _CFBundleUnloadScheduledBundles(); - _CFMutexLock(&(bundle->_bundleLoadingLock)); - } - if (bundle->_isLoaded) { - _CFMutexUnlock(&(bundle->_bundleLoadingLock)); - // Remove from the scheduled unload set if we are there. - _CFMutexLock(&CFBundleGlobalDataLock); - if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); - _CFMutexUnlock(&CFBundleGlobalDataLock); - return true; - } + // n.b. some applications may call API like CFBundleGetBundleWithIdentifier during library initialization. This could wind up recursively taking this lock. Therefore, even though it results in the potential for a race on the internals of bundle's data structures, we have to unlock before calling out to dlopen. rdar://67319441 _CFMutexUnlock(&(bundle->_bundleLoadingLock)); - + switch (bundle->_binaryType) { #if defined(BINARY_SUPPORT_DLFCN) case __CFBundleUnreadableBinary: @@ -1204,8 +1386,9 @@ Boolean _CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, Boolean forceG } break; } + if (result && bundle->_plugInData._isPlugIn) { - _CFBundlePlugInLoaded(bundle); + _CFPlugInHandleDynamicRegistration(bundle); } if (!result && error) *error = localError; return result; @@ -1306,15 +1489,18 @@ CFArrayRef CFBundleCopyExecutableArchitectures(CFBundleRef bundle) { } void CFBundleUnloadExecutable(CFBundleRef bundle) { + _CFBundleUnloadExecutable(bundle, false); +} + +CF_PRIVATE void _CFBundleUnloadExecutable(CFBundleRef bundle, Boolean unloadingPlugins) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); // First unload bundles scheduled for unloading (if that's not what we are already doing.) - if (!_scheduledBundlesAreUnloading) _CFBundleUnloadScheduledBundles(); + if (!unloadingPlugins) _CFPlugInUnloadScheduledPlugIns(); if (!bundle->_isLoaded) return; // Remove from the scheduled unload set if we are there. - if (!_scheduledBundlesAreUnloading) _CFMutexLock(&CFBundleGlobalDataLock); - if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); - if (!_scheduledBundlesAreUnloading) _CFMutexUnlock(&CFBundleGlobalDataLock); + _CFPlugInUnscheduleForUnloading(bundle); // Give the plugIn code a chance to realize this... _CFPlugInWillUnload(bundle); @@ -1324,7 +1510,6 @@ void CFBundleUnloadExecutable(CFBundleRef bundle) { _CFMutexUnlock(&(bundle->_bundleLoadingLock)); return; } - _CFMutexUnlock(&(bundle->_bundleLoadingLock)); switch (bundle->_binaryType) { #if defined(BINARY_SUPPORT_DYLD) @@ -1352,47 +1537,8 @@ void CFBundleUnloadExecutable(CFBundleRef bundle) { #endif /* BINARY_SUPPORT_DLFCN */ break; } -} - -CF_PRIVATE void _CFBundleScheduleForUnloading(CFBundleRef bundle) { - _CFMutexLock(&CFBundleGlobalDataLock); - if (!_bundlesToUnload) { - CFSetCallBacks nonRetainingCallbacks = kCFTypeSetCallBacks; - nonRetainingCallbacks.retain = NULL; - nonRetainingCallbacks.release = NULL; - _bundlesToUnload = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingCallbacks); - } - CFSetAddValue(_bundlesToUnload, bundle); - os_log_debug(_CFBundleLoadingLogger(), "Bundle %@ is now scheduled for unloading", bundle); - _CFMutexUnlock(&CFBundleGlobalDataLock); -} - -CF_PRIVATE void _CFBundleUnscheduleForUnloading(CFBundleRef bundle) { - _CFMutexLock(&CFBundleGlobalDataLock); - if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); - os_log_debug(_CFBundleLoadingLogger(), "Bundle %@ is now unscheduled for unloading", bundle); - _CFMutexUnlock(&CFBundleGlobalDataLock); -} - -static void _CFBundleUnloadScheduledBundles(void) { - _CFMutexLock(&CFBundleGlobalDataLock); - if (_bundlesToUnload) { - CFIndex i, c = CFSetGetCount(_bundlesToUnload); - if (c > 0) { - CFBundleRef *unloadThese = (CFBundleRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFBundleRef) * c, 0); - CFSetGetValues(_bundlesToUnload, (const void **)unloadThese); - _scheduledBundlesAreUnloading = true; - for (i = 0; i < c; i++) { - // This will cause them to be removed from the set. (Which is why we copied all the values out of the set up front.) - CFBundleRef unloadMe = unloadThese[i]; - os_log_debug(_CFBundleLoadingLogger(), "Bundle %@ is about to be unloaded", unloadMe); - CFBundleUnloadExecutable(unloadMe); - } - _scheduledBundlesAreUnloading = false; - CFAllocatorDeallocate(kCFAllocatorSystemDefault, unloadThese); - } - } - _CFMutexUnlock(&CFBundleGlobalDataLock); + + _CFMutexUnlock(&(bundle->_bundleLoadingLock)); } #pragma mark - @@ -1553,7 +1699,9 @@ static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath, Boole _CFMutexUnlock(&(bundle->_bundleLoadingLock)); // Perform delayed final processing steps. // This must be done after _isLoaded has been set, for security reasons (3624341). - _CFBundleInitPlugIn(bundle); + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + _CFBundleInitPlugIn(bundle, infoDict, NULL); + _CFPlugInHandleDynamicRegistration(bundle); } CFRelease(curURL); } @@ -1648,7 +1796,7 @@ CF_EXPORT CFArrayRef _CFBundleCopyAllBundles(void) { return bundles; } -CF_PRIVATE uint8_t _CFBundleLayoutVersion(CFBundleRef bundle) { +CF_PRIVATE _CFBundleVersion _CFBundleLayoutVersion(CFBundleRef bundle) { return bundle->_version; } @@ -1657,12 +1805,17 @@ CF_EXPORT CFURLRef _CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) { } CF_EXPORT CFURLRef CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFURLRef result = NULL; - if (1 == bundle->_version) { + if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase1, bundle->_url); - } else if (2 == bundle->_version) { + } else if (_CFBundleVersionContentsResources == bundle->_version) { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedPrivateFrameworksURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedFlat == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedPrivateFrameworksURLFromBase3, bundle->_url); } else { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase0, bundle->_url); } @@ -1674,12 +1827,17 @@ CF_EXPORT CFURLRef _CFBundleCopySharedFrameworksURL(CFBundleRef bundle) { } CF_EXPORT CFURLRef CFBundleCopySharedFrameworksURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFURLRef result = NULL; - if (1 == bundle->_version) { + if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase1, bundle->_url); - } else if (2 == bundle->_version) { + } else if (_CFBundleVersionContentsResources == bundle->_version) { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedFrameworksURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedFlat == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedFrameworksURLFromBase3, bundle->_url); } else { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase0, bundle->_url); } @@ -1691,12 +1849,17 @@ CF_EXPORT CFURLRef _CFBundleCopySharedSupportURL(CFBundleRef bundle) { } CF_EXPORT CFURLRef CFBundleCopySharedSupportURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFURLRef result = NULL; - if (1 == bundle->_version) { + if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase1, bundle->_url); - } else if (2 == bundle->_version) { + } else if (_CFBundleVersionContentsResources == bundle->_version) { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedSupportURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedFlat == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedSharedSupportURLFromBase3, bundle->_url); } else { result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase0, bundle->_url); } @@ -1708,21 +1871,30 @@ CF_PRIVATE CFURLRef _CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { } CF_EXPORT CFURLRef CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFURLRef result = NULL, alternateResult = NULL; CFAllocatorRef alloc = CFGetAllocator(bundle); - if (1 == bundle->_version) { + if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase1, bundle->_url); - } else if (2 == bundle->_version) { + } else if (_CFBundleVersionContentsResources == bundle->_version) { result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedBuiltInPlugInsURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedFlat == bundle->_version) { + result = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedBuiltInPlugInsURLFromBase3, bundle->_url); } else { result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase0, bundle->_url); } if (!result || !_CFURLExists(result)) { - if (1 == bundle->_version) { + if (_CFBundleVersionOldStyleSupportFiles == bundle->_version) { alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase1, bundle->_url); - } else if (2 == bundle->_version) { + } else if (_CFBundleVersionContentsResources == bundle->_version) { alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedContentsResources == bundle->_version) { + alternateResult = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedAlternateBuiltInPlugInsURLFromBase2, bundle->_url); + } else if (_CFBundleVersionWrappedFlat == bundle->_version) { + alternateResult = _CFURLCreateResolvedDirectoryWithString(CFGetAllocator(bundle), _CFBundleWrappedAlternateBuiltInPlugInsURLFromBase3, bundle->_url); } else { alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase0, bundle->_url); } diff --git a/CoreFoundation/PlugIn.subproj/CFBundle.h b/CoreFoundation/PlugIn.subproj/CFBundle.h index a9137a9724..e00359bc18 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle.h +++ b/CoreFoundation/PlugIn.subproj/CFBundle.h @@ -12,6 +12,10 @@ #include #include +#if TARGET_OS_MAC +#include +#endif + CF_IMPLICIT_BRIDGING_ENABLED CF_EXTERN_C_BEGIN @@ -241,7 +245,8 @@ enum { kCFBundleExecutableArchitectureI386 = 0x00000007, kCFBundleExecutableArchitecturePPC = 0x00000012, kCFBundleExecutableArchitectureX86_64 = 0x01000007, - kCFBundleExecutableArchitecturePPC64 = 0x01000012 + kCFBundleExecutableArchitecturePPC64 = 0x01000012, + kCFBundleExecutableArchitectureARM64 API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)) = 0x0100000c, } API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); CF_EXPORT @@ -297,6 +302,17 @@ CFURLRef CFBundleCopyAuxiliaryExecutableURL(CFBundleRef bundle, CFStringRef exec /* app to ship versions of the tool for each platform as it does for the */ /* main app executable. */ +#if TARGET_OS_MAC +CF_EXPORT +Boolean CFBundleIsExecutableLoadable(CFBundleRef bundle) API_AVAILABLE(macos(11.0)) API_UNAVAILABLE(ios, watchos, tvos); + +CF_EXPORT +Boolean CFBundleIsExecutableLoadableForURL(CFURLRef url) API_AVAILABLE(macos(11.0)) API_UNAVAILABLE(ios, watchos, tvos); + +CF_EXPORT +Boolean CFBundleIsArchitectureLoadable(cpu_type_t arch) API_AVAILABLE(macos(11.0)) API_UNAVAILABLE(ios, watchos, tvos); +#endif + /* ==================== Getting a bundle's plugIn ==================== */ CF_EXPORT diff --git a/CoreFoundation/PlugIn.subproj/CFBundlePriv.h b/CoreFoundation/PlugIn.subproj/CFBundlePriv.h index 0b031ce8d9..3edfdf9b1c 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundlePriv.h +++ b/CoreFoundation/PlugIn.subproj/CFBundlePriv.h @@ -230,6 +230,9 @@ void _CFBundleFlushCachesForURL(CFURLRef url) API_DEPRECATED("Function no longer CF_EXPORT void _CFBundleFlushBundleCaches(CFBundleRef bundle); // The previous two functions flush cached resource paths; this one also flushes bundle-specific caches such as the info dictionary and strings files +CF_EXPORT +void _CFBundleFlushLanguageCachesAfterEUIDChange(void); // When a process changes its EDIU during lifetime, language-related caches may be outdated. Call this function to flush those caches. The only known client is loginwindow. Email i18n-help@apple.com before using this. + CF_EXPORT CFArrayRef _CFBundleCopyAllBundles(void); // Pending publication, the only known client of this is PowerBox. Email david_smith@apple.com before using this. @@ -246,12 +249,34 @@ CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFStringRef executablePath); CF_EXPORT CFBundleRef _CFBundleGetBundleWithIdentifierAndLibraryName(CFStringRef bundleID, CFStringRef libraryName) API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)); +/* Provide a hint to CFBundleGetBundleWithIdentifier about which library might be the one with the specified bundle identifier. Looks up the library that contains the specified pointer. If it doesn't work out, still performs the regular search. */ +CF_EXPORT +CFBundleRef _CFBundleGetBundleWithIdentifierWithHint(CFStringRef bundleID, void *pointer) API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0)); + +/* Return the URL of the wrapped bundle (via the symlink at the root of the wrapper bundle). */ +CF_EXPORT +CFURLRef /* Nullable */ _CFBundleCopyWrappedBundleURL(CFBundleRef bundle) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + +/* Return the URL of the wrapper container inside the bundle. */ +CF_EXPORT +CFURLRef /* Nullable */ _CFBundleCopyWrapperContainerURL(CFBundleRef bundle) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + #if TARGET_OS_OSX || TARGET_OS_IPHONE #include CF_EXPORT void _CFBundleSetupXPCBootstrap(xpc_object_t bootstrap) API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); + +CF_EXPORT +void _CFBundleSetupXPCBootstrapWithLanguages(xpc_object_t bootstrap, CFArrayRef appleLanguages) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); #endif +#if TARGET_OS_MAC +CF_EXPORT +cpu_type_t _CFBundleGetPreferredExecutableArchitecture(CFBundleRef bundle) API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); + +CF_EXPORT +cpu_type_t _CFBundleGetPreferredExecutableArchitectureForURL(CFURLRef url) API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); +#endif /* SPI for AppKit usage only, they should be only used in limited secnarios of the application load lifecycle */ diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Binary.c b/CoreFoundation/PlugIn.subproj/CFBundle_Binary.c index 4c5d21b434..1304ff3abd 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Binary.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Binary.c @@ -776,6 +776,7 @@ CF_PRIVATE CFStringRef _CFBundleCopyLoadedImagePathForPointer(void *p) { } void *CFBundleGetFunctionPointerForName(CFBundleRef bundle, CFStringRef funcName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); void *tvp = NULL; // Load if necessary if (!bundle->_isLoaded) { @@ -856,6 +857,7 @@ void _CFBundleGetCFMFunctionPointersForNames(CFBundleRef bundle, CFArrayRef func } void *CFBundleGetDataPointerForName(CFBundleRef bundle, CFStringRef symbolName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); void *dp = NULL; // Load if necessary if (!bundle->_isLoaded && !CFBundleLoadExecutable(bundle)) return NULL; diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Grok.c b/CoreFoundation/PlugIn.subproj/CFBundle_Grok.c index cc791e5baa..1bf1100320 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Grok.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Grok.c @@ -243,7 +243,13 @@ static CFDictionaryRef _CFBundleCreateInfoDictFromFile(int fd, const void *bytes unsigned i, j; CFDictionaryRef result = NULL; Boolean foundIt = false; - if (fd >= 0 && fstat(fd, &statBuf) == 0 && (maploc = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != (void *)-1) { + + int mmapFlags = MAP_PRIVATE; +#if TARGET_OS_MAC + mmapFlags |= MAP_RESILIENT_CODESIGN; +#endif + + if (fd >= 0 && fstat(fd, &statBuf) == 0 && (maploc = mmap(0, statBuf.st_size, PROT_READ, mmapFlags, fd, 0)) != (void *)-1) { loc = maploc; fileLength = statBuf.st_size; } else { diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_InfoPlist.c b/CoreFoundation/PlugIn.subproj/CFBundle_InfoPlist.c index 72333c9e3b..3a9e0101eb 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_InfoPlist.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_InfoPlist.c @@ -27,38 +27,28 @@ #pragma mark - #pragma mark Product and Platform Getters - Exported -static CFStringRef _cfBundlePlatform = NULL; -#if TARGET_OS_IPHONE -static CFStringRef _cfBundlePlatformSuffix = NULL; -CF_PRIVATE CFStringRef _CFBundleGetProductNameSuffix(void); -#endif CF_EXPORT void _CFSetProductName(CFStringRef str) { - // TODO: This should be removed. The "CLASSIC" check below removes the need to set the product name manually. - if (str) CFRetain(str); - _cfBundlePlatform = str; - -#if TARGET_OS_IPHONE - // Reset the suffix version too - _cfBundlePlatformSuffix = NULL; - (void)_CFBundleGetProductNameSuffix(); -#endif - // Note that the previous value is leaked, which is fine normally - // because the initial values would tend to be the constant strings - // below. That is required for thread-safety value due to the Get - // function [not being Copy]. It is also fine because people - // shouldn't be screwing around with this value casually. + // Obsolete, does nothing } CF_EXPORT CFStringRef _CFGetProductName(void) { -#if TARGET_OS_IPHONE - if (!_cfBundlePlatform) { + static CFStringRef _cfBundlePlatform = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ +#if TARGET_OS_MAC // We only honor the classic suffix if it is one of two preset values. Otherwise we fall back to the result of sysctlbyname. const char *classicSuffix = __CFgetenv("CLASSIC_SUFFIX"); if (classicSuffix && strncmp(classicSuffix, "iphone", strlen("iphone")) == 0) { - _cfBundlePlatform = _CFBundleiPhoneDeviceName; + os_log_debug(_CFBundleResourceLogger(), "Using ~iphone resources (classic)"); + _cfBundlePlatform = _CFBundleiPhoneDeviceName; } else if (classicSuffix && strncmp(classicSuffix, "ipad", strlen("ipad")) == 0) { - _cfBundlePlatform = _CFBundleiPadDeviceName; + os_log_debug(_CFBundleResourceLogger(), "Using ~ipad resources (classic)"); + _cfBundlePlatform = _CFBundleiPadDeviceName; } else { +#if TARGET_OS_OSX + // Do not check the sysctl on macOS + _cfBundlePlatform = CFSTR(""); +#else char buffer[256]; memset(buffer, 0, sizeof(buffer)); size_t buflen = sizeof(buffer); @@ -68,12 +58,12 @@ CF_EXPORT CFStringRef _CFGetProductName(void) { if (6 <= buflen && 0 == memcmp(buffer, "iPhone", 6)) { _cfBundlePlatform = _CFBundleiPhoneDeviceName; } else - if (4 <= buflen && 0 == memcmp(buffer, "iPod", 4)) { - _cfBundlePlatform = _CFBundleiPodDeviceName; - } else - if (4 <= buflen && 0 == memcmp(buffer, "iPad", 4)) { - _cfBundlePlatform = _CFBundleiPadDeviceName; - } + if (4 <= buflen && 0 == memcmp(buffer, "iPod", 4)) { + _cfBundlePlatform = _CFBundleiPodDeviceName; + } else + if (4 <= buflen && 0 == memcmp(buffer, "iPad", 4)) { + _cfBundlePlatform = _CFBundleiPadDeviceName; + } #elif TARGET_OS_WATCH if (5 <= buflen && 0 == memcmp(buffer, "Watch", 5)) { _cfBundlePlatform = _CFBundleAppleWatchDeviceName; @@ -101,30 +91,33 @@ CF_EXPORT CFStringRef _CFGetProductName(void) { } } } +#endif // TARGET_OS_OSX + + os_log_debug(_CFBundleResourceLogger(), "Using ~%@ resources", _cfBundlePlatform); } +#endif // TARGET_OS_MAC + // This used to fall back to "iphone" on all unknown TARGET_OS_IPHONE platforms, but since that macro covers a wide swath of platforms, it now falls back to an empty string. - if (!_cfBundlePlatform) _cfBundlePlatform = CFSTR(""); // fallback - } + if (!_cfBundlePlatform) { + os_log_debug(_CFBundleResourceLogger(), "Using ~ resources"); + _cfBundlePlatform = CFSTR(""); // fallback + } + }); + return _cfBundlePlatform; -#endif - return CFSTR(""); } CF_PRIVATE CFStringRef _CFBundleGetProductNameSuffix(void) { -#if TARGET_OS_IPHONE - // Not dispatch once, because this can be reset (by a rare API call). If a race happens, it just leaks one string. - if (!_cfBundlePlatformSuffix) { + static CFStringRef _cfBundlePlatformSuffix = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ CFStringRef productName = _CFGetProductName(); if (CFEqual(productName, _CFBundleiPodDeviceName)) { productName = _CFBundleiPhoneDeviceName; } _cfBundlePlatformSuffix = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("~%@"), productName); - } + }); return _cfBundlePlatformSuffix; -#else - // Pedantically correct - _CFGetProductName returns an empty string on non-embedded platforms. - return CFSTR("~"); -#endif } CF_PRIVATE CFStringRef _CFBundleGetPlatformNameSuffix(void) { @@ -442,6 +435,13 @@ CF_PRIVATE void _CFBundleInfoPlistProcessInfoDictionary(CFMutableDictionaryRef d CFStringRef keyPlatformSuffix, keyProductSuffix, keySpecialCaseSuffix, keyName; CFStringRef key = (CFStringRef)keys[idx]; + // Non-string keys in plists aren't valid so remove them + // if we come across one + if (CFGetTypeID(key) != _kCFRuntimeIDCFString) { + CFDictionaryRemoveValue(dict, key); + continue; + } + Boolean const useFallbackPlatformAndProductKey = false; if (_isSpecialCaseKey(key, &keyName, &keySpecialCaseSuffix)) { // This special case key overrides the base value @@ -666,7 +666,7 @@ static UInt32 _CFVersionNumberFromString(CFStringRef versStr) { #pragma mark Info Plist Functions // If infoPlistUrl is passed as non-null it will return retained as the out parameter; callers are responsible for releasing. -static CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, CFURLRef * infoPlistUrl, uint8_t version) { +static CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, CFURLRef * infoPlistUrl, _CFBundleVersion version) { // We only return NULL for a bad URL, otherwise we create a dummy dictionary if (!url) return NULL; @@ -678,19 +678,27 @@ static CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAlloc CFStringRef infoURLFromBase = _CFBundleInfoURLFromBase0; CFURLRef directoryURL = NULL; - if (0 == version) { + if (_CFBundleVersionOldStyleResources == version) { directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, url); platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase0; infoURLFromBase = _CFBundleInfoURLFromBase0; - } else if (1 == version) { + } else if (_CFBundleVersionOldStyleSupportFiles == version) { directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, url); platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase1; infoURLFromBase = _CFBundleInfoURLFromBase1; - } else if (2 == version) { + } else if (_CFBundleVersionContentsResources == version) { directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, url); platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase2; infoURLFromBase = _CFBundleInfoURLFromBase2; - } else if (3 == version) { + } else if (_CFBundleVersionWrappedContentsResources == version) { + directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrappedSupportFilesURLFromBase2, url); + platformInfoURLFromBase = _CFBundleWrappedPlatformInfoURLFromBase2; + infoURLFromBase = _CFBundleWrappedInfoURLFromBase2; + } else if (_CFBundleVersionWrappedFlat == version) { + directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrappedSupportFilesURLFromBase3, url); + platformInfoURLFromBase = _CFBundleWrappedPlatformInfoURLFromBase3; + infoURLFromBase = _CFBundleWrappedInfoURLFromBase3; + } else if (_CFBundleVersionFlat == version) { CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle if (path) { @@ -808,10 +816,10 @@ static CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAlloc return result; } -CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) { +CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, _CFBundleVersion *version) { CFDictionaryRef dict = NULL; unsigned char buff[CFMaxPathSize]; - uint8_t localVersion = 0; + _CFBundleVersion localVersion = _CFBundleVersionOldStyleResources; if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) { CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true); @@ -1011,6 +1019,7 @@ CF_PRIVATE void _CFBundleRefreshInfoDictionaryAlreadyLocked(CFBundleRef bundle) } CFDictionaryRef CFBundleGetInfoDictionary(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); __CFLock(&bundle->_lock); _CFBundleRefreshInfoDictionaryAlreadyLocked(bundle); __CFUnlock(&bundle->_lock); @@ -1022,6 +1031,7 @@ CFDictionaryRef _CFBundleGetLocalInfoDictionary(CFBundleRef bundle) { } CFDictionaryRef CFBundleGetLocalInfoDictionary(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFDictionaryRef localInfoDict = NULL; __CFLock(&bundle->_lock); localInfoDict = bundle->_localInfoDict; @@ -1132,6 +1142,14 @@ static void __addSuffixesToKeys(const void *value, void *context) { // from CFUtilities.c CF_PRIVATE Boolean _CFReadMappedFromFile(CFStringRef path, Boolean map, Boolean uncached, void **outBytes, CFIndex *outLength, CFErrorRef *errorPtr); +// Ensure keyPaths are actually `CFString` +static void __validPlistKeys(const void *value, void *context) { + CFStringRef key = (CFStringRef)value; + if (CFGetTypeID(key) != _kCFRuntimeIDCFString) { + HALT_MSG("Property lists must have string keys!"); + } +} + // implementation of below functions - takes URL as parameter static CFPropertyListRef _CFBundleCreateFilteredInfoPlistWithURL(CFURLRef infoPlistURL, CFSetRef keyPaths, _CFBundleFilteredPlistOptions options) { CFPropertyListRef result = NULL; @@ -1156,6 +1174,11 @@ static CFPropertyListRef _CFBundleCreateFilteredInfoPlistWithURL(CFURLRef infoPl if (!success) return CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDataRef infoPlistData = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8 *)bytes, length, kCFAllocatorNull); + + // Ensure `keyPaths` are all actually `CFString`s + // if not `HALT` on first invalid keyPath + CFSetApplyFunction(keyPaths, __validPlistKeys, NULL); + // We need to include all possible variants of the platform/product combo as possible keys. CFMutableSetRef newKeyPaths = CFSetCreateMutable(kCFAllocatorSystemDefault, CFSetGetCount(keyPaths), &kCFTypeSetCallBacks); CFSetApplyFunction(keyPaths, __addSuffixesToKeys, newKeyPaths); diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h index 6534b3e3ea..13b3f2a6f5 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h @@ -63,6 +63,16 @@ typedef struct __CFResourceData { char _padding[2]; } _CFResourceData; +typedef CF_ENUM(uint8_t, _CFBundleVersion) { + _CFBundleVersionOldStyleResources = 0, + _CFBundleVersionOldStyleSupportFiles = 1, + _CFBundleVersionContentsResources = 2, + _CFBundleVersionFlat = 3, + _CFBundleVersionNotABundle = 4, + _CFBundleVersionWrappedContentsResources = 12, + _CFBundleVersionWrappedFlat = 13, +}; + CF_PRIVATE _CFResourceData *__CFBundleGetResourceData(CFBundleRef bundle); typedef struct __CFPlugInData { @@ -72,6 +82,7 @@ typedef struct __CFPlugInData { Boolean _needsDynamicRegistration; Boolean _registeredFactory; UInt32 _instanceCount; + UInt32 _unloadPreventionCount; CFMutableArrayRef _factories; } _CFPlugInData; @@ -90,7 +101,8 @@ struct __CFBundle { __CFPBinaryType _binaryType; _Atomic(Boolean) _isLoaded; - uint8_t _version; + _CFBundleVersion _version; + Boolean _sharesStringsFiles; Boolean _isUnique; @@ -136,7 +148,6 @@ struct __CFBundle { #if defined(BINARY_SUPPORT_DLL) HMODULE _hModule; #endif /* BINARY_SUPPORT_DLL */ - }; CF_PRIVATE os_log_t _CFBundleResourceLogger(void); @@ -147,6 +158,7 @@ extern _CFPlugInData *__CFBundleGetPlugInData(CFBundleRef bundle); /* Private CFBundle API */ CF_PRIVATE CFErrorRef _CFBundleCreateErrorDebug(CFAllocatorRef allocator, CFBundleRef bundle, CFIndex code, CFStringRef debugString); +CF_PRIVATE CFURLRef _CFURLCreateResolvedDirectoryWithString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL); CF_PRIVATE void _CFBundleInfoPlistProcessInfoDictionary(CFMutableDictionaryRef dict); CF_PRIVATE Boolean _CFBundleSupportedProductName(CFStringRef fileName, CFRange searchRange); @@ -170,7 +182,7 @@ CF_PRIVATE CFStringRef _CFBundleCopyLoadedImagePathForPointer(void *p); // Languages and locales -CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFURLRef url, uint8_t *version); +CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFURLRef url, _CFBundleVersion *version); CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInBundle(CFBundleRef bundle); CF_PRIVATE Boolean CFBundleAllowMixedLocalizations(void); @@ -180,11 +192,11 @@ CF_PRIVATE Boolean CFBundleAllowMixedLocalizations(void); CF_PRIVATE Boolean _CFIsResourceAtURL(CFURLRef url, Boolean *isDir); CF_PRIVATE Boolean _CFIsResourceAtPath(CFStringRef path, Boolean *isDir); -CF_PRIVATE uint8_t _CFBundleGetBundleVersionForURL(CFURLRef url); +CF_PRIVATE _CFBundleVersion _CFBundleGetBundleVersionForURL(CFURLRef url); CF_PRIVATE CFBundleRef _CFBundleCreateMain(CFAllocatorRef allocator, CFURLRef mainBundleURL); -CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt8 *version); -CF_PRIVATE CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFURLRef bundleURL, UInt8 version); +CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, _CFBundleVersion *version); +CF_PRIVATE CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFURLRef bundleURL, _CFBundleVersion version); CF_PRIVATE Boolean _CFBundleCouldBeBundle(CFURLRef url); CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInResourceForkWithAllocator(CFAllocatorRef alloc, CFURLRef url); @@ -199,11 +211,13 @@ CF_PRIVATE void _CFBundleRefreshInfoDictionaryAlreadyLocked(CFBundleRef bundle); CF_PRIVATE CFStringRef _CFBundleGetPlatformExecutablesSubdirectoryName(void); -CF_PRIVATE void _CFBundleScheduleForUnloading(CFBundleRef bundle); -CF_PRIVATE void _CFBundleUnscheduleForUnloading(CFBundleRef bundle); +CF_PRIVATE void _CFPlugInUnscheduleForUnloading(CFBundleRef bundle); +CF_PRIVATE void _CFPlugInUnloadScheduledPlugIns(void); +CF_PRIVATE void _CFBundleUnloadExecutable(CFBundleRef bundle, Boolean unloadingPlugins); +CF_PRIVATE void _CFPlugInHandleDynamicRegistration(CFBundleRef bundle); -CF_PRIVATE UInt8 _CFBundleLayoutVersion(CFBundleRef bundle); -CF_PRIVATE uint8_t _CFBundleEffectiveLayoutVersion(CFBundleRef bundle); +CF_PRIVATE _CFBundleVersion _CFBundleLayoutVersion(CFBundleRef bundle); +CF_PRIVATE _CFBundleVersion _CFBundleEffectiveLayoutVersion(CFBundleRef bundle); #if defined(BINARY_SUPPORT_DYLD) @@ -239,8 +253,8 @@ extern void *_CFBundleDLLGetSymbolByName(CFBundleRef bundle, CFStringRef symbolN /* Private PlugIn-related CFBundle API */ -extern void _CFBundleInitPlugIn(CFBundleRef bundle); -extern void _CFBundlePlugInLoaded(CFBundleRef bundle); +extern Boolean _CFBundleInitPlugIn(CFBundleRef bundle, CFDictionaryRef infoDict, CFBundleRef *existingPlugIn); +extern void _CFPlugInHandleDynamicRegistration(CFBundleRef bundle); extern void _CFBundleDeallocatePlugIn(CFBundleRef bundle); extern void _CFPlugInWillUnload(CFPlugInRef plugIn); @@ -249,32 +263,46 @@ extern void _CFPlugInWillUnload(CFPlugInRef plugIn); #define _CFBundleSupportFilesDirectoryName1 CFSTR("Support Files") #define _CFBundleSupportFilesDirectoryName2 CFSTR("Contents") #define _CFBundleResourcesDirectoryName CFSTR("Resources") +#define _CFBundleWrapperLinkName CFSTR("WrappedBundle") +#define _CFBundleWrapperDirectoryName CFSTR("Wrapper") #define _CFBundleExecutablesDirectoryName CFSTR("Executables") #define _CFBundleNonLocalizedResourcesDirectoryName CFSTR("Non-localized Resources") #if TARGET_OS_WIN32 #define _CFBundleSupportFilesDirectoryName1WithResources CFSTR("Support Files\\Resources") #define _CFBundleSupportFilesDirectoryName2WithResources CFSTR("Contents\\Resources") +#define _CFBundleWrappedSupportFilesDirectoryName2WithResources CFSTR("WrappedBundle\\Contents\\Resources") #else #define _CFBundleSupportFilesDirectoryName1WithResources CFSTR("Support Files/Resources") #define _CFBundleSupportFilesDirectoryName2WithResources CFSTR("Contents/Resources") +#define _CFBundleWrappedSupportFilesDirectoryName2WithResources CFSTR("WrappedBundle/Contents/Resources") #endif #define _CFBundleSupportFilesURLFromBase1 CFSTR("Support%20Files/") #define _CFBundleSupportFilesURLFromBase2 CFSTR("Contents/") +#define _CFBundleWrappedSupportFilesURLFromBase2 CFSTR("WrappedBundle/Contents/") +#define _CFBundleWrappedSupportFilesURLFromBase3 CFSTR("WrappedBundle/") #define _CFBundleResourcesURLFromBase0 CFSTR("Resources/") #define _CFBundleResourcesURLFromBase1 CFSTR("Support%20Files/Resources/") #define _CFBundleResourcesURLFromBase2 CFSTR("Contents/Resources/") +#define _CFBundleWrappedResourcesURLFromBase2 CFSTR("WrappedBundle/Contents/Resources/") +#define _CFBundleWrappedResourcesURLFromBase3 CFSTR("WrappedBundle/") #define _CFBundleAppStoreReceiptURLFromBase0 CFSTR("_MASReceipt/receipt") #define _CFBundleAppStoreReceiptURLFromBase1 CFSTR("Support%20Files/_MASReceipt/receipt") #define _CFBundleAppStoreReceiptURLFromBase2 CFSTR("Contents/_MASReceipt/receipt") +#define _CFBundleWrappedAppStoreReceiptURLFromBase2 CFSTR("WrappedBundle/Contents/_MASReceipt/receipt") +#define _CFBundleWrappedAppStoreReceiptURLFromBase3 CFSTR("WrappedBundle/_MASReceipt/receipt") #define _CFBundleExecutablesURLFromBase1 CFSTR("Support%20Files/Executables/") #define _CFBundleExecutablesURLFromBase2 CFSTR("Contents/") +#define _CFBundleWrappedExecutablesURLFromBase2 CFSTR("WrappedBundle/Contents/") +#define _CFBundleWrappedExecutablesURLFromBase3 CFSTR("WrappedBundle/") #define _CFBundleInfoURLFromBase0 CFSTR("Resources/Info.plist") #define _CFBundleInfoURLFromBase1 CFSTR("Support%20Files/Info.plist") #define _CFBundleInfoURLFromBase2 CFSTR("Contents/Info.plist") #define _CFBundleInfoURLFromBase3 CFSTR("Info.plist") +#define _CFBundleWrappedInfoURLFromBase2 CFSTR("WrappedBundle/Contents/Info.plist") +#define _CFBundleWrappedInfoURLFromBase3 CFSTR("WrappedBundle/Info.plist") #define _CFBundleInfoURLFromBaseNoExtension3 CFSTR("Info") #if TARGET_OS_OSX @@ -282,17 +310,23 @@ extern void _CFPlugInWillUnload(CFPlugInRef plugIn); #define _CFBundlePlatformInfoURLFromBase1 CFSTR("Support%20Files/Info-macos.plist") #define _CFBundlePlatformInfoURLFromBase2 CFSTR("Contents/Info-macos.plist") #define _CFBundlePlatformInfoURLFromBase3 CFSTR("Info-macos.plist") +#define _CFBundleWrappedPlatformInfoURLFromBase2 CFSTR("WrappedBundle/Contents/Info-macos.plist") +#define _CFBundleWrappedPlatformInfoURLFromBase3 CFSTR("WrappedBundle/Info-macos.plist") #elif TARGET_OS_IPHONE #define _CFBundlePlatformInfoURLFromBase0 CFSTR("Resources/Info-iphoneos.plist") #define _CFBundlePlatformInfoURLFromBase1 CFSTR("Support%20Files/Info-iphoneos.plist") #define _CFBundlePlatformInfoURLFromBase2 CFSTR("Contents/Info-iphoneos.plist") #define _CFBundlePlatformInfoURLFromBase3 CFSTR("Info-iphoneos.plist") +#define _CFBundleWrappedPlatformInfoURLFromBase2 CFSTR("WrappedBundle/Contents/Info-iphoneos.plist") +#define _CFBundleWrappedPlatformInfoURLFromBase3 CFSTR("WrappedBundle/Info-iphoneos.plist") #else // No platform-specific variants in these cases #define _CFBundlePlatformInfoURLFromBase0 _CFBundleInfoURLFromBase0 #define _CFBundlePlatformInfoURLFromBase1 _CFBundleInfoURLFromBase1 #define _CFBundlePlatformInfoURLFromBase2 _CFBundleInfoURLFromBase2 #define _CFBundlePlatformInfoURLFromBase3 _CFBundleInfoURLFromBase3 +#define _CFBundleWrappedPlatformInfoURLFromBase2 _CFBundleWrappedInfoURLFromBase2 +#define _CFBundleWrappedPlatformInfoURLFromBase3 _CFBundleWrappedInfoURLFromBase3 #endif #define _CFBundleInfoPlistName CFSTR("Info.plist") @@ -314,18 +348,28 @@ extern void _CFPlugInWillUnload(CFPlugInRef plugIn); #define _CFBundlePrivateFrameworksURLFromBase0 CFSTR("Frameworks/") #define _CFBundlePrivateFrameworksURLFromBase1 CFSTR("Support%20Files/Frameworks/") #define _CFBundlePrivateFrameworksURLFromBase2 CFSTR("Contents/Frameworks/") +#define _CFBundleWrappedPrivateFrameworksURLFromBase2 CFSTR("WrappedBundle/Contents/Frameworks/") +#define _CFBundleWrappedPrivateFrameworksURLFromBase3 CFSTR("WrappedBundle/Frameworks/") #define _CFBundleSharedFrameworksURLFromBase0 CFSTR("SharedFrameworks/") #define _CFBundleSharedFrameworksURLFromBase1 CFSTR("Support%20Files/SharedFrameworks/") #define _CFBundleSharedFrameworksURLFromBase2 CFSTR("Contents/SharedFrameworks/") +#define _CFBundleWrappedSharedFrameworksURLFromBase2 CFSTR("WrappedBundle/Contents/SharedFrameworks/") +#define _CFBundleWrappedSharedFrameworksURLFromBase3 CFSTR("WrappedBundle/SharedFrameworks/") #define _CFBundleSharedSupportURLFromBase0 CFSTR("SharedSupport/") #define _CFBundleSharedSupportURLFromBase1 CFSTR("Support%20Files/SharedSupport/") #define _CFBundleSharedSupportURLFromBase2 CFSTR("Contents/SharedSupport/") +#define _CFBundleWrappedSharedSupportURLFromBase2 CFSTR("WrappedBundle/Contents/SharedSupport/") +#define _CFBundleWrappedSharedSupportURLFromBase3 CFSTR("WrappedBundle/SharedSupport/") #define _CFBundleBuiltInPlugInsURLFromBase0 CFSTR("PlugIns/") #define _CFBundleBuiltInPlugInsURLFromBase1 CFSTR("Support%20Files/PlugIns/") #define _CFBundleBuiltInPlugInsURLFromBase2 CFSTR("Contents/PlugIns/") +#define _CFBundleWrappedBuiltInPlugInsURLFromBase2 CFSTR("WrappedBundle/Contents/PlugIns/") +#define _CFBundleWrappedBuiltInPlugInsURLFromBase3 CFSTR("WrappedBundle/PlugIns/") #define _CFBundleAlternateBuiltInPlugInsURLFromBase0 CFSTR("Plug-ins/") #define _CFBundleAlternateBuiltInPlugInsURLFromBase1 CFSTR("Support%20Files/Plug-ins/") #define _CFBundleAlternateBuiltInPlugInsURLFromBase2 CFSTR("Contents/Plug-ins/") +#define _CFBundleWrappedAlternateBuiltInPlugInsURLFromBase2 CFSTR("WrappedBundle/Contents/Plug-ins/") +#define _CFBundleWrappedAlternateBuiltInPlugInsURLFromBase3 CFSTR("WrappedBundle/Plug-ins/") #define _CFBundleLprojExtension CFSTR("lproj") #define _CFBundleLprojExtensionWithDot CFSTR(".lproj") @@ -367,7 +411,8 @@ STATIC_CONST_STRING_DECL(_CFBundleAppleTVDeviceName, "appletv"); CF_PRIVATE CFStringRef _CFBundleGetProductNameSuffix(void); CF_PRIVATE CFStringRef _CFBundleGetPlatformNameSuffix(void); -#define _CFBundleDefaultStringTableName CFSTR("Localizable") +CF_PRIVATE const CFStringRef _kCFBundleUseAppleLocalizationsKey; + #define _CFBundleStringTableType CFSTR("strings") #define _CFBundleStringDictTableType CFSTR("stringsdict") @@ -378,6 +423,47 @@ CF_PRIVATE CFStringRef _CFBundleGetPlatformNameSuffix(void); #define _CFBundleSiblingResourceDirectoryExtension CFSTR("resources") +#pragma mark - +#pragma mark Resolving paths from FDs + +// The buffer must be PATH_MAX long or more. +static bool _CFGetPathFromFileDescriptor(int fd, char *path); + +#if TARGET_OS_MAC || (TARGET_OS_BSD && !defined(__OpenBSD__)) + +static bool _CFGetPathFromFileDescriptor(int fd, char *path) { + return fcntl(fd, F_GETPATH, path) != -1; +} + +#elif TARGET_OS_LINUX + +static bool _CFGetPathFromFileDescriptor(int fd, char *path) { + char procfs[PATH_MAX] = { 0 }; + if (snprintf(procfs, PATH_MAX, "/proc/self/fd/%d", fd) < 0) { + return false; + } + + ssize_t size = readlink(procfs, path, PATH_MAX); + if (size != -1) { + return false; + } + + if (size < PATH_MAX - 1) { + path[size + 1] = 0; + } + + return true; +} + +#else + +static bool _CFGetPathFromFileDescriptor(int fd, char *path) { + #warning This platform does not have a way to go back from an open file descriptor to a path. + return false; +} + +#endif + CF_EXTERN_C_END #endif /* ! __COREFOUNDATION_CFBUNDLE_INTERNAL__ */ diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c b/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c index 7ae492145a..567caf3129 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c @@ -45,6 +45,13 @@ static Boolean _CFBundleGetInfoDictionaryBoolean(CFStringRef key) { return result; } +static Boolean _CFBundleIsExtension(void) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFDictionaryRef infoDict = mainBundle ? CFBundleGetInfoDictionary(mainBundle) : NULL; + CFTypeRef infoDictValue = infoDict ? CFDictionaryGetValue(infoDict, CFSTR("NSExtension")) : NULL; + return infoDictValue != NULL; +} + CF_PRIVATE Boolean CFBundleAllowMixedLocalizations(void) { static Boolean allowMixed = false; static dispatch_once_t once = 0; @@ -58,7 +65,8 @@ static Boolean CFBundleFollowParentLocalization(void) { static Boolean followParent = false; static dispatch_once_t once = 0; dispatch_once(&once, ^{ - followParent = _CFBundleGetInfoDictionaryBoolean(CFSTR("CFBundleFollowParentLocalization")); + // Info.plists with the CFBundleFollowParentLocalization key or any extension are allowed to use this + followParent = _CFBundleGetInfoDictionaryBoolean(CFSTR("CFBundleFollowParentLocalization")) || _CFBundleIsExtension(); }); return followParent; @@ -366,6 +374,24 @@ CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SIn #pragma mark - +static CFArrayRef _copyBundleLocalizationsFromResources(CFBundleRef bundle); + +static CFArrayRef _copyAppleLocalizations(CFDictionaryRef infoDict) { + CFArrayRef result = NULL; + // Only on Darwin +#if TARGET_OS_MAC + CFBooleanRef useAppleLocalizations = (CFBooleanRef)CFDictionaryGetValue(infoDict, _kCFBundleUseAppleLocalizationsKey); + if (useAppleLocalizations == kCFBooleanTrue) { + CFURLRef cfURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, CFSTR("/System/Library/Frameworks/CoreFoundation.framework"), kCFURLPOSIXPathStyle, true); + CFBundleRef cf = CFBundleCreate(kCFAllocatorSystemDefault, cfURL); + result = _copyBundleLocalizationsFromResources(cf); + CFRelease(cfURL); + CFRelease(cf); + } +#endif + return result; +} + // Get a list of lproj directories for a particular resource directory URL. Uncached. Does not include any predefined localizations from an Info.plist. This function does make any attempt to localize or canonicalize the results. static CFArrayRef _CFBundleCopyLProjDirectoriesForURL(CFAllocatorRef allocator, CFURLRef url) { __block CFMutableArrayRef result = NULL; @@ -411,6 +437,8 @@ static CFArrayRef _copyBundleLocalizationsFromResources(CFBundleRef bundle) { } result = CFArrayCreateCopy(CFGetAllocator(bundle), realPredefinedLocalizations); CFRelease(realPredefinedLocalizations); + } else { + result = _copyAppleLocalizations(infoDict); } } @@ -472,6 +500,7 @@ static CFArrayRef _copyBundleLocalizationsFromResources(CFBundleRef bundle) { Since the result of this is "typically passed as a parameter to either the CFBundleCopyPreferredLocalizationsFromArray or CFBundleCopyLocalizationsForPreferences function", those other functions will take into account the user prefs and pick the right lproj. */ CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); CFArrayRef result = NULL; __CFLock(&bundle->_lock); @@ -516,6 +545,12 @@ CF_EXPORT CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) { if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) == CFArrayGetTypeID()) { result = (CFArrayRef)CFRetain(predefinedLocalizations); } + + if (!result) { + // Check for using Apple localizations + result = _copyAppleLocalizations(infoDict); + } + if (!result) { devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); if (devLang && (CFGetTypeID(devLang) == CFStringGetTypeID() && CFStringGetLength(devLang) > 0)) { @@ -534,7 +569,7 @@ CF_EXPORT CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) { static CFArrayRef _CFBundleUserLanguages = NULL; static os_unfair_lock _CFBundleUserLanguagesLock = OS_UNFAIR_LOCK_INIT; -CF_PRIVATE void _CFBundleFlushUserLanguagesCache() { +static void _CFBundleFlushUserLanguagesCache(void) { os_unfair_lock_lock(&_CFBundleUserLanguagesLock); if (_CFBundleUserLanguages) { CFRelease(_CFBundleUserLanguages); @@ -583,6 +618,10 @@ CF_PRIVATE CFArrayRef _CFBundleCopyUserLanguages() { return result; } +CF_EXPORT void _CFBundleFlushLanguageCachesAfterEUIDChange() { + _CFBundleFlushBundleCaches(CFBundleGetMainBundle()); + _CFBundleFlushUserLanguagesCache(); +} CF_EXPORT void _CFBundleGetLanguageAndRegionCodes(SInt32 *languageCode, SInt32 *regionCode) { // an attempt to answer the question, "what language are we running in?" @@ -905,7 +944,9 @@ CF_EXPORT void _CFBundleSetDefaultLocalization(CFStringRef localizationName) { // This is the funnel point for looking up languages for a particular bundle. The returned order reflects the user preferences. CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInBundle(CFBundleRef bundle) { + __CFLock(&bundle->_lock); if (!bundle->_searchLanguages) { + __CFUnlock(&bundle->_lock); // includes predefined localizations CFArrayRef localizationsForBundle = CFBundleCopyBundleLocalizations(bundle); CFArrayRef userLanguages = _CFBundleCopyUserLanguages(); @@ -956,16 +997,22 @@ CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInBundle(CFBundleRef bundle } } - if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)result, (void * volatile *)&(bundle->_searchLanguages))) { + __CFLock(&bundle->_lock); + if (bundle->_searchLanguages) { + // Lost the race CFRelease(result); + } else { + bundle->_searchLanguages = result; } } - return (CFArrayRef)CFRetain(bundle->_searchLanguages); + CFArrayRef result = (CFArrayRef)CFRetain(bundle->_searchLanguages); + __CFUnlock(&bundle->_lock); + return result; } // This is the funnel point for looking up languages for a particular directory. -CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFURLRef url, uint8_t *version) { - uint8_t localVersion = 0; +CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFURLRef url, _CFBundleVersion *version) { + _CFBundleVersion localVersion = _CFBundleVersionOldStyleResources; CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, &localVersion); CFArrayRef predefinedLocalizations = NULL; @@ -975,9 +1022,15 @@ CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFURLRef url, u if (devLang && (CFGetTypeID(devLang) != CFStringGetTypeID() || CFStringGetLength(devLang) == 0)) devLang = NULL; predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey); + if (predefinedLocalizations) CFRetain(predefinedLocalizations); if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) { + CFRelease(predefinedLocalizations); predefinedLocalizations = NULL; } + + if (!predefinedLocalizations) { + predefinedLocalizations = _copyAppleLocalizations(infoDict); + } } CFURLRef resourcesURL = _CFBundleCopyResourcesDirectoryURLInDirectory(url, localVersion); @@ -994,6 +1047,8 @@ CF_PRIVATE CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFURLRef url, u } else if (!localizationsInDirectory) { localizationsInDirectory = CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks); } + + if (predefinedLocalizations) CFRelease(predefinedLocalizations); CFArrayRef userLanguages = _CFBundleCopyUserLanguages(); CFMutableArrayRef result = _CFBundleCopyPreferredLanguagesInList(localizationsInDirectory, devLang, userLanguages, true, url, NULL); diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Main.c b/CoreFoundation/PlugIn.subproj/CFBundle_Main.c index 3820903d7e..1e09e7dad3 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Main.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Main.c @@ -32,11 +32,11 @@ static void _CFBundleInitializeMainBundleInfoDictionaryAlreadyLocked(CFStringRef CFBundleGetInfoDictionary(_mainBundle); if (!_mainBundle->_infoDict || CFDictionaryGetCount(_mainBundle->_infoDict) == 0) { // if type 3 bundle and no Info.plist, treat as unbundled, since this gives too many false positives - if (_mainBundle->_version == 3) _mainBundle->_version = 4; - if (_mainBundle->_version == 0) { + if (_mainBundle->_version == _CFBundleVersionFlat) _mainBundle->_version = _CFBundleVersionNotABundle; + if (_mainBundle->_version == _CFBundleVersionOldStyleResources) { // if type 0 bundle and no Info.plist and not main executable for bundle, treat as unbundled, since this gives too many false positives CFStringRef executableName = _CFBundleCopyExecutableName(_mainBundle, NULL, NULL); - if (!executableName || !executablePath || !CFStringHasSuffix(executablePath, executableName)) _mainBundle->_version = 4; + if (!executableName || !executablePath || !CFStringHasSuffix(executablePath, executableName)) _mainBundle->_version = _CFBundleVersionNotABundle; if (executableName) CFRelease(executableName); } #if defined(BINARY_SUPPORT_DYLD) @@ -107,10 +107,10 @@ static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { // get cookie for already-loaded main bundle #if defined(BINARY_SUPPORT_DLFCN) if (!_mainBundle->_handleCookie) { -#if !defined(__OpenBSD__) - _mainBundle->_handleCookie = dlopen(NULL, RTLD_NOLOAD | RTLD_FIRST); +#if TARGET_OS_MAC + _mainBundle->_handleCookie = RTLD_MAIN_ONLY; #else - _mainBundle->_handleCookie = dlopen(NULL, RTLD_FIRST); + _mainBundle->_handleCookie = dlopen(NULL, 0); #endif #if LOG_BUNDLE_LOAD printf("main bundle %p getting handle %p\n", _mainBundle, _mainBundle->_handleCookie); @@ -129,7 +129,10 @@ static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { // This must be done after _isLoaded has been set, for security reasons (3624341). // It is safe to unlock and re-lock here because we don't really do anything under the lock after we are done. It is just re-locked to satisfy the 'already locked' contract. _CFMutexUnlock(&_mainBundleLock); - _CFBundleInitPlugIn(_mainBundle); + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(_mainBundle); + // CFBundleInitPlugIn will return false if the factory ID already exists. For the main bundle, if that happens, we just continue on since we cannot just return NULL. + _CFBundleInitPlugIn(_mainBundle, infoDict, NULL); + _CFPlugInHandleDynamicRegistration(_mainBundle); _CFMutexLock(&_mainBundleLock); } } @@ -159,7 +162,7 @@ CF_EXPORT CFURLRef _CFBundleCopyMainBundleExecutableURL(Boolean *looksLikeBundle } if (looksLikeBundle) { CFBundleRef mainBundle = _mainBundle; - if (mainBundle && (3 == mainBundle->_version || 4 == mainBundle->_version)) mainBundle = NULL; + if (mainBundle && (_CFBundleVersionFlat == mainBundle->_version || _CFBundleVersionNotABundle == mainBundle->_version)) mainBundle = NULL; *looksLikeBundle = (mainBundle ? true : false); } return executableURL; diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Resources.c b/CoreFoundation/PlugIn.subproj/CFBundle_Resources.c index e86d1e8dd8..d9482c2fd4 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Resources.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Resources.c @@ -26,6 +26,10 @@ #include #include +#if (!TARGET_OS_MAC && !TARGET_OS_BSD) || defined(__OpenBSD__) +#define strnstr(haystack, needle, size) strstr(haystack, needle) +#endif + #if TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD #include #if TARGET_OS_MAC || TARGET_OS_BSD @@ -96,12 +100,16 @@ CF_PRIVATE Boolean _CFIsResourceAtPath(CFStringRef path, Boolean *isDir) { } -static CFStringRef _CFBundleGetResourceDirForVersion(uint8_t version) { - if (1 == version) { +static CFStringRef _CFBundleGetResourceDirForVersion(_CFBundleVersion version) { + if (_CFBundleVersionOldStyleSupportFiles == version) { return _CFBundleSupportFilesDirectoryName1WithResources; - } else if (2 == version) { + } else if (_CFBundleVersionContentsResources == version) { return _CFBundleSupportFilesDirectoryName2WithResources; - } else if (0 == version) { + } else if (_CFBundleVersionWrappedContentsResources == version) { + return _CFBundleWrappedSupportFilesDirectoryName2WithResources; + } else if (_CFBundleVersionWrappedFlat == version) { + return _CFBundleWrapperLinkName; + } else if (_CFBundleVersionOldStyleResources == version) { return _CFBundleResourcesDirectoryName; } return CFSTR(""); @@ -173,7 +181,6 @@ CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleUR #pragma mark - -#if TARGET_OS_OSX || TARGET_OS_WIN32 CF_INLINE Boolean _CFBundleURLHasSubDir(CFURLRef url, CFStringRef subDirName) { Boolean isDir = false, result = false; CFURLRef dirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, subDirName, url); @@ -183,20 +190,20 @@ CF_INLINE Boolean _CFBundleURLHasSubDir(CFURLRef url, CFStringRef subDirName) { } return result; } -#endif -CF_PRIVATE uint8_t _CFBundleGetBundleVersionForURL(CFURLRef url) { +CF_PRIVATE _CFBundleVersion _CFBundleGetBundleVersionForURL(CFURLRef url) { // check for existence of "Resources" or "Contents" or "Support Files" // but check for the most likely one first // version 0: old-style "Resources" bundles // version 1: obsolete "Support Files" bundles // version 2: modern "Contents" bundles - // version 3: none of the above (see below) + // version 3: none of the above (see below) (flat) // version 4: not a bundle (for main bundle only) + // version 12: wrapper bundle of "Contents" bundle + // version 13: wrapper bundle of "Flat' bundle - CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); - CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); - CFRelease(absoluteURL); + CFURLRef bundleAbsoluteURL = CFURLCopyAbsoluteURL(url); + CFStringRef bundlePath = CFURLCopyFileSystemPath(bundleAbsoluteURL, PLATFORM_PATH_STYLE); Boolean hasFrameworkSuffix = CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/")); #if TARGET_OS_WIN32 @@ -210,17 +217,21 @@ CF_PRIVATE uint8_t _CFBundleGetBundleVersionForURL(CFURLRef url) { #define _CFBundleExecutablesDirectoryName CFSTR("Executables") #define _CFBundleNonLocalizedResourcesDirectoryName CFSTR("Non-localized Resources") */ - __block uint8_t localVersion = 3; + __block _CFBundleVersion localVersion = _CFBundleVersionFlat; CFIndex resourcesDirectoryLength = CFStringGetLength(_CFBundleResourcesDirectoryName); CFIndex contentsDirectoryLength = CFStringGetLength(_CFBundleSupportFilesDirectoryName2); CFIndex supportFilesDirectoryLength = CFStringGetLength(_CFBundleSupportFilesDirectoryName1); - + CFIndex wrapperLinkLength = CFStringGetLength(_CFBundleWrapperLinkName); + CFIndex wrapperDirLength = CFStringGetLength(_CFBundleWrapperDirectoryName); + __block Boolean foundResources = false; __block Boolean foundSupportFiles2 = false; __block Boolean foundSupportFiles1 = false; + __block Boolean foundAppWrapperLink = false; + __block Boolean foundAppWrapperDirectory = false; __block Boolean foundUnknown = false; - _CFIterateDirectory(directoryPath, false, NULL, ^Boolean (CFStringRef fileName, CFStringRef fileNameWithPrefix, uint8_t fileType) { + _CFIterateDirectory(bundlePath, false, NULL, ^Boolean (CFStringRef fileName, CFStringRef fileNameWithPrefix, uint8_t fileType) { // We're looking for a few different names, and also some info on if it's a directory or not. // We don't stop looking once we find one of the names. Otherwise we could run into the situation where we have both "Contents" and "Resources" in a framework, and we see Contents first but Resources is more important. if (fileType == DT_DIR || fileType == DT_LNK) { @@ -231,6 +242,10 @@ CF_PRIVATE uint8_t _CFBundleGetBundleVersionForURL(CFURLRef url) { foundSupportFiles2 = true; } else if (fileNameLen == supportFilesDirectoryLength && CFStringCompareWithOptions(fileName, _CFBundleSupportFilesDirectoryName1, CFRangeMake(0, supportFilesDirectoryLength), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { foundSupportFiles1 = true; + } else if (fileNameLen == wrapperDirLength && CFStringCompareWithOptions(fileName, _CFBundleWrapperDirectoryName, CFRangeMake(0, wrapperDirLength), kCFCompareEqualTo) == kCFCompareEqualTo) { + foundAppWrapperDirectory = true; + } else if (fileType == DT_LNK && fileNameLen == wrapperLinkLength && CFStringCompareWithOptions(fileName, _CFBundleWrapperLinkName, CFRangeMake(0, wrapperLinkLength), kCFCompareEqualTo) == kCFCompareEqualTo) { + foundAppWrapperLink = true; } } else if (fileType == DT_UNKNOWN) { // We'll have to do a more expensive check later; readdir couldn't tell us what the kind of a file was. This may mean that we are looking on a network directory. @@ -238,43 +253,173 @@ CF_PRIVATE uint8_t _CFBundleGetBundleVersionForURL(CFURLRef url) { } return true; }); - - // The order of these if statements is important - the Resources directory presence takes precedence over Contents, and so forth. - if (hasFrameworkSuffix) { + + // If we are on a network mount (or FAT volume), we need to do an additional check to look for the symlink and directory for wrapped bundles. readdir will give us DT_UNKNOWN. + if (foundUnknown && localVersion == _CFBundleVersionFlat) { + // Look for wrapper directory + if (_CFBundleURLHasSubDir(url, _CFBundleWrapperDirectoryName)) { + foundAppWrapperDirectory = true; + + // Look for wrapper link. Just verify something is there. We will verify it's linkiness later. + CFURLRef linkURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleWrapperLinkName, url); + Boolean isDir = false; + if (_CFIsResourceAtURL(linkURL, &isDir) && isDir) foundAppWrapperLink = true; + CFRelease(linkURL); + + if (foundAppWrapperDirectory && foundAppWrapperLink) { + // Reset the unknown flag + foundUnknown = false; + } + } + } + + if (foundAppWrapperDirectory && foundAppWrapperLink) { + // Default answer is flat until proven otherwise + localVersion = _CFBundleVersionFlat; + + // Descend into the wrapper to find out what version it is + CFURLRef linkURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, bundleAbsoluteURL, _CFBundleWrapperLinkName, true); + CFStringRef linkPath = CFURLCopyFileSystemPath(linkURL, PLATFORM_PATH_STYLE); + CFRelease(linkURL); + + __block Boolean foundWrappedSupportFiles2 = false; + _CFIterateDirectory(linkPath, false, NULL, ^Boolean (CFStringRef fileName, CFStringRef fileNameWithPrefix, uint8_t fileType) { + // Only contents and flat directories are supported as wrapped bundles + if (fileType == DT_DIR || fileType == DT_LNK) { + CFIndex fileNameLen = CFStringGetLength(fileName); + if (fileNameLen == contentsDirectoryLength && CFStringCompareWithOptions(fileName, _CFBundleSupportFilesDirectoryName2, CFRangeMake(0, contentsDirectoryLength), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + foundWrappedSupportFiles2 = true; + } + } + return true; + }); + + + + // 1. extension of bundle must match pointed-to + Boolean extensionCheckOk = false; + Boolean subdirectoryCheckOk = false; + + char linkPathCString[CFMaxPathSize]; + char linkContentsCString[CFMaxPathSize]; + char bundlePathCString[CFMaxPathSize]; + + if (CFStringGetFileSystemRepresentation(linkPath, linkPathCString, PATH_MAX) && + CFStringGetFileSystemRepresentation(bundlePath, bundlePathCString, PATH_MAX)) { + // Leave room for a null terminator + ssize_t len = readlink(linkPathCString, linkContentsCString, PATH_MAX - 1); + // Make sure this is not an absolute link but a relative one + if (len < 2 || (len > 1 && linkContentsCString[0] == '/')) { + os_log_error(_CFBundleResourceLogger(), "`WrappedBundle` link too short or pointed outside bundle at %{public}@", url); + } else { + // readlink does not null terminate, so we manually do it here + // CFStringGetFileSystemRepresentation does null terminate + linkContentsCString[len] = 0; + + const char *extensionOfWrapped = NULL; + const char *extensionOfWrapper = NULL; + + const char *lastPeriodInWrapped = strrchr(linkContentsCString, '.'); + if (lastPeriodInWrapped) { + extensionOfWrapped = lastPeriodInWrapped + 1; // advance past the . + } + + const char *lastPeriodInWrapper = strrchr(bundlePathCString, '.'); + if (lastPeriodInWrapper) { + extensionOfWrapper = lastPeriodInWrapper + 1; // advance past the . + } + + if (extensionOfWrapper && extensionOfWrapped) { + if (strcmp(extensionOfWrapped, extensionOfWrapper) == 0) { + extensionCheckOk = true; + } else { + os_log_error(_CFBundleResourceLogger(), "Extensions of wrapped bundles did not match at %{public}@", url); + } + } else if (!extensionOfWrapper && !extensionOfWrapped) { + // If they both have no extensions, that is allowed + extensionCheckOk = true; + } else { + // One doesn't have an extension + os_log_error(_CFBundleResourceLogger(), "Extensions of wrapped bundles did not match (one missing) at %{public}@", url); + } + + // 2. pointed-to must not traverse outside bundle + // We check this by making sure that the path of the wrapper bundle is found at the start of the resolved symlink of the wrapped bundle. Also check for links to the same directory. + int resolvedWrappedBundleFd = open(linkPathCString, O_RDONLY); + int resolvedBundlePathFd = open(bundlePathCString, O_RDONLY); + + if (resolvedWrappedBundleFd > 0 && resolvedBundlePathFd > 0) { + char resolvedWrappedBundlePath[PATH_MAX]; + char resolvedBundlePath[PATH_MAX]; + + // Get the path for the wrapped bundle and the wrapper bundle here + if (_CFGetPathFromFileDescriptor(resolvedWrappedBundleFd, resolvedWrappedBundlePath) && + _CFGetPathFromFileDescriptor(resolvedBundlePathFd, resolvedBundlePath) && + strncmp(resolvedWrappedBundlePath, resolvedBundlePath, PATH_MAX) != 0 && + strnstr(resolvedWrappedBundlePath, resolvedBundlePath, PATH_MAX) == resolvedWrappedBundlePath) + { + subdirectoryCheckOk = true; + } + + } + + if (resolvedWrappedBundleFd > 0) close(resolvedWrappedBundleFd); + if (resolvedBundlePathFd > 0) close(resolvedBundlePathFd); + + if (!subdirectoryCheckOk) { + os_log_error(_CFBundleResourceLogger(), "`WrappedBundle` link invalid or pointed outside bundle at %{public}@", url); + } + } + } + + CFRelease(linkPath); + + if (extensionCheckOk && subdirectoryCheckOk) { + if (foundWrappedSupportFiles2) { + localVersion = _CFBundleVersionWrappedContentsResources; + } else { + localVersion = _CFBundleVersionWrappedFlat; + } + } + + } else if (hasFrameworkSuffix) { + // The order of these if statements is important - the Resources directory presence takes precedence over Contents, and so forth. The order for frameworks is different than other bundles for compatibility reasons. if (foundResources) { - localVersion = 0; + localVersion = _CFBundleVersionOldStyleResources; } else if (foundSupportFiles2) { - localVersion = 2; + localVersion = _CFBundleVersionContentsResources; } else if (foundSupportFiles1) { - localVersion = 1; + localVersion = _CFBundleVersionOldStyleSupportFiles; } } else { + // The order of these if statements is important - the Resources directory presence takes precedence over Contents, and so forth. if (foundSupportFiles2) { - localVersion = 2; + localVersion = _CFBundleVersionContentsResources; } else if (foundResources) { - localVersion = 0; + localVersion = _CFBundleVersionOldStyleResources; } else if (foundSupportFiles1) { - localVersion = 1; + localVersion = _CFBundleVersionOldStyleSupportFiles; } } #if TARGET_OS_OSX || TARGET_OS_WIN32 // Do a more substantial check for the subdirectories that make up version 0/1/2 bundles. These are sometimes symlinks (like in Frameworks) and they would have been missed by our check above. // n.b. that the readdir above may return DT_UNKNOWN, for example, when the directory is on a network mount. - if (foundUnknown && localVersion == 3) { + if (foundUnknown && localVersion == _CFBundleVersionFlat) { if (hasFrameworkSuffix) { - if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0; - else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2; - else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1; + if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = _CFBundleVersionOldStyleResources; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = _CFBundleVersionContentsResources; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = _CFBundleVersionOldStyleSupportFiles; } else { - if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2; - else if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0; - else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1; + if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = _CFBundleVersionContentsResources; + else if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = _CFBundleVersionOldStyleResources; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = _CFBundleVersionOldStyleSupportFiles; } } #endif - CFRelease(directoryPath); + CFRelease(bundleAbsoluteURL); + CFRelease(bundlePath); return localVersion; } @@ -717,7 +862,7 @@ static void _CFBundleFindResourcesWithPredicate(CFMutableArrayRef interResult, C free(values); } -static CFTypeRef _copyResourceURLsFromBundle(CFBundleRef bundle, CFURLRef bundleURL, CFArrayRef bundleURLLanguages, CFStringRef resourcesDirectory, CFStringRef subDir, CFStringRef key, CFStringRef lproj, Boolean returnArray, Boolean localized, uint8_t bundleVersion, Boolean (^predicate)(CFStringRef filename, Boolean *stop)) +static CFTypeRef _copyResourceURLsFromBundle(CFBundleRef bundle, CFURLRef bundleURL, CFArrayRef bundleURLLanguages, CFStringRef resourcesDirectory, CFStringRef subDir, CFStringRef key, CFStringRef lproj, Boolean returnArray, Boolean localized, _CFBundleVersion bundleVersion, Boolean (^predicate)(CFStringRef filename, Boolean *stop)) { Boolean stop = false; // for predicate CFMutableArrayRef interResult = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); @@ -727,7 +872,7 @@ static CFTypeRef _copyResourceURLsFromBundle(CFBundleRef bundle, CFURLRef bundle CFDictionaryRef subTable = NULL; CFMutableStringRef path = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, resourcesDirectory); - if (1 == bundleVersion) { + if (_CFBundleVersionOldStyleSupportFiles == bundleVersion) { CFIndex savedPathLength = CFStringGetLength(path); // add the non-localized resource dir _CFAppendPathComponent2(path, _CFBundleNonLocalizedResourcesDirectoryName); @@ -909,8 +1054,9 @@ static CFTypeRef _copyResourceURLsFromBundle(CFBundleRef bundle, CFURLRef bundle // Research shows that by far the most common scenario is to pass in a bundle object, a resource name, and a resource type, using the default localization. // It is probably the case that more than a few resources will be looked up, making the cost of a readdir less than repeated stats. But it is a relative waste of memory to create strings for every file name in the bundle, especially since those are not what are returned to the caller (URLs are). So, an idea: cache the existence of the most common file names (Info.plist, en.lproj, etc) instead of creating entries for them. If other resources are requested, then go ahead and do the readdir and cache the rest of the file names. // Another idea: if you want caching, you should create a bundle object. Otherwise we'll happily readdir each time. -CF_EXPORT CFTypeRef _Nullable _CFBundleCopyFindResources(CFBundleRef bundle, CFURLRef bundleURL, CFArrayRef _unused_pass_null_, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subPath, CFStringRef lproj, Boolean returnArray, Boolean localized, Boolean (^predicate)(CFStringRef filename, Boolean *stop)) +CF_EXPORT CFTypeRef _Nullable _CFBundleCopyFindResources(CFBundleRef _Nullable bundle, CFURLRef _Nullable bundleURL, CFArrayRef _Nullable _unused_pass_null_, CFStringRef _Nullable resourceName, CFStringRef _Nullable resourceType, CFStringRef _Nullable subPath, CFStringRef _Nullable lproj, Boolean returnArray, Boolean localized, Boolean (^_Nullable predicate)(CFStringRef filename, Boolean *stop)) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFBundle, bundle); CFTypeRef returnValue = NULL; if ( @@ -1013,7 +1159,7 @@ CF_EXPORT CFTypeRef _Nullable _CFBundleCopyFindResources(CFBundleRef bundle, CFU _CFAppendPathComponent2((CFMutableStringRef)realSubdirectory, subPathFromResourceName); } - uint8_t bundleVersion = bundle ? _CFBundleLayoutVersion(bundle) : 0; + _CFBundleVersion bundleVersion = bundle ? _CFBundleLayoutVersion(bundle) : _CFBundleVersionOldStyleResources; CFArrayRef bundleURLLanguages = NULL; if (bundleURL) { bundleURLLanguages = _CFBundleCopyLanguageSearchListInDirectory(bundleURL, &bundleVersion); @@ -1024,7 +1170,8 @@ CF_EXPORT CFTypeRef _Nullable _CFBundleCopyFindResources(CFBundleRef bundle, CFU // if returnArray is true then this function will always return a CFArrayRef, even if it's empty returnValue = _copyResourceURLsFromBundle(bundle, bundleURL, bundleURLLanguages, resDir, realSubdirectory, key, lproj, returnArray, localized, bundleVersion, predicate); - if ((!returnValue || (CFGetTypeID(returnValue) == CFArrayGetTypeID() && CFArrayGetCount((CFArrayRef)returnValue) == 0)) && (0 == bundleVersion || 2 == bundleVersion)) { + // This is a rarely taken path to add additional resources for old-style bundles and a special case (Spotlight) + if ((!returnValue || (CFGetTypeID(returnValue) == CFArrayGetTypeID() && CFArrayGetCount((CFArrayRef)returnValue) == 0)) && (_CFBundleVersionOldStyleResources == bundleVersion || _CFBundleVersionContentsResources == bundleVersion)) { CFStringRef bundlePath = NULL; if (bundle) { bundlePath = bundle->_bundleBasePath; @@ -1034,16 +1181,16 @@ CF_EXPORT CFTypeRef _Nullable _CFBundleCopyFindResources(CFBundleRef bundle, CFU bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); CFRelease(absoluteURL); } - if ((0 == bundleVersion) || CFEqual(CFSTR("/Library/Spotlight"), bundlePath)){ + if ((_CFBundleVersionOldStyleResources == bundleVersion) || CFEqual(CFSTR("/Library/Spotlight"), bundlePath)){ if (returnValue) CFRelease(returnValue); - if ((bundleVersion == 0 && realSubdirectory && CFEqual(realSubdirectory, CFSTR("Resources"))) || (bundleVersion == 2 && realSubdirectory && CFEqual(realSubdirectory, CFSTR("Contents/Resources")))) { + if ((bundleVersion == _CFBundleVersionOldStyleResources && realSubdirectory && CFEqual(realSubdirectory, CFSTR("Resources"))) || (bundleVersion == _CFBundleVersionContentsResources && realSubdirectory && CFEqual(realSubdirectory, CFSTR("Contents/Resources")))) { if (realSubdirectory) CFRelease(realSubdirectory); realSubdirectory = CFSTR(""); - } else if (bundleVersion == 0 && realSubdirectory && CFStringGetLength(realSubdirectory) > 10 && CFStringHasPrefix(realSubdirectory, CFSTR("Resources/"))) { + } else if (bundleVersion == _CFBundleVersionOldStyleResources && realSubdirectory && CFStringGetLength(realSubdirectory) > 10 && CFStringHasPrefix(realSubdirectory, CFSTR("Resources/"))) { CFStringRef tmpRealSubdirectory = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, realSubdirectory, CFRangeMake(10, CFStringGetLength(realSubdirectory) - 10)); if (realSubdirectory) CFRelease(realSubdirectory); realSubdirectory = tmpRealSubdirectory; - } else if (bundleVersion == 2 && realSubdirectory && CFStringGetLength(realSubdirectory) > 19 && CFStringHasPrefix(realSubdirectory, CFSTR("Contents/Resources/"))) { + } else if (bundleVersion == _CFBundleVersionContentsResources && realSubdirectory && CFStringGetLength(realSubdirectory) > 19 && CFStringHasPrefix(realSubdirectory, CFSTR("Contents/Resources/"))) { CFStringRef tmpRealSubdirectory = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, realSubdirectory, CFRangeMake(19, CFStringGetLength(realSubdirectory) - 19)); if (realSubdirectory) CFRelease(realSubdirectory); realSubdirectory = tmpRealSubdirectory; diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Strings.c b/CoreFoundation/PlugIn.subproj/CFBundle_Strings.c index fd72f09378..3f3f1c2268 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Strings.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Strings.c @@ -31,7 +31,8 @@ CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRe } -static CFStringRef _copyStringFromTable(CFBundleRef bundle, CFStringRef tableName, CFStringRef key, CFStringRef localizationName) { +/* outActualTableFile is the URL to a localization table file we're getting strings from. It may be set to NULL on return to mean that we have pulled this from the cache of the preferred language, which is fine since we want this URL to determine which localization was picked. */ +static CFStringRef _copyStringFromTable(CFBundleRef bundle, CFStringRef tableName, CFStringRef key, CFStringRef localizationName, Boolean preventMarkdownParsing, CFURLRef *outActualLocalizationFile) { // Check the cache first. If it's not there, populate the cache and check again. __CFLock(&bundle->_lock); @@ -44,6 +45,10 @@ static CFStringRef _copyStringFromTable(CFBundleRef bundle, CFStringRef tableNam CFRetain(result); } __CFUnlock(&bundle->_lock); + + if (outActualLocalizationFile) { + *outActualLocalizationFile = NULL; // Preferred localization. + } return result; } } @@ -63,10 +68,9 @@ static CFStringRef _copyStringFromTable(CFBundleRef bundle, CFStringRef tableNam stringsTableURL = CFBundleCopyResourceURL(bundle, tableName, _CFBundleStringTableType, NULL); stringsDictTableURL = CFBundleCopyResourceURL(bundle, tableName, _CFBundleStringDictTableType, NULL); } - - + // Next, look on disk for the regular strings file. - if (!stringsTable && stringsTableURL) { + if (stringsTableURL) { CFDataRef tableData = _CFDataCreateFromURL(stringsTableURL, NULL); if (tableData) { CFErrorRef error = NULL; @@ -82,8 +86,7 @@ static CFStringRef _copyStringFromTable(CFBundleRef bundle, CFStringRef tableNam CFRelease(error); error = NULL; } - } - + } } // Check for a .stringsdict file. @@ -124,10 +127,18 @@ static CFStringRef _copyStringFromTable(CFBundleRef bundle, CFStringRef tableNam if (stringsTable) CFRelease(stringsTable); // The new strings table is the result of all the transforms above. stringsTable = mutableStringsDictTable; + + if (outActualLocalizationFile) { + *outActualLocalizationFile = CFRetain(stringsDictTableURL); + } } } } + if (outActualLocalizationFile && !*outActualLocalizationFile && stringsTableURL) { + *outActualLocalizationFile = CFRetain(stringsTableURL); + } + if (stringsTableURL) CFRelease(stringsTableURL); if (stringsDictTableURL) CFRelease(stringsDictTableURL); @@ -162,7 +173,9 @@ static CFStringRef _copyStringFromTable(CFBundleRef bundle, CFStringRef tableNam return result; } -CF_EXPORT CFStringRef CFBundleCopyLocalizedStringForLocalization(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName, CFStringRef localizationName) { +CF_EXPORT CFStringRef _CFBundleCopyLocalizedStringForLocalizationTableURLAndMarkdownOption(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName, CFStringRef localizationName, Boolean preventMarkdownParsing, CFURLRef *outActualTableURL) { + + CF_ASSERT_TYPE(_kCFRuntimeIDCFBundle, bundle); if (!key) { return (value ? (CFStringRef)CFRetain(value) : (CFStringRef)CFRetain(CFSTR(""))); } // Make sure to check the mixed localizations key early -- if the main bundle has not yet been cached, then we need to create the cache of the Info.plist before we start asking for resources (11172381) @@ -170,7 +183,8 @@ CF_EXPORT CFStringRef CFBundleCopyLocalizedStringForLocalization(CFBundleRef bun if (!tableName || CFEqual(tableName, CFSTR(""))) tableName = _CFBundleDefaultStringTableName; - CFStringRef result = _copyStringFromTable(bundle, tableName, key, localizationName); + CFURLRef actualTableURL = NULL; + CFStringRef result = _copyStringFromTable(bundle, tableName, key, localizationName, preventMarkdownParsing, &actualTableURL); if (!result) { if (!value) { @@ -189,7 +203,20 @@ CF_EXPORT CFStringRef CFBundleCopyLocalizedStringForLocalization(CFBundleRef bun result = capitalizedResult; } } + if (outActualTableURL) { + *outActualTableURL = actualTableURL; + } else if (actualTableURL) { + CFRelease(actualTableURL); + } + os_log_debug(_CFBundleLocalizedStringLogger(), "Bundle: %{private}@, key: %{public}@, value: %{public}@, table: %{public}@, localizationName: %{public}@, result: %{public}@", bundle, key, value, tableName, localizationName, result); return result; } +CF_EXPORT CFStringRef _CFBundleCopyLocalizedStringForLocalizationAndTableURL(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName, CFStringRef localizationName, CFURLRef *outActualTableURL) { + return _CFBundleCopyLocalizedStringForLocalizationTableURLAndMarkdownOption(bundle, key, value, tableName, localizationName, false, outActualTableURL); +} + +CF_EXPORT CFStringRef CFBundleCopyLocalizedStringForLocalization(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName, CFStringRef localizationName) { + return _CFBundleCopyLocalizedStringForLocalizationTableURLAndMarkdownOption(bundle, key, value, tableName, localizationName, false, NULL); +} diff --git a/CoreFoundation/PlugIn.subproj/CFPlugIn.c b/CoreFoundation/PlugIn.subproj/CFPlugIn.c index 649e351bf5..fe3776c847 100644 --- a/CoreFoundation/PlugIn.subproj/CFPlugIn.c +++ b/CoreFoundation/PlugIn.subproj/CFPlugIn.c @@ -27,8 +27,9 @@ static CFPlugInRef _CFPFactoryCopyPlugInLocked(_CFPFactoryRef factory); static void _CFPlugInRegisterFactoryFunctionByNameLocked(CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef functionName); static void _CFPlugInRegisterPlugInTypeLocked(CFUUIDRef factoryID, CFUUIDRef typeID); -static void *_CFPFactoryCreateInstanceLocked(CFAllocatorRef allocator, _CFPFactoryRef factory, CFUUIDRef typeID); static void _CFPFactoryDisableLocked(_CFPFactoryRef factory); +static void *__CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(CFPlugInFactoryFunction, CFAllocatorRef, CFUUIDRef) __attribute__((noinline)); + static void _CFPFactoryAddTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID); static void _CFPFactoryRemoveTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID); @@ -40,6 +41,8 @@ static void _CFPFactoryRemoveInstanceLocked(_CFPFactoryRef factory); static void _CFPlugInAddPlugInInstanceLocked(CFPlugInRef plugIn); static void _CFPlugInRemovePlugInInstanceLocked(CFPlugInRef plugIn); +static void _CFPlugInIncrementUnloadPreventionLocked(CFPlugInRef plugIn); +static void _CFPlugInDecrementUnloadPreventionLocked(CFPlugInRef plugIn); static void _CFPlugInAddFactoryLocked(CFPlugInRef plugIn, _CFPFactoryRef factory); static void _CFPlugInRemoveFactoryLocked(CFPlugInRef plugIn, _CFPFactoryRef factory); @@ -91,29 +94,10 @@ struct __CFPFactory { // 3. The enabled flag in each factory instance // 4. The plugInData inside each bundle instance (except isPlugIn, which is constant after init) // In order to synchronize all of this, there is one global lock for all of it. -#if __has_include() -static os_unfair_recursive_lock CFPlugInGlobalDataLock = OS_UNFAIR_RECURSIVE_LOCK_INIT; -#define _CFPlugInGlobalDataLockAcquire() os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION) -#define _CFPlugInGlobalDataLockRelease() os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock) -#else -static _CFRecursiveMutex CFPlugInGlobalDataLock; - -static void _CFPlugInGlobalDataLockAcquire() { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _CFRecursiveMutexCreate(&CFPlugInGlobalDataLock); - }); - _CFRecursiveMutexLock(&CFPlugInGlobalDataLock); -} - -static void _CFPlugInGlobalDataLockRelease() { - _CFRecursiveMutexUnlock(&CFPlugInGlobalDataLock); -} - -#endif - +os_unfair_recursive_lock CFPlugInGlobalDataLock = OS_UNFAIR_RECURSIVE_LOCK_INIT; static CFMutableDictionaryRef _factoriesByFactoryID = NULL; /* Value is _CFPFactoryRef */ static CFMutableDictionaryRef _factoriesByTypeID = NULL; /* Value is array of _CFPFactoryRef */ +static CFMutableSetRef _plugInsToUnload = NULL; // MARK: - Plugin @@ -127,23 +111,50 @@ static os_log_t _CFBundlePluginLogger(void) { } CF_EXPORT void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryID, CFUUIDRef typeID) { - _CFPlugInGlobalDataLockAcquire(); + void *result = NULL; + CFPlugInFactoryFunction f = NULL; + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true); - void *result = NULL; if (!factory) { os_log_error(_CFBundlePluginLogger(), "Cannot find factory %{public}@", factoryID); } else { if (!_CFPFactorySupportsTypeLocked(factory, typeID)) { os_log_error(_CFBundlePluginLogger(), "Factory %{public}@ does not support type %{public}@", factoryID, typeID); + } else if (factory->_enabled) { + if (!factory->_func) { + factory->_func = (CFPlugInFactoryFunction)CFBundleGetFunctionPointerForName(factory->_plugIn, factory->_funcName); + + if (!factory->_func) { + os_log_error(_CFBundlePluginLogger(), "Cannot find function pointer %{public}@ for factory %{public}@ in %{public}@", factory->_funcName, factory->_uuid, factory->_plugIn); + } + } + if (factory->_func) { + f = factory->_func; + + // Not every factory comes from a plugin, but if it does, we must prevent unload of the plugin so that the function pointer 'f' remains valid, even if factory->_func is cleared. + if (factory->_plugIn) { + _CFPlugInIncrementUnloadPreventionLocked(factory->_plugIn); + } + } } else { - result = _CFPFactoryCreateInstanceLocked(allocator, factory, typeID); + os_log_debug(_CFBundlePluginLogger(), "Attempted to create instance, but factory %{public}@ is disabled", factory->_uuid); } } - - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); + + // Call out to the factory function outside of the lock + if (f) { + result = __CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(f, allocator, typeID); + os_log_debug(_CFBundlePluginLogger(), "Created instance of plugin for factory %{public}@ type %{public}@", factoryID, typeID); + + os_unfair_recursive_lock_lock(&CFPlugInGlobalDataLock); + if (factory->_plugIn) { + _CFPlugInDecrementUnloadPreventionLocked(factory->_plugIn); + } + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); + } - os_log_debug(_CFBundlePluginLogger(), "Created instance of plugin for factory %{public}@ type %{public}@", factoryID, typeID); return result; } @@ -155,14 +166,14 @@ CF_EXPORT Boolean CFPlugInRegisterFactoryFunction(CFUUIDRef factoryID, CFPlugInF // Create factories without plugIns from default allocator // MF:!!! Should probably check that this worked, and maybe do some pre-checking to see if it already exists - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPFactoryRef factory = _CFPFactoryCommonCreateLocked(kCFAllocatorSystemDefault, factoryID); factory->_func = func; factory->_plugIn = NULL; factory->_funcName = NULL; - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return true; } @@ -170,9 +181,9 @@ CF_EXPORT Boolean CFPlugInRegisterFactoryFunction(CFUUIDRef factoryID, CFPlugInF CF_EXPORT Boolean CFPlugInRegisterFactoryFunctionByName(CFUUIDRef factoryID, CFPlugInRef plugIn, CFStringRef functionName) { // Create factories with plugIns from plugIn's allocator // MF:!!! Should probably check that this worked, and maybe do some pre-checking to see if it already exists - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPlugInRegisterFactoryFunctionByNameLocked(factoryID, plugIn, functionName); - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return true; } @@ -187,7 +198,7 @@ static void _CFPlugInRegisterFactoryFunctionByNameLocked(CFUUIDRef factoryID, CF CF_EXPORT Boolean CFPlugInUnregisterFactory(CFUUIDRef factoryID) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true); @@ -197,15 +208,15 @@ CF_EXPORT Boolean CFPlugInUnregisterFactory(CFUUIDRef factoryID) { } else { _CFPFactoryDisableLocked(factory); } - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return true; } CF_EXPORT Boolean CFPlugInRegisterPlugInType(CFUUIDRef factoryID, CFUUIDRef typeID) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPlugInRegisterPlugInTypeLocked(factoryID, typeID); - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return true; } @@ -223,7 +234,7 @@ static void _CFPlugInRegisterPlugInTypeLocked(CFUUIDRef factoryID, CFUUIDRef typ CF_EXPORT Boolean CFPlugInUnregisterPlugInType(CFUUIDRef factoryID, CFUUIDRef typeID) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true); @@ -234,7 +245,7 @@ CF_EXPORT Boolean CFPlugInUnregisterPlugInType(CFUUIDRef factoryID, CFUUIDRef ty _CFPFactoryRemoveTypeLocked(factory, typeID); } - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return true; } @@ -245,7 +256,7 @@ CF_EXPORT Boolean CFPlugInUnregisterPlugInType(CFUUIDRef factoryID, CFUUIDRef ty /* This means that an instance must keep track of the CFUUIDRef of the factory that created it so it can unregister when it goes away. */ CF_EXPORT void CFPlugInAddInstanceForFactory(CFUUIDRef factoryID) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true); @@ -254,13 +265,14 @@ CF_EXPORT void CFPlugInAddInstanceForFactory(CFUUIDRef factoryID) { os_log_error(_CFBundlePluginLogger(), "AddInstanceForFactory: No factory registered for id %{public}@", factoryID); } else { _CFPFactoryAddInstanceLocked(factory); + os_log_debug(_CFBundlePluginLogger(), "AddInstanceForFactory: Added instance on %p for %{public}@", factory, factoryID); } - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); } CF_EXPORT void CFPlugInRemoveInstanceForFactory(CFUUIDRef factoryID) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPFactoryRef factory = _CFPFactoryFindLocked(factoryID, true); @@ -269,13 +281,103 @@ CF_EXPORT void CFPlugInRemoveInstanceForFactory(CFUUIDRef factoryID) { os_log_error(_CFBundlePluginLogger(), "RemoveInstanceForFactory: No factory registered for id %{public}@", factoryID); } else { _CFPFactoryRemoveInstanceLocked(factory); + os_log_debug(_CFBundlePluginLogger(), "RemoveInstanceForFactory: Removed instance on %p for %{public}@", factory, factoryID); + } + + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); +} + +// MARK: Plugin - Unloading + +static void _CFPlugInScheduleForUnloading(CFBundleRef bundle) { + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); + if (!_plugInsToUnload) { + CFSetCallBacks nonRetainingCallbacks = kCFTypeSetCallBacks; + nonRetainingCallbacks.retain = NULL; + nonRetainingCallbacks.release = NULL; + _plugInsToUnload = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingCallbacks); + } + CFSetAddValue(_plugInsToUnload, bundle); + os_log_debug(_CFBundlePluginLogger(), "PlugIn %{public}@ is now scheduled for unloading", bundle); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); +} + +CF_PRIVATE void _CFPlugInUnscheduleForUnloading(CFBundleRef bundle) { + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); + if (_plugInsToUnload) CFSetRemoveValue(_plugInsToUnload, bundle); + os_log_debug(_CFBundlePluginLogger(), "PlugIn %{public}@ is now unscheduled for unloading", bundle); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); +} + +CF_PRIVATE void _CFPlugInUnloadScheduledPlugIns(void) { + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); + if (_plugInsToUnload) { + CFIndex i, c = CFSetGetCount(_plugInsToUnload); + if (c > 0) { + CFBundleRef *unloadThese = (CFBundleRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFBundleRef) * c, 0); + CFSetGetValues(_plugInsToUnload, (const void **)unloadThese); + for (i = 0; i < c; i++) { + // This will cause them to be removed from the set. (Which is why we copied all the values out of the set up front.) + CFBundleRef unloadMe = unloadThese[i]; + + // If its unloadPreventionCount is > 0 then leave it in the set and unload it the next time someone asks + if (__CFBundleGetPlugInData(unloadMe)->_unloadPreventionCount == 0) { + os_log_debug(_CFBundlePluginLogger(), "PlugIn %{public}@ is about to be unloaded", unloadMe); + _CFBundleUnloadExecutable(unloadMe, true); + } + } + CFAllocatorDeallocate(kCFAllocatorSystemDefault, unloadThese); + } } - - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); } // MARK: Plugin - Internals +static void _searchForDummyUUID(const void *key, const void *val, void *context) { + Boolean *found = (Boolean *)context; + if (*found) { + // No need to continue searching here + return; + } + + CFStringRef factoryIDStr = (CFStringRef)key; + if (CFGetTypeID(factoryIDStr) != CFStringGetTypeID()) { + // Factory ID is not a string, skip this entry + return; + } + + if (CFStringCompare(factoryIDStr, CFSTR("00000000-0000-0000-0000-000000000000"), 0) == kCFCompareEqualTo) { + // This is a dummy UUID. Don't count this as a plugin if it uses the dummy function name too. + CFStringRef factoryFunctionStr = (CFStringRef)val; + if (factoryFunctionStr && CFGetTypeID(factoryFunctionStr) == CFStringGetTypeID()) { + if (CFStringCompare(factoryFunctionStr, CFSTR("MyFactoryFunction"), 0) == kCFCompareEqualTo) { + *found = true; + } + } + } +} + +static void _searchForExistingFactoryLocked(const void *key, const void *val, void *context) { + CFBundleRef *found = (CFBundleRef *)context; + if (*found) { + // No need to continue searching here + return; + } + + CFStringRef factoryIDStr = (CFStringRef)key; + CFUUIDRef factoryID = (CFGetTypeID(factoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault, factoryIDStr) : NULL; + if (!factoryID) factoryID = (CFUUIDRef)CFRetain(factoryIDStr); + + // Match any factory, not just enabled ones + _CFPFactoryRef existing = _CFPFactoryFindLocked(factoryID, false); + if (existing) { + *found = (CFBundleRef)CFRetain(existing->_plugIn); + } + + if (factoryID) CFRelease(factoryID); +} + static void _registerFactoryLocked(const void *key, const void *val, void *context) { CFStringRef factoryIDStr = (CFStringRef)key; CFStringRef factoryFuncStr = (CFStringRef)val; @@ -317,37 +419,61 @@ static void _registerTypeLocked(const void *key, const void *val, void *context) if (typeID) CFRelease(typeID); } -CF_PRIVATE void _CFBundleInitPlugIn(CFBundleRef bundle) { +// Returns false if we found another plugin with the same factory ID. +// Important: Do not call out to user code from here, as it is called with the global bundle lock taken. The lock ordering must be: +// CFBundleGlobalDataLock -> CFPlugInGlobalDataLock +// PlugIn lock is recursive but the bundle lock is not +CF_PRIVATE Boolean _CFBundleInitPlugIn(CFBundleRef bundle, CFDictionaryRef infoDict, CFBundleRef *existingPlugIn) { CFArrayCallBacks _pluginFactoryArrayCallbacks = {0, NULL, NULL, NULL, NULL}; Boolean doDynamicReg = false; - CFDictionaryRef infoDict; CFDictionaryRef factoryDict; CFDictionaryRef typeDict; CFStringRef tempStr; - infoDict = CFBundleGetInfoDictionary(bundle); - if (!infoDict) return; + if (!infoDict) return true; factoryDict = (CFDictionaryRef)CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey); if (factoryDict && CFGetTypeID(factoryDict) != CFDictionaryGetTypeID()) factoryDict = NULL; tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey); if (tempStr && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) doDynamicReg = true; - if (!factoryDict && !doDynamicReg) return; // This is not a plug-in. + if (!factoryDict && !doDynamicReg) return true; // This is not a plug-in. + + // Search for placeholder UUIDs (all zero) + Boolean foundDummy = false; + if (factoryDict) CFDictionaryApplyFunction(factoryDict, _searchForDummyUUID, &foundDummy); + if (foundDummy) { + // Not a plugin. This combination seems to be part of a template, and is often left in Info.plists without much consideration. + os_log_debug(_CFBundlePluginLogger(), "Bundle %{public}@ contains a factory UUID of 00000000-0000-0000-0000-000000000000 with function 'MyFactoryFunction'. This bundle is not a valid plugin.", bundle); + return true; + } - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); if (__CFBundleGetPlugInData(bundle)->_registeredFactory) { // We already registered - don't do it again - _CFPlugInGlobalDataLockRelease(); - return; + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); + return true; } + // Look for existing plugins with this factory ID + CFBundleRef found = NULL; + if (factoryDict) CFDictionaryApplyFunction(factoryDict, _searchForExistingFactoryLocked, &found); + if (found) { + if (existingPlugIn) { + *existingPlugIn = found; + } + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); + return false; + } + /* loadOnDemand is true by default if the plugIn does not do dynamic registration. It is false, by default if it does do dynamic registration. The dynamic register function can set this. */ __CFBundleGetPlugInData(bundle)->_isPlugIn = true; __CFBundleGetPlugInData(bundle)->_loadOnDemand = true; __CFBundleGetPlugInData(bundle)->_isDoingDynamicRegistration = false; - __CFBundleGetPlugInData(bundle)->_needsDynamicRegistration = false; + // It is the responsibility of the caller of this function to call _CFPlugInHandleDynamicRegistration once we are out of critical sections + __CFBundleGetPlugInData(bundle)->_needsDynamicRegistration = doDynamicReg; __CFBundleGetPlugInData(bundle)->_instanceCount = 0; + __CFBundleGetPlugInData(bundle)->_unloadPreventionCount = 0; __CFBundleGetPlugInData(bundle)->_registeredFactory = true; __CFBundleGetPlugInData(bundle)->_factories = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &_pluginFactoryArrayCallbacks); @@ -360,13 +486,9 @@ CF_PRIVATE void _CFBundleInitPlugIn(CFBundleRef bundle) { if (typeDict && CFGetTypeID(typeDict) != CFDictionaryGetTypeID()) typeDict = NULL; if (typeDict) CFDictionaryApplyFunction(typeDict, _registerTypeLocked, bundle); - _CFPlugInGlobalDataLockRelease(); - - /* Now set key for dynamic registration if necessary */ - if (doDynamicReg) { - __CFBundleGetPlugInData(bundle)->_needsDynamicRegistration = true; - if (CFBundleIsExecutableLoaded(bundle)) _CFBundlePlugInLoaded(bundle); - } + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); + + return true; } static void __CFPLUGIN_IS_CALLING_OUT_TO_A_DYNAMIC_REGISTRATION_FUNCTION__(CFPlugInDynamicRegisterFunction f, CFBundleRef bundle) __attribute__((noinline)); @@ -376,53 +498,54 @@ static void __CFPLUGIN_IS_CALLING_OUT_TO_A_DYNAMIC_REGISTRATION_FUNCTION__(CFPlu __asm __volatile__(""); // thwart tail-call optimization } -CF_PRIVATE void _CFBundlePlugInLoaded(CFBundleRef bundle) { +CF_PRIVATE void _CFPlugInHandleDynamicRegistration(CFBundleRef bundle) { _CFPlugInData *plugIn = __CFBundleGetPlugInData(bundle); - if (!plugIn->_isPlugIn || !CFBundleIsExecutableLoaded(bundle)) { + + // In order to proceed, it must be a plugin, loaded, and need dynamic registration + if (!(plugIn->_isPlugIn && CFBundleIsExecutableLoaded(bundle) && plugIn->_needsDynamicRegistration)) { return; } - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); if (plugIn->_isDoingDynamicRegistration) { - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return; } - if (plugIn->_needsDynamicRegistration) { - plugIn->_needsDynamicRegistration = false; - CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); - CFStringRef tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegisterFunctionKey); - if (!tempStr || CFGetTypeID(tempStr) != CFStringGetTypeID() || CFStringGetLength(tempStr) <= 0) tempStr = CFSTR("CFPlugInDynamicRegister"); - plugIn->_loadOnDemand = false; - - plugIn->_isDoingDynamicRegistration = true; - - CFPlugInDynamicRegisterFunction func = (CFPlugInDynamicRegisterFunction)CFBundleGetFunctionPointerForName(bundle, tempStr); - if (func) { - __CFPLUGIN_IS_CALLING_OUT_TO_A_DYNAMIC_REGISTRATION_FUNCTION__(func, bundle); - } - - plugIn->_isDoingDynamicRegistration = false; - - if (plugIn->_loadOnDemand && plugIn->_instanceCount == 0) CFBundleUnloadExecutable(bundle); // Unload now if we can/should. + plugIn->_needsDynamicRegistration = false; + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFStringRef tempStr = (CFStringRef)CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegisterFunctionKey); + if (!tempStr || CFGetTypeID(tempStr) != CFStringGetTypeID() || CFStringGetLength(tempStr) <= 0) tempStr = CFSTR("CFPlugInDynamicRegister"); + plugIn->_loadOnDemand = false; + + plugIn->_isDoingDynamicRegistration = true; + + CFPlugInDynamicRegisterFunction func = (CFPlugInDynamicRegisterFunction)CFBundleGetFunctionPointerForName(bundle, tempStr); + if (func) { + __CFPLUGIN_IS_CALLING_OUT_TO_A_DYNAMIC_REGISTRATION_FUNCTION__(func, bundle); } - _CFPlugInGlobalDataLockRelease(); + plugIn->_isDoingDynamicRegistration = false; + + if (plugIn->_loadOnDemand && plugIn->_instanceCount == 0) CFBundleUnloadExecutable(bundle); // Unload now if we can/should. + + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); } CF_PRIVATE void _CFBundleDeallocatePlugIn(CFBundleRef bundle) { _CFPlugInData *plugIn = __CFBundleGetPlugInData(bundle); - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); if (plugIn->_isPlugIn) { /* Go through factories disabling them. Disabling these factories should cause them to dealloc since we wouldn't be deallocating if any of the factories had outstanding instances. So go backwards. */ + os_log_debug(_CFBundlePluginLogger(), "Disabling factories in array %{public}p for bundle %{public}p", __CFBundleGetPlugInData(bundle)->_factories, bundle); SInt32 c = CFArrayGetCount(plugIn->_factories); while (c-- > 0) _CFPFactoryDisableLocked((_CFPFactoryRef)CFArrayGetValueAtIndex(plugIn->_factories, c)); CFRelease(plugIn->_factories); plugIn->_isPlugIn = false; } - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); } CF_EXPORT CFTypeID CFPlugInGetTypeID(void) { @@ -441,24 +564,24 @@ CF_EXPORT CFBundleRef CFPlugInGetBundle(CFPlugInRef plugIn) { CF_EXPORT void CFPlugInSetLoadOnDemand(CFPlugInRef plugIn, Boolean flag) { _CFPlugInData *plugInData = __CFBundleGetPlugInData(plugIn); if (plugInData->_isPlugIn) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); plugInData->_loadOnDemand = flag; if (plugInData->_loadOnDemand && !plugInData->_isDoingDynamicRegistration && plugInData->_instanceCount == 0) { /* Unload now if we can/should. */ /* If we are doing dynamic registration currently, do not unload. The unloading will happen when dynamic registration is done, if necessary. */ - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); CFBundleUnloadExecutable(plugIn); } else if (!plugInData->_loadOnDemand) { - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); /* Make sure we're loaded now. */ CFBundleLoadExecutable(plugIn); } else { - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); } } } @@ -474,7 +597,7 @@ CF_EXPORT Boolean CFPlugInIsLoadOnDemand(CFPlugInRef plugIn) { CF_PRIVATE void _CFPlugInWillUnload(CFPlugInRef plugIn) { if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); SInt32 c = CFArrayGetCount(__CFBundleGetPlugInData(plugIn)->_factories); /* First, flush all the function pointers that may be cached by our factories. */ @@ -483,13 +606,15 @@ CF_PRIVATE void _CFPlugInWillUnload(CFPlugInRef plugIn) { factory->_func = NULL; } - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); } } static void _CFPlugInAddPlugInInstanceLocked(CFPlugInRef plugIn) { if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { - if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) { _CFBundleUnscheduleForUnloading(CFPlugInGetBundle(plugIn)); // Make sure we are not scheduled for unloading + if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) { + // Make sure we are not scheduled for unloading + _CFPlugInUnscheduleForUnloading(CFPlugInGetBundle(plugIn)); } __CFBundleGetPlugInData(plugIn)->_instanceCount++; /* Instances also retain the CFBundle */ @@ -499,18 +624,30 @@ static void _CFPlugInAddPlugInInstanceLocked(CFPlugInRef plugIn) { static void _CFPlugInRemovePlugInInstanceLocked(CFPlugInRef plugIn) { if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { - /* MF:!!! Assert that instanceCount > 0. */ __CFBundleGetPlugInData(plugIn)->_instanceCount--; if (__CFBundleGetPlugInData(plugIn)->_instanceCount == 0 && __CFBundleGetPlugInData(plugIn)->_loadOnDemand) { // We unload the code lazily because the code that caused this function to be called is probably code from the plugin itself. If we unload now, we will hose things. - _CFBundleScheduleForUnloading(CFPlugInGetBundle(plugIn)); + _CFPlugInScheduleForUnloading(CFPlugInGetBundle(plugIn)); } /* Instances also retain the CFPlugIn */ /* MF:!!! This will cause immediate unloading if it was the last ref on the plugin. */ + // Unless there is an 'unload prevention count' CFRelease(plugIn); } } +static void _CFPlugInIncrementUnloadPreventionLocked(CFPlugInRef plugIn) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + __CFBundleGetPlugInData(plugIn)->_unloadPreventionCount++; + } +} + +static void _CFPlugInDecrementUnloadPreventionLocked(CFPlugInRef plugIn) { + if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) { + __CFBundleGetPlugInData(plugIn)->_unloadPreventionCount--; + } +} + static void _CFPlugInAddFactoryLocked(CFPlugInRef plugIn, _CFPFactoryRef factory) { if (__CFBundleGetPlugInData(plugIn)->_isPlugIn) CFArrayAppendValue(__CFBundleGetPlugInData(plugIn)->_factories, factory); } @@ -561,12 +698,12 @@ static void _CFPFactoryRemoveFromTableLocked(_CFPFactoryRef factory) { os_log_debug(_CFBundlePluginLogger(), "Unregistered factory %{public}@ (%{public}@)", factory, uuid); } -static _CFPFactoryRef _CFPFactoryFindLocked(CFUUIDRef factoryID, Boolean enabled) { +static _CFPFactoryRef _CFPFactoryFindLocked(CFUUIDRef factoryID, Boolean matchOnlyEnabled) { _CFPFactoryRef result = NULL; if (_factoriesByFactoryID) { result = (_CFPFactoryRef )CFDictionaryGetValue(_factoriesByFactoryID, factoryID); - if (result && result->_enabled != enabled) result = NULL; + if (result && matchOnlyEnabled && !result->_enabled) result = NULL; } return result; @@ -576,7 +713,7 @@ static void _CFPFactoryDeallocate(CFTypeRef ty) { SInt32 c; _CFPFactoryRef factory = (_CFPFactoryRef)ty; - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); _CFPFactoryRemoveFromTableLocked(factory); @@ -590,7 +727,7 @@ static void _CFPFactoryDeallocate(CFTypeRef ty) { while (c-- > 0) _CFPFactoryRemoveTypeLocked(factory, (CFUUIDRef)CFArrayGetValueAtIndex(factory->_types, c)); CFRelease(factory->_types); - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); if (factory->_funcName) CFRelease(factory->_funcName); if (factory->_uuid) CFRelease(factory->_uuid); @@ -624,8 +761,6 @@ static CFPlugInRef _CFPFactoryCopyPlugInLocked(_CFPFactoryRef factory) { return result; } -static void *__CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(CFPlugInFactoryFunction, CFAllocatorRef, CFUUIDRef) __attribute__((noinline)); - static void *__CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(CFPlugInFactoryFunction f, CFAllocatorRef allocator, CFUUIDRef typeID) { FAULT_CALLBACK((void **)&(f)); void *result = (void *)INVOKE_CALLBACK2(f, allocator, typeID); @@ -633,32 +768,12 @@ static void *__CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(CFPlugInFactoryFu return result; } -static void *_CFPFactoryCreateInstanceLocked(CFAllocatorRef allocator, _CFPFactoryRef factory, CFUUIDRef typeID) { - void *result = NULL; - - if (factory->_enabled) { - if (!factory->_func) { - factory->_func = (CFPlugInFactoryFunction)CFBundleGetFunctionPointerForName(factory->_plugIn, factory->_funcName); - - if (!factory->_func) { - os_log_error(_CFBundlePluginLogger(), "Cannot find function pointer %{public}@ for factory %{public}@ in %{public}@", factory->_funcName, factory->_uuid, factory->_plugIn); - } - } - if (factory->_func) { - CFPlugInFactoryFunction f = factory->_func; - result = __CFPLUGIN_IS_CALLING_OUT_TO_A_FACTORY_FUNCTION__(f, allocator, typeID); - } - } else { - os_log_debug(_CFBundlePluginLogger(), "Atempted to create instance, but factory %{public}@ is disabled", factory->_uuid); - } - - return result; -} - static void _CFPFactoryDisableLocked(_CFPFactoryRef factory) { factory->_enabled = false; os_log_debug(_CFBundlePluginLogger(), "Factory %{public}@ has been disabled", factory->_uuid); +#if !__clang_analyzer__ CFRelease(factory); +#endif } static void _CFPFactoryAddTypeLocked(_CFPFactoryRef factory, CFUUIDRef typeID) { @@ -724,7 +839,7 @@ static void _CFPFactoryRemoveInstanceLocked(_CFPFactoryRef factory) { /* Functions for finding factories to create specific types and actually creating instances of a type. */ CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInType(CFUUIDRef typeID) CF_RETURNS_RETAINED { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); CFArrayRef array = NULL; if (_factoriesByTypeID) { array = (CFArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID); @@ -744,13 +859,13 @@ CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInType(CFUUIDRef typeID) CF_RET } } - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); os_log_debug(_CFBundlePluginLogger(), "%{public}ld factories found for requested plugin type %{public}@", result ? CFArrayGetCount(result) : 0, typeID); return result; } CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInTypeInPlugIn(CFUUIDRef typeID, CFPlugInRef plugIn) CF_RETURNS_RETAINED { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); CFArrayRef array = NULL; if (_factoriesByTypeID) { array = (CFArrayRef)CFDictionaryGetValue(_factoriesByTypeID, typeID); @@ -773,7 +888,7 @@ CF_EXPORT CFArrayRef CFPlugInFindFactoriesForPlugInTypeInPlugIn(CFUUIDRef typeID } } - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); os_log_debug(_CFBundlePluginLogger(), "%{public}ld factories found for requested plugin type %{public}@ in plugin %{public}@", result ? CFArrayGetCount(result) : 0, typeID, plugIn); return result; } @@ -790,7 +905,7 @@ static void __CFPlugInInstanceDeallocate(CFTypeRef cf) { __CFGenericValidateType(cf, CFPlugInInstanceGetTypeID()); - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); if (instance->deallocateInstanceDataFunction) { FAULT_CALLBACK((void **)&(instance->deallocateInstanceDataFunction)); @@ -799,7 +914,7 @@ static void __CFPlugInInstanceDeallocate(CFTypeRef cf) { if (instance->factory) _CFPFactoryRemoveInstanceLocked(instance->factory); - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); } const CFRuntimeClass __CFPlugInInstanceClass = { @@ -825,14 +940,14 @@ CF_EXPORT CFPlugInInstanceRef CFPlugInInstanceCreateWithInstanceDataSize(CFAlloc instance = (CFPlugInInstanceRef)_CFRuntimeCreateInstance(allocator, CFPlugInInstanceGetTypeID(), size, NULL); if (!instance) return NULL; - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); instance->factory = _CFPFactoryFindLocked((CFUUIDRef)factoryName, true); if (instance->factory) _CFPFactoryAddInstanceLocked(instance->factory); instance->getInterfaceFunction = getInterfaceFunction; instance->deallocateInstanceDataFunction = deallocateInstanceFunction; - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return instance; } @@ -850,13 +965,13 @@ CF_EXPORT Boolean CFPlugInInstanceGetInterfaceFunctionTable(CFPlugInInstanceRef } CF_EXPORT CFStringRef CFPlugInInstanceGetFactoryName(CFPlugInInstanceRef instance) { - _CFPlugInGlobalDataLockAcquire(); + os_unfair_recursive_lock_lock_with_options(&CFPlugInGlobalDataLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); // This function leaks, but it's the only safe way to access the factory name (on 10.8 or later). // On 10.9 we added the CF_RETURNS_RETAINED annotation to the header. CFUUIDRef factoryId = _CFPFactoryCopyFactoryIDLocked(instance->factory); - _CFPlugInGlobalDataLockRelease(); + os_unfair_recursive_lock_unlock(&CFPlugInGlobalDataLock); return (CFStringRef)factoryId; } diff --git a/CoreFoundation/RunLoop.subproj/CFMachPort.c b/CoreFoundation/RunLoop.subproj/CFMachPort.c index c40afd8a2b..edf81efb26 100644 --- a/CoreFoundation/RunLoop.subproj/CFMachPort.c +++ b/CoreFoundation/RunLoop.subproj/CFMachPort.c @@ -136,7 +136,10 @@ CF_INLINE void __CFMachPortInvalidateLocked(CFRunLoopSourceRef source, CFMachPor __CFLock(&mp->_lock); } mp->_state = kCFMachPortStateInvalid; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" OSMemoryBarrier(); +#pragma GCC diagnostic pop } static void __CFMachPortDeallocate(CFTypeRef cf) { @@ -149,7 +152,10 @@ static void __CFMachPortDeallocate(CFTypeRef cf) { Boolean wasReady = (mp->_state == kCFMachPortStateReady); if (wasReady) { mp->_state = kCFMachPortStateInvalidating; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" OSMemoryBarrier(); +#pragma GCC diagnostic pop if (mp->_dsrc) { dispatch_source_cancel(mp->_dsrc); mp->_dsrc = NULL; @@ -203,7 +209,10 @@ static void __CFMachPortChecker(void) { } else { mp->_state = kCFMachPortStateInvalidating; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" OSMemoryBarrier(); +#pragma GCC diagnostic pop if (mp->_dsrc) { dispatch_source_cancel(mp->_dsrc); mp->_dsrc = NULL; @@ -320,7 +329,10 @@ CFMachPortRef _CFMachPortCreateWithPort2(CFAllocatorRef allocator, mach_port_t p _cfmp_source_invalidated(_CFMPLifetimeClientCFMachPort, port); dispatch_release(theSource); }); - dispatch_source_set_event_handler(theSource, ^{ __CFMachPortChecker(); }); + dispatch_source_set_event_handler(theSource, ^{ + _cfmp_source_record_deadness(_CFMPLifetimeClientCFMachPort, port); + __CFMachPortChecker(); + }); memory->_dsrc = theSource; dispatch_resume(theSource); } @@ -379,7 +391,10 @@ void CFMachPortInvalidate(CFMachPortRef mp) { wasReady = (mp->_state == kCFMachPortStateReady); if (wasReady) { mp->_state = kCFMachPortStateInvalidating; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" OSMemoryBarrier(); +#pragma GCC diagnostic pop for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) { CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx); if (p == mp) { @@ -412,6 +427,7 @@ mach_port_t CFMachPortGetPort(CFMachPortRef mp) { } void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFMachPort, mp); __CFGenericValidateType(mp, CFMachPortGetTypeID()); CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); memmove(context, &mp->_context, sizeof(CFMachPortContext)); @@ -431,7 +447,7 @@ Boolean CFMachPortIsValid(CFMachPortRef mp) { } CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) { - __CFGenericValidateType(mp, CFMachPortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMachPort, mp); __CFLock(&mp->_lock); CFMachPortInvalidationCallBack cb = mp->_icallout; __CFUnlock(&mp->_lock); @@ -441,8 +457,8 @@ CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef m /* After the CFMachPort has started going invalid, or done invalid, you can't change this, and we'll only do the callout directly on a transition from NULL to non-NULL. */ void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFMachPort, mp); CHECK_FOR_FORK_RET(); - __CFGenericValidateType(mp, CFMachPortGetTypeID()); if (callout) { mach_port_type_t type = 0; kern_return_t ret = mach_port_type(mach_task_self(), mp->_port, &type); @@ -466,8 +482,8 @@ void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationC /* Returns the number of messages queued for a receive port. */ CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFMachPort, mp); CHECK_FOR_FORK_RET(0); - __CFGenericValidateType(mp, CFMachPortGetTypeID()); mach_port_status_t status; mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT; kern_return_t ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num); @@ -512,8 +528,8 @@ CF_PRIVATE void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef all CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFMachPort, mp); CHECK_FOR_FORK_RET(NULL); - __CFGenericValidateType(mp, CFMachPortGetTypeID()); if (!CFMachPortIsValid(mp)) return NULL; CFRunLoopSourceRef result = NULL; __CFLock(&mp->_lock); diff --git a/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.c b/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.c index f385e643fa..3c344decdc 100644 --- a/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.c +++ b/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.c @@ -242,6 +242,19 @@ void _cfmp_source_invalidated(_CFMPLifetimeClient const client, mach_port_t port } } +// records that we have received a deadname notification for the specified port +void _cfmp_source_record_deadness(_CFMPLifetimeClient const client, mach_port_t const port) { + CFMutableSetRef const records = _cfmp_records(); + os_unfair_lock_lock(&_cfmp_records_lock); + _cfmp_deallocation_record *const pr = _cfmp_find_record_for_port(records, client, port); + if (pr == NULL) { + _cfmp_log_failure("received deadname notification for untracked port", pr, client, port); + } else { + pr->doReceive = 0; + } + os_unfair_lock_unlock(&_cfmp_records_lock); +} + void _cfmp_record_nsmachport_is_interested(_CFMPLifetimeClient const client, mach_port_t const port) { if (port == MACH_PORT_NULL) { return; } diff --git a/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.h b/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.h index 307e4e5f71..b34ef6d2bb 100644 --- a/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.h +++ b/CoreFoundation/RunLoop.subproj/CFMachPort_Lifetime.h @@ -31,6 +31,9 @@ CF_PRIVATE void _cfmp_record_intent_to_invalidate(_CFMPLifetimeClient const clie /// Records when the cancel-handler for a given dispatch_port is called. CF_PRIVATE void _cfmp_source_invalidated(_CFMPLifetimeClient const client, mach_port_t const port); +/// Records that we have received a deadname notification for the specified port. +CF_PRIVATE void _cfmp_source_record_deadness(_CFMPLifetimeClient const client, mach_port_t const port); + /// For Foundation Only /// Records when an nsmach port is going to be interested in a bridges CFMachPort CF_EXPORT void _cfmp_record_nsmachport_is_interested(_CFMPLifetimeClient const client, mach_port_t const port); diff --git a/CoreFoundation/RunLoop.subproj/CFMessagePort.c b/CoreFoundation/RunLoop.subproj/CFMessagePort.c index 60f609f654..8fc8d82d84 100644 --- a/CoreFoundation/RunLoop.subproj/CFMessagePort.c +++ b/CoreFoundation/RunLoop.subproj/CFMessagePort.c @@ -536,20 +536,20 @@ CFMessagePortRef CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator, C } Boolean CFMessagePortIsRemote(CFMessagePortRef ms) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); return __CFMessagePortIsRemote(ms); } CFStringRef CFMessagePortGetName(CFMessagePortRef ms) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); return ms->_name; } Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef inName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); CFAllocatorRef allocator = CFGetAllocator(ms); uint8_t *utfname = NULL; - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); if (ms->_perPID || __CFMessagePortIsRemote(ms)) return false; CFStringRef const name = __CFMessagePortCreateSanitizedStringName(inName, &utfname, NULL); if (NULL == name) { @@ -664,14 +664,14 @@ Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef inName) { } void CFMessagePortGetContext(CFMessagePortRef ms, CFMessagePortContext *context) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); //#warning CF: assert that this is a local port CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); memmove(context, &ms->_context, sizeof(CFMessagePortContext)); } void CFMessagePortInvalidate(CFMessagePortRef ms) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFMessagePort, ms); if (ms == NULL) { return; } @@ -749,7 +749,7 @@ void CFMessagePortInvalidate(CFMessagePortRef ms) { } Boolean CFMessagePortIsValid(CFMessagePortRef ms) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); if (!__CFMessagePortIsValid(ms)) return false; CFRetain(ms); if (NULL != ms->_port && !CFMachPortIsValid(ms->_port)) { @@ -767,12 +767,12 @@ Boolean CFMessagePortIsValid(CFMessagePortRef ms) { } CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); return ms->_icallout; } void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms, CFMessagePortInvalidationCallBack callout) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); if (!__CFMessagePortIsValid(ms) && NULL != callout) { callout(ms, ms->_context.info); } else { @@ -848,6 +848,7 @@ static void __CFMessagePortReplyCallBack(CFMachPortRef port, void *msg, CFIndex } SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, remote); mach_msg_base_t *sendmsg; CFRunLoopRef currentRL = CFRunLoopGetCurrent(); CFRunLoopSourceRef source = NULL; @@ -908,9 +909,16 @@ SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef if (KERN_SUCCESS != ret) { // need to deallocate the send-once right that might have been created if (replyMode != NULL && - (ret == MACH_SEND_INVALID_DEST || ret == MACH_SEND_TIMED_OUT) // [55207069] it is only valid to clean cleaup the local port for these two return values; to do otherwise is unsafe/undefined-behavior + (ret == MACH_SEND_INVALID_DEST || ret == MACH_SEND_INTERRUPTED || ret == MACH_SEND_TIMED_OUT) // [55207069, 39359253] it is only valid to clean cleaup the local port for these two return values; to do otherwise is unsafe/undefined-behavior ) { - mach_port_deallocate(mach_task_self(), ((mach_msg_header_t *)sendmsg)->msgh_local_port); + mach_port_t port = ((mach_msg_header_t *)sendmsg)->msgh_local_port; + if (MACH_PORT_VALID(port)) { + if (MACH_MSGH_BITS_LOCAL(((mach_msg_header_t *)sendmsg)->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) { + /* destroy the send-once right */ + (void) mach_port_deallocate(mach_task_self(), port); + ((mach_msg_header_t *)sendmsg)->msgh_local_port = MACH_PORT_NULL; + } + } } if (didRegister) { CFRunLoopRemoveSource(currentRL, source, replyMode); @@ -971,7 +979,7 @@ static mach_port_t __CFMessagePortGetPort(void *info) { static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { CFMessagePortRef ms = info; mach_msg_base_t *msgp = msg; - mach_msg_base_t *replymsg; + mach_msg_base_t *replymsg = NULL; void *context_info; void (*context_release)(const void *); CFDataRef returnData, data = NULL; @@ -1064,17 +1072,21 @@ static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allo return_bytes = (void *)CFDataGetBytePtr(returnData); } else if (returnData) { return_bytes = NULL; - vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); - /* vm_copy would only be a win here if the source address - is page aligned; it is a lose in all other cases, since - the kernel will just do the memmove for us (but not in - as simple a way). */ - memmove(return_bytes, CFDataGetBytePtr(returnData), return_len); + kern_return_t ret = vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); + if (ret == KERN_SUCCESS) { + /* vm_copy would only be a win here if the source address + is page aligned; it is a lose in all other cases, since + the kernel will just do the memmove for us (but not in + as simple a way). */ + memmove(return_bytes, CFDataGetBytePtr(returnData), return_len); + } else { + return_len = 0; + } } } replymsg = __CFMessagePortCreateMessage(true, msgp->header.msgh_remote_port, MACH_PORT_NULL, -1 * (int32_t)MSGP_INFO(msgp, convid), msgid, return_bytes, return_len); if (replymsg->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) { - MSGP_GET(replymsg, ool).deallocate = true; + MSGP_GET(replymsg, ool).deallocate = true; } if (data) CFRelease(data); if (msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) { @@ -1088,8 +1100,8 @@ static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allo } CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef ms, CFIndex order) { + CF_ASSERT_TYPE_OR_NULL(_kCFRuntimeIDCFMessagePort, ms); CFRunLoopSourceRef result = NULL; - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); if (!CFMessagePortIsValid(ms)) return NULL; if (__CFMessagePortIsRemote(ms)) return NULL; __CFMessagePortLock(ms); @@ -1118,7 +1130,7 @@ CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CF } void CFMessagePortSetDispatchQueue(CFMessagePortRef ms, dispatch_queue_t queue) { - __CFGenericValidateType(ms, CFMessagePortGetTypeID()); + CF_ASSERT_TYPE(_kCFRuntimeIDCFMessagePort, ms); __CFMessagePortLock(ms); if (!__CFMessagePortIsValid(ms)) { __CFMessagePortUnlock(ms); @@ -1184,7 +1196,7 @@ void CFMessagePortSetDispatchQueue(CFMessagePortRef ms, dispatch_queue_t queue) if (NULL != reply) { kern_return_t const ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); - __CFMachMessageCheckForAndDestroyUnsentMessage(ret, msg); + __CFMachMessageCheckForAndDestroyUnsentMessage(ret, reply); CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); } diff --git a/CoreFoundation/RunLoop.subproj/CFRunLoop.c b/CoreFoundation/RunLoop.subproj/CFRunLoop.c index 3855245c6c..4c14b90259 100644 --- a/CoreFoundation/RunLoop.subproj/CFRunLoop.c +++ b/CoreFoundation/RunLoop.subproj/CFRunLoop.c @@ -14,6 +14,7 @@ #include #include #include "CFInternal.h" +#include "CFPriv.h" #include "CFRuntime_Internal.h" #include "CFMachPort_Internal.h" #include @@ -33,9 +34,12 @@ extern void objc_terminate(void); +#if TARGET_OS_WIN32 +#include +#endif #include "CFOverflow.h" -#if TARGET_OS_MAC || TARGET_OS_WIN32 || !DEPLOYMENT_RUNTIME_OBJC +#if DEPLOYMENT_RUNTIME_OBJC #define USE_DISPATCH_SOURCE_FOR_TIMERS __HAS_DISPATCH__ #else #define USE_DISPATCH_SOURCE_FOR_TIMERS 0 @@ -120,6 +124,7 @@ CF_EXPORT _CFThreadRef _CF_pthread_main_thread_np(void); #include "Block_private.h" #endif + // Open source CF may not have this defined. #ifndef cf_trace #define cf_trace(...) do {} while (0) @@ -195,11 +200,6 @@ static _CFThreadRef const kNilPthreadT = (_CFThreadRef)0; #define CFRUNLOOP_WAKEUP_FOR_WAKEUP_ENABLED() (0) #endif -#define CFRUNLOOP_ARP_BEGIN -#define CFRUNLOOP_ARP_END - - - // NOTE: this is locally defined rather than in CFInternal.h as on Linux, // `linux/sysctl.h` defines `struct __sysctl_args` with an `__unused` member @@ -214,9 +214,12 @@ static _CFThreadRef const kNilPthreadT = (_CFThreadRef)0; #endif // !defined(__unused) #endif +#define CFRUNLOOP_ARP_BEGIN(...) +#define CFRUNLOOP_ARP_END(...) + // In order to reuse most of the code across Mach and Windows v1 RunLoopSources, we define a // simple abstraction layer spanning Mach ports and Windows HANDLES -#if TARGET_OS_MAC +#if TARGET_OS_MAC typedef mach_port_t __CFPort; #define CFPORT_NULL MACH_PORT_NULL typedef mach_port_t __CFPortSet; @@ -404,14 +407,6 @@ typedef int __CFPort; typedef int __CFPortSet; #define CFPORTSET_NULL -1 -#ifndef __unused - #if __has_attribute(unused) - #define __unused __attribute__((unused)) - #else - #define __unused - #endif -#endif // !defined(__unused) - static __CFPort __CFPortAllocate(__unused uintptr_t guard) { return eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK); } @@ -694,6 +689,7 @@ struct __CFRunLoop { CFAbsoluteTime _sleepTime; CFTypeRef _counterpart; _Atomic(uint8_t) _fromTSD; + Boolean _perCalloutARP; CFLock_t _timerTSRLock; }; @@ -701,6 +697,30 @@ struct __CFRunLoop { /* Bit 1 of the base reserved bits is used for sleeping state */ /* Bit 2 of the base reserved bits is used for deallocating state */ +// When `rl` is 0, will push an ARP unconditionally. A hack to facilitate places where we had ARPs before. +static inline uintptr_t __CFRunLoopPerCalloutARPBegin(CFRunLoopRef rl) { +#if DEPLOYMENT_RUNTIME_OBJC + return !rl || rl->_perCalloutARP ? _CFAutoreleasePoolPush() : 0; +#else + return 0; +#endif +} + +static inline void __CFRunLoopPerCalloutARPEnd(const uintptr_t pool) { +#if DEPLOYMENT_RUNTIME_OBJC + if (pool) { + @try { + _CFAutoreleasePoolPop(pool); + } @catch (NSException *e) { + os_log_error(_CFOSLog(), "Caught exception during runloop's autorelease pool drain of client objects %{public}@: %{private}@ userInfo: %{private}@", e.name, e.reason, e.userInfo); + objc_terminate(); + } @catch (...) { + objc_terminate(); + } + } +#endif +} + CF_INLINE volatile _per_run_data *__CFRunLoopPushPerRunData(CFRunLoopRef rl) { volatile _per_run_data *previous = rl->_perRunData; rl->_perRunData = (volatile _per_run_data *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(_per_run_data), 0); @@ -789,8 +809,8 @@ CF_PRIVATE void __CFRunLoopDump() { // __private_extern__ to keep the compiler f CFRelease(desc); } -/* call with rl locked, returns mode locked */ -static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeName, Boolean create) { +/* call with rl locked, returns mode retained */ +static CFRunLoopModeRef __CFRunLoopCopyMode(CFRunLoopRef rl, CFStringRef modeName, Boolean create) { CHECK_FOR_FORK(); CFRunLoopModeRef rlm; struct __CFRunLoopMode srlm; @@ -799,7 +819,7 @@ static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeNam srlm._name = modeName; rlm = (CFRunLoopModeRef)CFSetGetValue(rl->_modes, &srlm); if (NULL != rlm) { - __CFRunLoopModeLock(rlm); + CFRetain(rlm); return rlm; } if (!create) { @@ -816,7 +836,6 @@ static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeNam rlm->_timerHardDeadline = UINT64_MAX; kern_return_t ret = KERN_SUCCESS; -#if TARGET_OS_MAC #if USE_DISPATCH_SOURCE_FOR_TIMERS rlm->_timerFired = false; rlm->_queue = _dispatch_runloop_root_queue_create_4CF("Run Loop Mode Queue", 0); @@ -836,7 +855,6 @@ static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeNam ret = __CFPortSetInsert(queuePort, rlm->_portSet); if (KERN_SUCCESS != ret) CRASH("*** Unable to insert timer port into port set. (%d) ***", ret); -#endif #endif rlm->_timerPort = mk_timer_create(); if (rlm->_timerPort == MACH_PORT_NULL) { @@ -853,8 +871,6 @@ static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeNam rlm->_msgPump = NULL; #endif CFSetAddValue(rl->_modes, rlm); - CFRelease(rlm); - __CFRunLoopModeLock(rlm); /* return mode locked */ return rlm; } @@ -899,8 +915,10 @@ uint32_t _CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef modeN __CFRunLoopLock(rl); CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false); if (rlm) { + __CFRunLoopModeLock(rlm); result = rlm->_msgQMask; __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } __CFRunLoopUnlock(rl); return (uint32_t)result; @@ -913,8 +931,10 @@ void _CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, uint32_t mask, CFStri } __CFRunLoopLock(rl); CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true); + __CFRunLoopModeLock(rlm); rlm->_msgQMask = (DWORD)mask; __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); __CFRunLoopUnlock(rl); } @@ -935,8 +955,10 @@ CFWindowsMessageQueueHandler _CFRunLoopGetWindowsMessageQueueHandler(CFRunLoopRe __CFRunLoopLock(rl); CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false); if (rlm) { - result = rlm->_msgPump; + __CFRunLoopModeLock(rlm); + result = rlm->_msgPump; __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } __CFRunLoopUnlock(rl); return result; @@ -953,8 +975,10 @@ void _CFRunLoopSetWindowsMessageQueueHandler(CFRunLoopRef rl, CFStringRef modeNa } __CFRunLoopLock(rl); CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true); + __CFRunLoopModeLock(rlm); rlm->_msgPump = func; __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); __CFRunLoopUnlock(rl); } @@ -1364,11 +1388,14 @@ static CFRunLoopRef __CFRunLoopCreate(_CFThreadRef t) { loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); loop->_pthread = t; loop->_timerTSRLock = CFLockInit; + loop->_perCalloutARP = true; #if TARGET_OS_WIN32 loop->_winthread = GetCurrentThreadId(); #endif - rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true); - if (NULL != rlm) __CFRunLoopModeUnlock(rlm); + rlm = __CFRunLoopCopyMode(loop, kCFRunLoopDefaultMode, true); + if (NULL != rlm) { + CFRelease(rlm); + } return loop; } @@ -1386,7 +1413,10 @@ CF_PRIVATE CFRunLoopRef _CFRunLoopCacheLookup(_CFThreadRef t, const Boolean crea CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) { +#pragma GCC diagnostic pop CFRelease(dict); } CFRelease(mainLoop); @@ -1428,7 +1458,10 @@ CF_EXPORT CFRunLoopRef _CFRunLoopGet0(_CFThreadRef t) { CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) { +#pragma GCC diagnostic pop CFRelease(dict); } CFRelease(mainLoop); @@ -1578,6 +1611,7 @@ CFRunLoopRef CFRunLoopGetCurrent(void) { } CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); CFStringRef result = NULL; __CFRunLoopLock(rl); @@ -1595,6 +1629,7 @@ static void __CFRunLoopGetModeName(const void *value, void *context) { } CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); CFMutableArrayRef array; __CFRunLoopLock(rl); @@ -1654,6 +1689,7 @@ CF_EXPORT Boolean _CFRunLoop01(CFRunLoopRef rl, CFStringRef modeName) { } void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return; __CFRunLoopLock(rl); @@ -1673,7 +1709,7 @@ void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) { #if __HAS_DISPATCH__ -static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg) __attribute__((noinline)); +static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *) __attribute__((noinline)); static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg) { _dispatch_main_queue_callback_4CF(msg); __asm __volatile__(""); // thwart tail-call optimization @@ -1681,7 +1717,7 @@ static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg) { #endif -static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) __attribute__((noinline)); +static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack, CFRunLoopObserverRef, CFRunLoopActivity, void *) __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { if (func) { func(observer, activity, info); @@ -1689,7 +1725,7 @@ static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunL __asm __volatile__(""); // thwart tail-call optimization } -static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) __attribute__((noinline)); +static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack, CFRunLoopTimerRef, void *) __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) { if (func) { func(timer, info); @@ -1697,7 +1733,7 @@ static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopT __asm __volatile__(""); // thwart tail-call optimization } -static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) __attribute__((noinline)); +static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^)(void)) __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) { if (block) { block(); @@ -1740,9 +1776,11 @@ static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // C CFRelease(curr->_mode); free(curr); if (doit) { + CFRUNLOOP_ARP_BEGIN(rl); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_BLOCK | DBG_FUNC_START, rl, rlm, block, 0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_BLOCK | DBG_FUNC_END, rl, rlm, block, 0); + CFRUNLOOP_ARP_END(); did = true; } Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc @@ -1762,7 +1800,7 @@ static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // C } /* rl is locked, rlm is locked on entrance and exit */ -static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) __attribute__((noinline)); +static void __CFRunLoopDoObservers(CFRunLoopRef, CFRunLoopModeRef, CFRunLoopActivity) __attribute__((noinline)); static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */ cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_OBSERVERS | DBG_FUNC_START, rl, rlm, activity, 0); @@ -1793,9 +1831,11 @@ static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunL __CFRunLoopObserverUnlock(rlo); CFRunLoopObserverCallBack callout = rlo->_callout; void *info = rlo->_context.info; + CFRUNLOOP_ARP_BEGIN(rl) cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_OBSERVER | DBG_FUNC_START, callout, rlo, activity, info); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(callout, rlo, activity, info); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_OBSERVER | DBG_FUNC_END, callout, rlo, activity, info); + CFRUNLOOP_ARP_END() if (doInvalidate) { CFRunLoopObserverInvalidate(rlo); } @@ -1845,7 +1885,7 @@ static void __CFRunLoopCollectSources0(const void *value, void *context) { } } -static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) __attribute__((noinline)); +static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*)(void *), void *) __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) { if (perform) { perform(info); @@ -1880,7 +1920,7 @@ static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__( __asm __volatile__(""); // thwart tail-call optimization } -static Boolean __CFRunLoopDoSource0(CFRunLoopSourceRef rls) { +static Boolean __CFRunLoopDoSource0(CFRunLoopRef rl, CFRunLoopSourceRef rls) { Boolean sourceHandled = false; __CFRunLoopSourceLock(rls); @@ -1890,9 +1930,11 @@ static Boolean __CFRunLoopDoSource0(CFRunLoopSourceRef rls) { __CFRunLoopSourceUnlock(rls); void *perform = rls->_context.version0.perform; void *info = rls->_context.version0.info; + CFRUNLOOP_ARP_BEGIN(rl) cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE0 | DBG_FUNC_START, perform, info, 0, 0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(perform, info); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE0 | DBG_FUNC_END, perform, info, 0, 0); + CFRUNLOOP_ARP_END() CHECK_FOR_FORK(); sourceHandled = true; } else { @@ -1927,7 +1969,7 @@ static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Bool if (CFGetTypeID(sources) == CFRunLoopSourceGetTypeID()) { CFRunLoopSourceRef rls = (CFRunLoopSourceRef)sources; - sourceHandled = __CFRunLoopDoSource0(rls); + sourceHandled = __CFRunLoopDoSource0(rl, rls); } else { CFIndex cnt = CFArrayGetCount((CFArrayRef)sources); @@ -1935,7 +1977,7 @@ static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Bool for (CFIndex idx = 0; idx < cnt; idx++) { CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx); - sourceHandled = __CFRunLoopDoSource0(rls); + sourceHandled = __CFRunLoopDoSource0(rl, rls); if (stopAfterHandle && sourceHandled) { break; @@ -1956,12 +1998,11 @@ CF_INLINE void __CFRunLoopDebugInfoForRunLoopSource(CFRunLoopSourceRef rls) { } // msg, size and reply are unused on Windows - -#if TARGET_OS_MAC +#if TARGET_OS_MAC static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls, mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply) __attribute__((noinline)); + static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls, mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply) #else -static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls) __attribute__((noinline)); static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls) #endif @@ -1981,6 +2022,7 @@ static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRun __CFRunLoopDebugInfoForRunLoopSource(rls); void *perform = rls->_context.version1.perform; void *info = rls->_context.version1.info; + CFRUNLOOP_ARP_BEGIN(rl) cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE1 | DBG_FUNC_START, rl, rlm, perform, info); __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(perform, #if TARGET_OS_MAC @@ -1988,6 +2030,7 @@ static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRun #endif info); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE1 | DBG_FUNC_END, rl, rlm, perform, info); + CFRUNLOOP_ARP_END() CHECK_FOR_FORK(); sourceHandled = true; } else { @@ -2197,10 +2240,12 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo __CFRunLoopUnlock(rl); CFRunLoopTimerCallBack callout = rlt->_callout; + CFRUNLOOP_ARP_BEGIN(NULL) cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_TIMER | DBG_FUNC_START, callout, rlt, context_info, 0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(callout, rlt, context_info); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_TIMER | DBG_FUNC_END, callout, rlt, context_info, 0); - + CFRUNLOOP_ARP_END() + CHECK_FOR_FORK(); if (doInvalidate) { CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */ @@ -2269,7 +2314,10 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo __CFRunLoopTimerUnlock(rlt); for (CFIndex idx = 0; idx < cnt; idx++) { CFStringRef name = (CFStringRef)modes[idx]; - modes[idx] = (CFTypeRef)__CFRunLoopFindMode(rlt_rl, name, false); + modes[idx] = (CFTypeRef)__CFRunLoopCopyMode(rlt_rl, name, false); + if (modes[idx]) { + __CFRunLoopModeLock((CFRunLoopModeRef)modes[idx]); + } CFRelease(name); } __CFLock(&rl->_timerTSRLock); @@ -2282,8 +2330,11 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo } } __CFUnlock(&rl->_timerTSRLock); - for (CFIndex idx = 0; idx < cnt; idx++) { - __CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]); + for (CFIndex idx = cnt - 1; idx >= 0; idx--) { // reverse index here so we unlock in the right order + if (modes[idx] != NULL) { + __CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]); + CFRelease((CFRunLoopModeRef)modes[idx]); + } } CFRelease(rlt_rl); } else { @@ -2323,8 +2374,6 @@ static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64 } } - CFRUNLOOP_ARP_BEGIN; - for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) { CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx); Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt); @@ -2332,9 +2381,6 @@ static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64 } if (timers) CFRelease(timers); - CFRUNLOOP_ARP_END; - - cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_TIMERS | DBG_FUNC_END, rl, rlm, limitTSR, 0); return timerHandled; @@ -2346,11 +2392,17 @@ CF_EXPORT Boolean _CFRunLoopFinished(CFRunLoopRef rl, CFStringRef modeName) { CFRunLoopModeRef rlm; Boolean result = false; __CFRunLoopLock(rl); - rlm = __CFRunLoopFindMode(rl, modeName, false); + rlm = __CFRunLoopCopyMode(rl, modeName, false); + if (rlm) { + __CFRunLoopModeLock(rlm); + } if (NULL == rlm || __CFRunLoopModeIsEmpty(rl, rlm, NULL)) { result = true; } - if (rlm) __CFRunLoopModeUnlock(rlm); + if (rlm) { + __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); + } __CFRunLoopUnlock(rl); return result; } @@ -2406,11 +2458,31 @@ static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header *livePort = MACH_PORT_NULL; return false; } - if (MACH_RCV_TOO_LARGE != ret) break; + if (MACH_RCV_TOO_LARGE != ret) { + if (((MACH_RCV_HEADER_ERROR & ret) == MACH_RCV_HEADER_ERROR) || (MACH_RCV_BODY_ERROR & ret) == MACH_RCV_BODY_ERROR) { + kern_return_t specialBits = MACH_MSG_MASK & ret; + if (MACH_MSG_IPC_SPACE == specialBits) { + CRSetCrashLogMessage("Out of IPC space"); + } else if (MACH_MSG_VM_SPACE == specialBits) { + CRSetCrashLogMessage("Out of VM address space"); + } else if (MACH_MSG_IPC_KERNEL == specialBits) { + CRSetCrashLogMessage("Kernel resource shortage handling IPC"); + } else if (MACH_MSG_VM_KERNEL == specialBits) { + CRSetCrashLogMessage("Kernel resource shortage handling out-of-line memory"); + } + } else { + CRSetCrashLogMessage(mach_error_string(ret)); + } + break; + } buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE); if (originalBuffer) *buffer = NULL; originalBuffer = false; *buffer = __CFSafelyReallocate(*buffer, buffer_size, NULL); + + if (voucherCopy != NULL && *voucherCopy != NULL) { + os_release(*voucherCopy); + } } HALT; return false; @@ -2562,33 +2634,6 @@ static Boolean __CFRunLoopWaitForMultipleObjects(__CFPortSet portSet, HANDLE *on #endif -struct __timeout_context { -#if __HAS_DISPATCH__ - dispatch_source_t ds; -#endif - CFRunLoopRef rl; - _Atomic(uint64_t) termTSR; -}; - -static void __CFRunLoopTimeoutCancel(void *arg) { - struct __timeout_context *context = (struct __timeout_context *)arg; - CFRelease(context->rl); -#if __HAS_DISPATCH__ - dispatch_release(context->ds); -#endif - free(context); -} - -static void __CFRunLoopTimeout(void *arg) { - struct __timeout_context *context = (struct __timeout_context *)arg; - context->termTSR = 0ULL; - CFRUNLOOP_WAKEUP_FOR_TIMEOUT(); - CFRunLoopRef rl = context->rl; - cf_trace(KDEBUG_EVENT_CFRL_DID_WAKEUP_FOR_TIMEOUT, rl, 0, 0, 0); - CFRunLoopWakeUp(rl); - // The interval is DISPATCH_TIME_FOREVER, so this won't fire again -} - /* rl, rlm are locked on entrance and exit */ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { uint64_t startTSR = mach_absolute_time(); @@ -2607,7 +2652,6 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF(); #endif -#if TARGET_OS_MAC #if USE_DISPATCH_SOURCE_FOR_TIMERS mach_port_name_t modeQueuePort = MACH_PORT_NULL; if (rlm->_queue) { @@ -2617,35 +2661,38 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter } } #endif -#endif + uint64_t termTSR = 0ULL; #if __HAS_DISPATCH__ dispatch_source_t timeout_timer = NULL; #endif - struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context)); if (seconds <= 0.0) { // instant timeout seconds = 0.0; - timeout_context->termTSR = 0ULL; + termTSR = 0ULL; } else if (seconds <= TIMER_INTERVAL_LIMIT) { + termTSR = startTSR + __CFTimeIntervalToTSR(seconds); #if __HAS_DISPATCH__ dispatch_queue_t queue = (pthread_main_np() == 1) ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground(); timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); - dispatch_retain(timeout_timer); - timeout_context->ds = timeout_timer; -#endif - timeout_context->rl = (CFRunLoopRef)CFRetain(rl); - timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds); -#if __HAS_DISPATCH__ - dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context - dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout); - dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel); + + CFRetain(rl); + dispatch_source_set_event_handler(timeout_timer, ^{ + CFRUNLOOP_WAKEUP_FOR_TIMEOUT(); + cf_trace(KDEBUG_EVENT_CFRL_DID_WAKEUP_FOR_TIMEOUT, rl, 0, 0, 0); + CFRunLoopWakeUp(rl); + // The interval is DISPATCH_TIME_FOREVER, so this won't fire again + }); + dispatch_source_set_cancel_handler(timeout_timer, ^{ + CFRelease(rl); + }); + uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL); dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL); dispatch_resume(timeout_timer); #endif } else { // infinite timeout seconds = 9999999999.0; - timeout_context->termTSR = UINT64_MAX; + termTSR = UINT64_MAX; } Boolean didDispatchPortLastTime = true; @@ -2684,7 +2731,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __CFRunLoopDoBlocks(rl, rlm); } - Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); + Boolean poll = sourceHandledThisLoop || (0ULL == termTSR); #if __HAS_DISPATCH__ if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { @@ -2830,7 +2877,6 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter ResetEvent(rl->_wakeUpPort); #endif } -#if TARGET_OS_MAC #if USE_DISPATCH_SOURCE_FOR_TIMERS else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); @@ -2840,7 +2886,6 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __CFArmNextTimerInMode(rlm, rl); } } -#endif #endif else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); @@ -2869,17 +2914,15 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __CFRunLoopUnlock(rl); _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL); - CFRUNLOOP_ARP_BEGIN; - #if TARGET_OS_WIN32 || TARGET_OS_LINUX void *msg = 0; #endif + CFRUNLOOP_ARP_BEGIN(NULL) cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_DISPATCH | DBG_FUNC_START, rl, rlm, msg, livePort); __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_DISPATCH | DBG_FUNC_END, rl, rlm, msg, livePort); - - CFRUNLOOP_ARP_END; - + CFRUNLOOP_ARP_END() + _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL); __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); @@ -2922,7 +2965,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter if (sourceHandledThisLoop && stopAfterHandle) { retVal = kCFRunLoopRunHandledSource; - } else if (timeout_context->termTSR < mach_absolute_time()) { + } else if (termTSR < mach_absolute_time()) { retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); @@ -2939,11 +2982,8 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter if (timeout_timer) { dispatch_source_cancel(timeout_timer); dispatch_release(timeout_timer); - } else -#endif - { - free(timeout_context); } +#endif return retVal; } @@ -2951,6 +2991,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter CF_BREAKPOINT_FUNCTION(void _CFRunLoopError_RunCalledWithInvalidMode(void)); SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); if (modeName == NULL || modeName == kCFRunLoopCommonModes || CFEqual(modeName, kCFRunLoopCommonModes)) { static dispatch_once_t onceToken; @@ -2962,12 +3003,16 @@ SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterva } if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished; __CFRunLoopLock(rl); - CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false); + CFRunLoopModeRef currentMode = __CFRunLoopCopyMode(rl, modeName, false); if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) { - if (currentMode) { __CFRunLoopModeUnlock(currentMode); } + if (currentMode) { + __CFRunLoopModeUnlock(currentMode); + CFRelease(currentMode); + } __CFRunLoopUnlock(rl); return kCFRunLoopRunFinished; } + __CFRunLoopModeLock(currentMode); volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl); CFRunLoopModeRef previousMode = rl->_currentMode; rl->_currentMode = currentMode; @@ -2980,6 +3025,7 @@ SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterva if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); __CFRunLoopModeUnlock(currentMode); + CFRelease(currentMode); __CFRunLoopPopPerRunData(rl, previousPerRun); rl->_currentMode = previousMode; __CFRunLoopUnlock(rl); @@ -3000,20 +3046,28 @@ SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean } CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFStringRef modeName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); __CFRunLoopLock(rl); - CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false); + CFRunLoopModeRef rlm = __CFRunLoopCopyMode(rl, modeName, false); + if (rlm) { + __CFRunLoopModeLock(rlm); + } CFAbsoluteTime at = 0.0; CFRunLoopTimerRef nextTimer = (rlm && rlm->_timers && 0 < CFArrayGetCount(rlm->_timers)) ? (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, 0) : NULL; if (nextTimer) { at = CFRunLoopTimerGetNextFireDate(nextTimer); } - if (rlm) __CFRunLoopModeUnlock(rlm); + if (rlm) { + __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); + } __CFRunLoopUnlock(rl); return at; } Boolean CFRunLoopIsWaiting(CFRunLoopRef rl) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); return __CFRunLoopIsSleeping(rl); } @@ -3028,6 +3082,8 @@ void CFRunLoopWakeUp(CFRunLoopRef rl) { _CFRunLoopError_MainThreadHasExited(); return; } + // Temporarily relocating type check AFTER the above pointer comparison to CFRunLoopGetMain() to avoid 60187188. + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); // This lock is crucial to ignorable wakeups, do not remove it. __CFRunLoopLock(rl); @@ -3062,6 +3118,7 @@ void CFRunLoopWakeUp(CFRunLoopRef rl) { } void CFRunLoopStop(CFRunLoopRef rl) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); Boolean doWake = false; CHECK_FOR_FORK(); __CFRunLoopLock(rl); @@ -3079,10 +3136,12 @@ CF_EXPORT void _CFRunLoopStopMode(CFRunLoopRef rl, CFStringRef modeName) { CHECK_FOR_FORK(); CFRunLoopModeRef rlm; __CFRunLoopLock(rl); - rlm = __CFRunLoopFindMode(rl, modeName, true); + rlm = __CFRunLoopCopyMode(rl, modeName, true); if (NULL != rlm) { + __CFRunLoopModeLock(rlm); rlm->_stopped = true; __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } __CFRunLoopUnlock(rl); CFRunLoopWakeUp(rl); @@ -3103,12 +3162,17 @@ void CFRunLoopPerformBlock(CFRunLoopRef rl, CFTypeRef mode, void (^block)(void)) _CFRunLoopError_MainThreadHasExited(); return; } + // Temporarily relocating type check AFTER the above pointer comparison to CFRunLoopGetMain() to avoid 60187188. + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); + if (_kCFRuntimeIDCFString == CFGetTypeID(mode)) { mode = CFStringCreateCopy(kCFAllocatorSystemDefault, (CFStringRef)mode); __CFRunLoopLock(rl); // ensure mode exists - CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, (CFStringRef)mode, true); - if (currentMode) __CFRunLoopModeUnlock(currentMode); + CFRunLoopModeRef currentMode = __CFRunLoopCopyMode(rl, (CFStringRef)mode, true); + if (currentMode) { + CFRelease(currentMode); + } __CFRunLoopUnlock(rl); } else if (CFArrayGetTypeID() == CFGetTypeID(mode)) { CFIndex cnt = CFArrayGetCount((CFArrayRef)mode); @@ -3118,8 +3182,10 @@ void CFRunLoopPerformBlock(CFRunLoopRef rl, CFTypeRef mode, void (^block)(void)) __CFRunLoopLock(rl); // ensure modes exist for (CFIndex idx = 0; idx < cnt; idx++) { - CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, (CFStringRef)values[idx], true); - if (currentMode) __CFRunLoopModeUnlock(currentMode); + CFRunLoopModeRef currentMode = __CFRunLoopCopyMode(rl, (CFStringRef)values[idx], true); + if (currentMode) { + CFRelease(currentMode); + } } __CFRunLoopUnlock(rl); free(values); @@ -3131,8 +3197,10 @@ void CFRunLoopPerformBlock(CFRunLoopRef rl, CFTypeRef mode, void (^block)(void)) __CFRunLoopLock(rl); // ensure modes exist for (CFIndex idx = 0; idx < cnt; idx++) { - CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, (CFStringRef)values[idx], true); - if (currentMode) __CFRunLoopModeUnlock(currentMode); + CFRunLoopModeRef currentMode = __CFRunLoopCopyMode(rl, (CFStringRef)values[idx], true); + if (currentMode) { + CFRelease(currentMode); + } } __CFRunLoopUnlock(rl); free(values); @@ -3159,7 +3227,17 @@ void CFRunLoopPerformBlock(CFRunLoopRef rl, CFTypeRef mode, void (^block)(void)) __CFRunLoopUnlock(rl); } +Boolean _CFRunLoopPerCalloutAutoreleasepoolEnabled(void) { + return CFRunLoopGetCurrent()->_perCalloutARP; +} + +Boolean _CFRunLoopSetPerCalloutAutoreleasepoolEnabled(Boolean enabled) { + CFRunLoopRef rl = CFRunLoopGetCurrent(); + return rl->_perCalloutARP = enabled; +} + Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); CFRunLoopModeRef rlm; Boolean hasValue = false; @@ -3169,10 +3247,12 @@ Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStrin hasValue = CFSetContainsValue(rl->_commonModeItems, rls); } } else { - rlm = __CFRunLoopFindMode(rl, modeName, false); + rlm = __CFRunLoopCopyMode(rl, modeName, false); if (NULL != rlm) { - hasValue = (rlm->_sources0 ? CFSetContainsValue(rlm->_sources0, rls) : false) || (rlm->_sources1 ? CFSetContainsValue(rlm->_sources1, rls) : false); + __CFRunLoopModeLock(rlm); + hasValue = (rlm->_sources0 ? CFSetContainsValue(rlm->_sources0, rls) : false) || (rlm->_sources1 ? CFSetContainsValue(rlm->_sources1, rls) : false); __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); @@ -3192,6 +3272,9 @@ void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef mod _CFRunLoopError_MainThreadHasExited(); return; } + // Temporarily relocating type check AFTER the above pointer comparison to CFRunLoopGetMain() to avoid 60187188. + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); + #if TARGET_OS_MAC // Preflight Version-1 ports to make sure their mach port has a RECV right. @@ -3225,7 +3308,10 @@ void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef mod CFRelease(set); } } else { - CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true); + CFRunLoopModeRef rlm = __CFRunLoopCopyMode(rl, modeName, true); + if (rlm) { + __CFRunLoopModeLock(rlm); + } if (NULL != rlm && NULL == rlm->_sources0) { rlm->_sources0 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); rlm->_sources1 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks); @@ -3256,6 +3342,7 @@ void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef mod } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); @@ -3269,6 +3356,7 @@ void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef mod } void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */ + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); Boolean doVer0Callout = false, doRLSRelease = false; __CFRunLoopLock(rl); @@ -3285,7 +3373,10 @@ void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef } else { } } else { - CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false); + CFRunLoopModeRef rlm = __CFRunLoopCopyMode(rl, modeName, false); + if (rlm) { + __CFRunLoopModeLock(rlm); + } if (NULL != rlm && ((NULL != rlm->_sources0 && CFSetContainsValue(rlm->_sources0, rls)) || (NULL != rlm->_sources1 && CFSetContainsValue(rlm->_sources1, rls)))) { CFRetain(rls); if (1 == rls->_context.version0.version) { @@ -3311,6 +3402,7 @@ void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); @@ -3351,7 +3443,10 @@ static void __CFRunLoopRemoveAllSources(CFRunLoopRef rl, CFStringRef modeName) { } else { } } else { - rlm = __CFRunLoopFindMode(rl, modeName, false); + rlm = __CFRunLoopCopyMode(rl, modeName, false); + if (rlm) { + __CFRunLoopModeLock(rlm); + } if (NULL != rlm && NULL != rlm->_sources0) { CFSetRef set = CFSetCreateCopy(kCFAllocatorSystemDefault, rlm->_sources0); CFTypeRef context[2] = {rl, modeName}; @@ -3366,12 +3461,14 @@ static void __CFRunLoopRemoveAllSources(CFRunLoopRef rl, CFStringRef modeName) { } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); } Boolean CFRunLoopContainsObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); CFRunLoopModeRef rlm; Boolean hasValue = false; @@ -3381,12 +3478,16 @@ Boolean CFRunLoopContainsObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFS hasValue = CFSetContainsValue(rl->_commonModeItems, rlo); } } else { - rlm = __CFRunLoopFindMode(rl, modeName, false); + rlm = __CFRunLoopCopyMode(rl, modeName, false); + if (rlm) { + __CFRunLoopModeLock(rlm); + } if (NULL != rlm && NULL != rlm->_observers) { hasValue = CFArrayContainsValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo); } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); @@ -3405,6 +3506,9 @@ void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef _CFRunLoopError_MainThreadHasExited(); return; } + // Temporarily relocating type check AFTER the above pointer comparison to CFRunLoopGetMain() to avoid 60187188. + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); + if (!__CFIsValid(rlo) || (NULL != rlo->_runLoop && rlo->_runLoop != rl)) return; __CFRunLoopLock(rl); if (modeName == kCFRunLoopCommonModes) { @@ -3420,7 +3524,10 @@ void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef CFRelease(set); } } else { - rlm = __CFRunLoopFindMode(rl, modeName, true); + rlm = __CFRunLoopCopyMode(rl, modeName, true); + if (rlm) { + __CFRunLoopModeLock(rlm); + } if (NULL != rlm && NULL == rlm->_observers) { rlm->_observers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); } @@ -3442,12 +3549,14 @@ void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); } void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); CFRunLoopModeRef rlm; __CFRunLoopLock(rl); @@ -3464,7 +3573,10 @@ void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFString } else { } } else { - rlm = __CFRunLoopFindMode(rl, modeName, false); + rlm = __CFRunLoopCopyMode(rl, modeName, false); + if (rlm) { + __CFRunLoopModeLock(rlm); + } if (NULL != rlm && NULL != rlm->_observers) { CFRetain(rlo); CFIndex idx = CFArrayGetFirstIndexOfValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo); @@ -3476,12 +3588,14 @@ void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFString } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); } Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); if (NULL == rlt->_runLoop || rl != rlt->_runLoop) return false; Boolean hasValue = false; @@ -3491,20 +3605,22 @@ Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringR hasValue = CFSetContainsValue(rl->_commonModeItems, rlt); } } else { - CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false); + CFRunLoopModeRef rlm = __CFRunLoopCopyMode(rl, modeName, false); if (NULL != rlm) { + __CFRunLoopModeLock(rlm); if (NULL != rlm->_timers) { CFIndex idx = CFArrayGetFirstIndexOfValue(rlm->_timers, CFRangeMake(0, CFArrayGetCount(rlm->_timers)), rlt); hasValue = (kCFNotFound != idx); } __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); return hasValue; } -void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { +void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return; if (__CFMainThreadHasExited && rl == CFRunLoopGetMain()) { @@ -3515,6 +3631,9 @@ void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeN _CFRunLoopError_MainThreadHasExited(); return; } + // Temporarily relocating type check AFTER the above pointer comparison to CFRunLoopGetMain() to avoid 60187188. + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); + if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return; __CFRunLoopLock(rl); if (modeName == kCFRunLoopCommonModes) { @@ -3530,8 +3649,9 @@ void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeN CFRelease(set); } } else { - CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true); + CFRunLoopModeRef rlm = __CFRunLoopCopyMode(rl, modeName, true); if (NULL != rlm) { + __CFRunLoopModeLock(rlm); if (NULL == rlm->_timers) { CFArrayCallBacks cb = kCFTypeArrayCallBacks; cb.equal = NULL; @@ -3545,6 +3665,7 @@ void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeN } else if (rl != rlt->_runLoop) { __CFRunLoopTimerUnlock(rlt); __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); __CFRunLoopUnlock(rl); return; } @@ -3561,12 +3682,14 @@ void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeN } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); } void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoop, rl); CHECK_FOR_FORK(); __CFRunLoopLock(rl); if (modeName == kCFRunLoopCommonModes) { @@ -3582,10 +3705,11 @@ void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef mo } else { } } else { - CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false); + CFRunLoopModeRef rlm = __CFRunLoopCopyMode(rl, modeName, false); CFIndex idx = kCFNotFound; CFMutableArrayRef timerList = NULL; if (NULL != rlm) { + __CFRunLoopModeLock(rlm); timerList = rlm->_timers; if (NULL != timerList) { idx = CFArrayGetFirstIndexOfValue(timerList, CFRangeMake(0, CFArrayGetCount(timerList)), rlt); @@ -3603,6 +3727,7 @@ void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef mo } if (NULL != rlm) { __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); } } __CFRunLoopUnlock(rl); @@ -3717,6 +3842,7 @@ CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order } CFIndex CFRunLoopSourceGetOrder(CFRunLoopSourceRef rls) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopSource, rls); CHECK_FOR_FORK(); __CFGenericValidateType(rls, CFRunLoopSourceGetTypeID()); return rls->_order; @@ -3749,6 +3875,7 @@ static void __CFRunLoopSourceRemoveFromRunLoop(const void *value, void *context) } void CFRunLoopSourceInvalidate(CFRunLoopSourceRef rls) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopSource, rls); CHECK_FOR_FORK(); __CFGenericValidateType(rls, CFRunLoopSourceGetTypeID()); __CFRunLoopSourceLock(rls); @@ -3776,12 +3903,14 @@ void CFRunLoopSourceInvalidate(CFRunLoopSourceRef rls) { } Boolean CFRunLoopSourceIsValid(CFRunLoopSourceRef rls) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopSource, rls); CHECK_FOR_FORK(); __CFGenericValidateType(rls, CFRunLoopSourceGetTypeID()); return __CFIsValid(rls); } void CFRunLoopSourceGetContext(CFRunLoopSourceRef rls, CFRunLoopSourceContext *context) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopSource, rls); CHECK_FOR_FORK(); __CFGenericValidateType(rls, CFRunLoopSourceGetTypeID()); CFAssert1(0 == context->version || 1 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0 or 1", __PRETTY_FUNCTION__); @@ -3798,6 +3927,7 @@ void CFRunLoopSourceGetContext(CFRunLoopSourceRef rls, CFRunLoopSourceContext *c } void CFRunLoopSourceSignal(CFRunLoopSourceRef rls) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopSource, rls); CHECK_FOR_FORK(); __CFRunLoopSourceLock(rls); if (__CFIsValid(rls)) { @@ -3809,6 +3939,7 @@ void CFRunLoopSourceSignal(CFRunLoopSourceRef rls) { } Boolean CFRunLoopSourceIsSignalled(CFRunLoopSourceRef rls) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopSource, rls); CHECK_FOR_FORK(); __CFRunLoopSourceLock(rls); Boolean ret = __CFRunLoopSourceIsSignaled(rls) ? true : false; @@ -3817,6 +3948,7 @@ Boolean CFRunLoopSourceIsSignalled(CFRunLoopSourceRef rls) { } CF_PRIVATE void _CFRunLoopSourceWakeUpRunLoops(CFRunLoopSourceRef rls) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopSource, rls); CFBagRef loops = NULL; __CFRunLoopSourceLock(rls); if (__CFIsValid(rls) && NULL != rls->_runLoops) { @@ -3928,26 +4060,26 @@ CFRunLoopObserverRef CFRunLoopObserverCreateWithHandler(CFAllocatorRef allocator } CFOptionFlags CFRunLoopObserverGetActivities(CFRunLoopObserverRef rlo) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopObserver, rlo); CHECK_FOR_FORK(); - __CFGenericValidateType(rlo, CFRunLoopObserverGetTypeID()); return rlo->_activities; } CFIndex CFRunLoopObserverGetOrder(CFRunLoopObserverRef rlo) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopObserver, rlo); CHECK_FOR_FORK(); - __CFGenericValidateType(rlo, CFRunLoopObserverGetTypeID()); return rlo->_order; } Boolean CFRunLoopObserverDoesRepeat(CFRunLoopObserverRef rlo) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopObserver, rlo); CHECK_FOR_FORK(); - __CFGenericValidateType(rlo, CFRunLoopObserverGetTypeID()); return __CFRunLoopObserverRepeats(rlo); } void CFRunLoopObserverInvalidate(CFRunLoopObserverRef rlo) { /* DOES CALLOUT */ + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopObserver, rlo); CHECK_FOR_FORK(); - __CFGenericValidateType(rlo, CFRunLoopObserverGetTypeID()); __CFRunLoopObserverLock(rlo); CFRetain(rlo); if (__CFIsValid(rlo)) { @@ -3986,13 +4118,14 @@ void CFRunLoopObserverInvalidate(CFRunLoopObserverRef rlo) { /* DOES CALLOUT } Boolean CFRunLoopObserverIsValid(CFRunLoopObserverRef rlo) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopObserver, rlo); CHECK_FOR_FORK(); return __CFIsValid(rlo); } void CFRunLoopObserverGetContext(CFRunLoopObserverRef rlo, CFRunLoopObserverContext *context) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopObserver, rlo); CHECK_FOR_FORK(); - __CFGenericValidateType(rlo, CFRunLoopObserverGetTypeID()); CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); *context = rlo->_context; } @@ -4140,6 +4273,7 @@ CFAbsoluteTime CFRunLoopTimerGetNextFireDate(CFRunLoopTimerRef rlt) { } void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDate) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopTimer, rlt); CHECK_FOR_FORK(); if (!__CFIsValid(rlt)) return; if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT; @@ -4170,7 +4304,7 @@ void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDat __CFRunLoopLock(rl); for (CFIndex idx = 0; idx < cnt; idx++) { CFStringRef name = (CFStringRef)modes[idx]; - modes[idx] = __CFRunLoopFindMode(rl, name, false); + modes[idx] = __CFRunLoopCopyMode(rl, name, false); CFRelease(name); } __CFLock(&rl->_timerTSRLock); @@ -4184,7 +4318,11 @@ void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDat } __CFUnlock(&rl->_timerTSRLock); for (CFIndex idx = 0; idx < cnt; idx++) { - __CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]); + CFRunLoopModeRef rlm = (CFRunLoopModeRef)modes[idx]; + if (rlm) { + __CFRunLoopModeUnlock(rlm); + CFRelease(rlm); + } } __CFRunLoopUnlock(rl); // This is setting the date of a timer, not a direct @@ -4209,14 +4347,14 @@ CFTimeInterval CFRunLoopTimerGetInterval(CFRunLoopTimerRef rlt) { } Boolean CFRunLoopTimerDoesRepeat(CFRunLoopTimerRef rlt) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopTimer, rlt); CHECK_FOR_FORK(); - __CFGenericValidateType(rlt, CFRunLoopTimerGetTypeID()); return (0.0 < rlt->_interval); } CFIndex CFRunLoopTimerGetOrder(CFRunLoopTimerRef rlt) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopTimer, rlt); CHECK_FOR_FORK(); - __CFGenericValidateType(rlt, CFRunLoopTimerGetTypeID()); return rlt->_order; } @@ -4279,8 +4417,8 @@ Boolean CFRunLoopTimerIsValid(CFRunLoopTimerRef rlt) { } void CFRunLoopTimerGetContext(CFRunLoopTimerRef rlt, CFRunLoopTimerContext *context) { + CF_ASSERT_TYPE(_kCFRuntimeIDCFRunLoopTimer, rlt); CHECK_FOR_FORK(); - __CFGenericValidateType(rlt, CFRunLoopTimerGetTypeID()); CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); *context = rlt->_context; } diff --git a/CoreFoundation/RunLoop.subproj/CFSocket.c b/CoreFoundation/RunLoop.subproj/CFSocket.c index cbd3b99653..31add9ef64 100644 --- a/CoreFoundation/RunLoop.subproj/CFSocket.c +++ b/CoreFoundation/RunLoop.subproj/CFSocket.c @@ -1716,6 +1716,7 @@ CFSocketRef CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHan } void CFSocketInvalidate(CFSocketRef s) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); UInt32 previousSocketManagerIteration; __CFGenericValidateType(s, CFSocketGetTypeID()); @@ -1799,21 +1800,21 @@ void CFSocketInvalidate(CFSocketRef s) { } Boolean CFSocketIsValid(CFSocketRef s) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); - __CFGenericValidateType(s, CFSocketGetTypeID()); return __CFSocketIsValid(s); } CFSocketNativeHandle CFSocketGetNative(CFSocketRef s) { + CF_ASSERT_TYPE_OR_NULL(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); - __CFGenericValidateType(s, CFSocketGetTypeID()); return s == NULL? -1 : s->_socket; } CFDataRef CFSocketCopyAddress(CFSocketRef s) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); CFDataRef result = NULL; - __CFGenericValidateType(s, CFSocketGetTypeID()); __CFSocketLock(s); __CFSocketEstablishAddress(s); if (NULL != s->_address) { @@ -1833,9 +1834,9 @@ CFDataRef CFSocketCopyAddress(CFSocketRef s) { } CFDataRef CFSocketCopyPeerAddress(CFSocketRef s) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); CFDataRef result = NULL; - __CFGenericValidateType(s, CFSocketGetTypeID()); __CFSocketLock(s); __CFSocketEstablishPeerAddress(s); if (NULL != s->_peerAddress) { @@ -1855,21 +1856,21 @@ CFDataRef CFSocketCopyPeerAddress(CFSocketRef s) { } void CFSocketGetContext(CFSocketRef s, CFSocketContext *context) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); - __CFGenericValidateType(s, CFSocketGetTypeID()); CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); *context = s->_context; } CFOptionFlags CFSocketGetSocketFlags(CFSocketRef s) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); - __CFGenericValidateType(s, CFSocketGetTypeID()); return s->_f.client; } void CFSocketSetSocketFlags(CFSocketRef s, CFOptionFlags flags) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); - __CFGenericValidateType(s, CFSocketGetTypeID()); __CFSocketLock(s); #if LOG_CFSOCKET CFOptionFlags oldFlags = s->_f.client; @@ -1880,10 +1881,10 @@ void CFSocketSetSocketFlags(CFSocketRef s, CFOptionFlags flags) { } void CFSocketDisableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); Boolean wakeup = false; uint8_t readCallBackType; - __CFGenericValidateType(s, CFSocketGetTypeID()); __CFSocketLock(s); if (__CFSocketIsValid(s) && __CFSocketIsScheduled(s)) { callBackTypes &= __CFSocketCallBackTypes(s); @@ -1971,8 +1972,8 @@ void __CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes, Boole } void CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); - __CFGenericValidateType(s, CFSocketGetTypeID()); __CFSocketLock(s); __CFSocketEnableCallBacks(s, callBackTypes, TRUE, 'r'); __CFSOCKETLOG_WS(s, "done for callbackTypes %x", callBackTypes); @@ -2192,9 +2193,9 @@ static void __CFSocketPerformV0(void *info) { } CFRunLoopSourceRef CFSocketCreateRunLoopSource(CFAllocatorRef allocator, CFSocketRef s, CFIndex order) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); CFRunLoopSourceRef result = NULL; - __CFGenericValidateType(s, CFSocketGetTypeID()); __CFSocketLock(s); if (__CFSocketIsValid(s)) { if (NULL != s->_source0 && !CFRunLoopSourceIsValid(s->_source0)) { @@ -2248,12 +2249,12 @@ CF_INLINE void __CFSocketWriteUnlock(CFSocketRef s) { //??? need timeout, error handling, retries CFSocketError CFSocketSendData(CFSocketRef s, CFDataRef address, CFDataRef data, CFTimeInterval timeout) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); const uint8_t *dataptr, *addrptr = NULL; SInt32 datalen, addrlen = 0, size = 0; CFSocketNativeHandle sock = INVALID_SOCKET; struct timeval tv; - __CFGenericValidateType(s, CFSocketGetTypeID()); if (address) { addrptr = CFDataGetBytePtr(address); addrlen = CFDataGetLength(address); @@ -2280,6 +2281,7 @@ CFSocketError CFSocketSendData(CFSocketRef s, CFDataRef address, CFDataRef data, } CFSocketError CFSocketSetAddress(CFSocketRef s, CFDataRef address) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); struct sockaddr *name; socklen_t namelen; @@ -2330,6 +2332,7 @@ CFSocketError CFSocketSetAddress(CFSocketRef s, CFDataRef address) { } CFSocketError CFSocketConnectToAddress(CFSocketRef s, CFDataRef address, CFTimeInterval timeout) { + CF_ASSERT_TYPE(CFSocketGetTypeID(), s); CHECK_FOR_FORK(); //??? need error handling, retries const uint8_t *name; diff --git a/CoreFoundation/Stream.subproj/CFConcreteStreams.c b/CoreFoundation/Stream.subproj/CFConcreteStreams.c index 338c9dae82..4cf2b208cd 100644 --- a/CoreFoundation/Stream.subproj/CFConcreteStreams.c +++ b/CoreFoundation/Stream.subproj/CFConcreteStreams.c @@ -23,6 +23,9 @@ #include #endif +// cb version must be > 0 +CF_PRIVATE struct _CFStream *_CFStreamCreateWithConstantCallbacks(CFAllocatorRef alloc, void *info, const struct _CFStreamCallBacks *cb, Boolean isReading); + #define SCHEDULE_AFTER_WRITE (0) #define SCHEDULE_AFTER_READ (1) @@ -75,9 +78,16 @@ static void _fs_release(void *info) } } -static void constructCFFD(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) { +static Boolean constructCFFD(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) { CFFileDescriptorContext context = {0, stream, _fs_retain, _fs_release, (void *)CFCopyDescription}; CFFileDescriptorRef cffd = CFFileDescriptorCreate(CFGetAllocator(stream), fileStream->fd, false, fileCallBack, &context); + if (cffd == NULL) { + if (fileStream->rlInfo.rlArray) { + CFRelease(fileStream->rlInfo.rlArray); + fileStream->rlInfo.rlArray = NULL; + } + return FALSE; + } CFFileDescriptorEnableCallBacks(cffd, forRead ? kCFFileDescriptorReadCallBack : kCFFileDescriptorWriteCallBack); if (fileStream->rlInfo.rlArray) { CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray); @@ -91,6 +101,7 @@ static void constructCFFD(_CFFileStreamContext *fileStream, Boolean forRead, str CFRelease(src); } fileStream->rlInfo.cffd = cffd; + return TRUE; } #endif @@ -101,7 +112,7 @@ static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *erro flags |= (_O_BINARY|_O_NOINHERIT); if (_CFURLGetWideFileSystemRepresentation(fileStream->url, TRUE, path, CFMaxPathSize) == FALSE) #else - char path[CFMaxPathSize]; + char path[CFMaxPathSize]; if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, (UInt8 *)path, CFMaxPathSize) == FALSE) #endif { @@ -128,7 +139,9 @@ static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *erro #ifdef REAL_FILE_SCHEDULING if (fileStream->rlInfo.rlArray != NULL) { - constructCFFD(fileStream, forRead, stream); + if (constructCFFD(fileStream, forRead, stream) == FALSE) { + break; + } } #endif @@ -727,11 +740,16 @@ static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { size += buf->length; } - bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0); - currByte = bytes; - for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { - memmove(currByte, buf->bytes, buf->length); - currByte += buf->length; + if (size > 0) { + bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0); + if (bytes == NULL) return NULL; + currByte = bytes; + for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { + memmove(currByte, buf->bytes, buf->length); + currByte += buf->length; + } + } else { + bytes = NULL; } return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc); } diff --git a/CoreFoundation/Stream.subproj/CFStream.c b/CoreFoundation/Stream.subproj/CFStream.c index 5cee669075..010f69bc13 100644 --- a/CoreFoundation/Stream.subproj/CFStream.c +++ b/CoreFoundation/Stream.subproj/CFStream.c @@ -742,8 +742,9 @@ static void _wakeUpRunLoop(struct _CFStream *stream) { } } } - if (NULL != rl && CFRunLoopIsWaiting(rl)) + if (NULL != rl) { CFRunLoopWakeUp(rl); + } CFRelease(rlArray); } } @@ -1320,6 +1321,8 @@ CF_EXPORT Boolean CFReadStreamSetClient(CFReadStreamRef readStream, CFOptionFlag else { _CFStreamDelegate* d = [[_CFStreamDelegate alloc] initWithStreamEvents:streamEvents callback:(void*) clientCB context:clientContext]; [is setDelegate:d]; + objc_setAssociatedObject(is, (void *)[_CFStreamDelegate class], d, OBJC_ASSOCIATION_RETAIN); + [d release]; } return true; } @@ -1343,6 +1346,8 @@ CF_EXPORT Boolean CFWriteStreamSetClient(CFWriteStreamRef writeStream, CFOptionF else { _CFStreamDelegate* d = [[_CFStreamDelegate alloc] initWithStreamEvents:streamEvents callback:(void*) clientCB context:clientContext]; [os setDelegate:d]; + objc_setAssociatedObject(os, (void *)[_CFStreamDelegate class], d, OBJC_ASSOCIATION_RETAIN); + [d release]; } return true; } @@ -1669,7 +1674,10 @@ extern _CFThreadRef _CFMainPThread; static void _perform(void* info) { #if TARGET_OS_MAC +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" OSAtomicAdd64(1, &sPerformCount); +#pragma GCC diagnostic pop #endif } @@ -1852,11 +1860,11 @@ static void waitForOpen(struct _CFStream *stream) { _CFStreamUnscheduleFromRunLoop(stream, runLoop, privateMode); } -CFArrayRef _CFReadStreamCopyRunLoopsAndModes(CFReadStreamRef readStream) { +CF_PRIVATE CFArrayRef _CFReadStreamCopyRunLoopsAndModes(CFReadStreamRef readStream) { return _CFStreamCopyRunLoopsAndModes((struct _CFStream *)readStream); } -CFArrayRef _CFWriteStreamCopyRunLoopsAndModes(CFWriteStreamRef writeStream) { +CF_PRIVATE CFArrayRef _CFWriteStreamCopyRunLoopsAndModes(CFWriteStreamRef writeStream) { return _CFStreamCopyRunLoopsAndModes((struct _CFStream *)writeStream); } diff --git a/CoreFoundation/Stream.subproj/CFStream.h b/CoreFoundation/Stream.subproj/CFStream.h index 4a80ff90a0..b3ed237862 100644 --- a/CoreFoundation/Stream.subproj/CFStream.h +++ b/CoreFoundation/Stream.subproj/CFStream.h @@ -327,11 +327,11 @@ CF_EXPORT const CFStringRef _Nonnull kCFStreamPropertyShouldCloseNativeSocket CF CF_IMPLICIT_BRIDGING_DISABLED /* Socket streams; the returned streams are paired such that they use the same socket; pass NULL if you want only the read stream or the write stream */ CF_EXPORT -void CFStreamCreatePairWithSocket(CFAllocatorRef _Null_unspecified alloc, CFSocketNativeHandle sock, CFReadStreamRef _Null_unspecified * _Null_unspecified readStream, CFWriteStreamRef _Null_unspecified * _Null_unspecified writeStream); +void CFStreamCreatePairWithSocket(CFAllocatorRef _Null_unspecified alloc, CFSocketNativeHandle sock, CFReadStreamRef _Null_unspecified * _Null_unspecified readStream, CFWriteStreamRef _Null_unspecified * _Null_unspecified writeStream) API_DEPRECATED("Use nw_connection_t in Network framework instead", macos(10.1, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED)); CF_EXPORT -void CFStreamCreatePairWithSocketToHost(CFAllocatorRef _Null_unspecified alloc, CFStringRef _Null_unspecified host, UInt32 port, CFReadStreamRef _Null_unspecified * _Null_unspecified readStream, CFWriteStreamRef _Null_unspecified * _Null_unspecified writeStream); +void CFStreamCreatePairWithSocketToHost(CFAllocatorRef _Null_unspecified alloc, CFStringRef _Null_unspecified host, UInt32 port, CFReadStreamRef _Null_unspecified * _Null_unspecified readStream, CFWriteStreamRef _Null_unspecified * _Null_unspecified writeStream) API_DEPRECATED("Use nw_connection_t in Network framework instead", macos(10.1, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED)); CF_EXPORT -void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef _Null_unspecified alloc, const CFSocketSignature * _Null_unspecified signature, CFReadStreamRef _Null_unspecified * _Null_unspecified readStream, CFWriteStreamRef _Null_unspecified * _Null_unspecified writeStream); +void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef _Null_unspecified alloc, const CFSocketSignature * _Null_unspecified signature, CFReadStreamRef _Null_unspecified * _Null_unspecified readStream, CFWriteStreamRef _Null_unspecified * _Null_unspecified writeStream) API_DEPRECATED("Use nw_connection_t in Network framework instead", macos(10.1, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED)); CF_IMPLICIT_BRIDGING_ENABLED diff --git a/CoreFoundation/Stream.subproj/CFStreamInternal.h b/CoreFoundation/Stream.subproj/CFStreamInternal.h index 693b081f0d..6e3a155f48 100644 --- a/CoreFoundation/Stream.subproj/CFStreamInternal.h +++ b/CoreFoundation/Stream.subproj/CFStreamInternal.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include "CFInternal.h" #include CF_EXTERN_C_BEGIN diff --git a/CoreFoundation/Stream.subproj/CFStreamPriv.h b/CoreFoundation/Stream.subproj/CFStreamPriv.h index e2a6c8ce64..f4a24a8846 100644 --- a/CoreFoundation/Stream.subproj/CFStreamPriv.h +++ b/CoreFoundation/Stream.subproj/CFStreamPriv.h @@ -55,19 +55,6 @@ struct _CFStream; CF_EXPORT void* _CFStreamGetInfoPointer(struct _CFStream* stream); -#if !defined(CF_PRIVATE) -#define CF_PRIVATE extern __attribute__((__visibility__("hidden"))) -#endif - -// cb version must be > 0 -CF_PRIVATE struct _CFStream *_CFStreamCreateWithConstantCallbacks(CFAllocatorRef alloc, void *info, const struct _CFStreamCallBacks *cb, Boolean isReading); - -// Returns an array of the runloops and modes on which the stream is currently scheduled -CF_PRIVATE -CFArrayRef _CFReadStreamCopyRunLoopsAndModes(CFReadStreamRef readStream); -CF_PRIVATE -CFArrayRef _CFWriteStreamCopyRunLoopsAndModes(CFWriteStreamRef writeStream); - CF_EXPORT CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd); diff --git a/CoreFoundation/String.subproj/CFBurstTrie.c b/CoreFoundation/String.subproj/CFBurstTrie.c index 5db7d7fa6b..da4928e51a 100644 --- a/CoreFoundation/String.subproj/CFBurstTrie.c +++ b/CoreFoundation/String.subproj/CFBurstTrie.c @@ -248,6 +248,7 @@ struct _CFBurstTrie { uint32_t count; uint32_t containerSize; int retain; + bool isMmapped; #if TARGET_OS_WIN32 HANDLE mapHandle; HANDLE mappedFileHandle; @@ -377,6 +378,7 @@ CFBurstTrieRef CFBurstTrieCreateFromFile(CFStringRef path) { trie->cflags = CFSwapInt32LittleToHost(((fileHeader*)trie->mapBase)->flags); trie->count = CFSwapInt32LittleToHost(((fileHeader*)trie->mapBase)->count); trie->retain = 1; + trie->isMmapped = true; #if TARGET_OS_WIN32 trie->mappedFileHandle = mappedFileHandle; trie->mapHandle = mapHandle; @@ -391,6 +393,7 @@ CFBurstTrieRef CFBurstTrieCreateFromFile(CFStringRef path) { trie->cflags = CFSwapInt32LittleToHost(header->flags); trie->count = CFSwapInt32LittleToHost(header->count); trie->retain = 1; + trie->isMmapped = true; #if TARGET_OS_WIN32 trie->mappedFileHandle = mappedFileHandle; trie->mapHandle = mapHandle; @@ -416,6 +419,7 @@ CFBurstTrieRef CFBurstTrieCreateFromMapBytes(char *mapBase) { trie->cflags = CFSwapInt32LittleToHost(((fileHeader*)trie->mapBase)->flags); trie->count = CFSwapInt32LittleToHost(((fileHeader*)trie->mapBase)->count); trie->retain = 1; + trie->isMmapped = false; } else if (mapBase && (header->signature == 0xcafebabe || header->signature == 0x0ddba11)) { trie = (CFBurstTrieRef) malloc(sizeof(struct _CFBurstTrie)); trie->mapBase = mapBase; @@ -423,6 +427,7 @@ CFBurstTrieRef CFBurstTrieCreateFromMapBytes(char *mapBase) { trie->cflags = CFSwapInt32LittleToHost(header->flags); trie->count = CFSwapInt32LittleToHost(header->count); trie->retain = 1; + trie->isMmapped = false; } return trie; } @@ -1987,7 +1992,7 @@ static size_t serializeCFBurstTrie(CFBurstTrieRef trie, size_t start_offset, int #endif static void destroyCFBurstTrie(CFBurstTrieRef trie) { - if (trie->mapBase) { + if (trie->mapBase && trie->isMmapped) { #if TARGET_OS_WIN32 UnmapViewOfFile(trie->mapBase); CloseHandle(trie->mapHandle); @@ -1995,7 +2000,7 @@ static void destroyCFBurstTrie(CFBurstTrieRef trie) { #else munmap(trie->mapBase, trie->mapSize); #endif - } else { + } else if (!trie->mapBase) { finalizeCFBurstTrie(&trie->root); } free(trie); diff --git a/CoreFoundation/String.subproj/CFCharacterSet.c b/CoreFoundation/String.subproj/CFCharacterSet.c index e1419be16c..e8e540703d 100644 --- a/CoreFoundation/String.subproj/CFCharacterSet.c +++ b/CoreFoundation/String.subproj/CFCharacterSet.c @@ -37,6 +37,10 @@ */ #define __kCFStringCharSetMax 64 +/* The first builtin set ID number +*/ +#define __kCFFirstBuiltinSetID kCFCharacterSetControl + /* The last builtin set ID number */ #define __kCFLastBuiltinSetID kCFCharacterSetNewline @@ -177,22 +181,27 @@ CF_INLINE void __CFCSetPutCompactBitmapBits(CFMutableCharacterSetRef cset, uint8 /* Validation funcs */ -#if defined(CF_ENABLE_ASSERTIONS) + CF_INLINE void __CFCSetValidateBuiltinType(CFCharacterSetPredefinedSet type, const char *func) { - CFAssert2(type > 0 && type <= __kCFLastBuiltinSetID, __kCFLogAssertion, "%s: Unknowen builtin type %d", func, type); -} -CF_INLINE void __CFCSetValidateRange(CFRange theRange, const char *func) { - CFAssert3(theRange.location >= 0 && theRange.location + theRange.length <= 0x1FFFFF, __kCFLogAssertion, "%s: Range out of Unicode range (location -> %d length -> %d)", func, theRange.location, theRange.length); + if (type < __kCFFirstBuiltinSetID || type > __kCFLastBuiltinSetID) { + CFLog(__kCFLogAssertion, CFSTR("%s: Unknown builtin type %ld"), func, (long)type); + HALT_MSG("Unknown builtin CFCharacterSet type"); + } } CF_INLINE void __CFCSetValidateTypeAndMutability(CFCharacterSetRef cset, const char *func) { __CFGenericValidateType(cset, _kCFRuntimeIDCFCharacterSet); - CFAssert1(__CFCSetIsMutable(cset), __kCFLogAssertion, "%s: Immutable character set passed to mutable function", func); + if (!__CFCSetIsMutable(cset)) { + CFLog(__kCFLogAssertion, CFSTR("%s: Immutable character set passed to mutable function"), func); + HALT_MSG("Immutable character set passed to mutable function"); + } +} + +CF_INLINE void __CFCSetValidateRange(CFRange theRange, const char *func) { + if (theRange.location < UCHAR_MIN_VALUE || theRange.location > UCHAR_MAX_VALUE || theRange.length > UCHAR_MAX_VALUE + 1 || theRange.location + theRange.length < UCHAR_MIN_VALUE || theRange.location + theRange.length > UCHAR_MAX_VALUE + 1) { + CFLog(__kCFLogAssertion, CFSTR("%s: Range (location: %ld, length: %ld) outside of valid Unicode range (0x0 - 0x10FFFF)"), func, (long)theRange.location, (long)theRange.length); + HALT_MSG("CFCharacterSet range is outside of valid Unicode range (0x0 - 0x10FFFF)"); + } } -#else -#define __CFCSetValidateBuiltinType(t,f) -#define __CFCSetValidateRange(r,f) -#define __CFCSetValidateTypeAndMutability(r,f) -#endif /* Inline utility funcs */ @@ -1362,8 +1371,6 @@ CFCharacterSetRef CFCharacterSetGetPredefined(CFCharacterSetPredefinedSet theSet __CFCSetValidateBuiltinType(theSetIdentifier, __PRETTY_FUNCTION__); - if ((theSetIdentifier < kCFCharacterSetControl) || (theSetIdentifier > __kCFLastBuiltinSetID)) { return NULL; } - _CFCharacterSetLockGlobal(); cset = __CFBuiltinSets[theSetIdentifier - 1]; _CFCharacterSetUnlockGlobal(); @@ -1371,11 +1378,11 @@ CFCharacterSetRef CFCharacterSetGetPredefined(CFCharacterSetPredefinedSet theSet if (NULL != cset) return cset; if (!(cset = __CFCSetGenericCreate(kCFAllocatorSystemDefault, __kCFCharSetClassBuiltin, false))) return NULL; + __CFCSetPutBuiltinType((CFMutableCharacterSetRef)cset, theSetIdentifier); //make builtin _CFCharacterSetLockGlobal(); if (__CFBuiltinSets[theSetIdentifier - 1] == NULL) { __CFBuiltinSets[theSetIdentifier - 1] = cset; - __CFCSetPutBuiltinType((CFMutableCharacterSetRef)cset, theSetIdentifier); //make builtin _CFCharacterSetUnlockGlobal(); } else { CFCharacterSetRef tmp = cset; @@ -1389,6 +1396,8 @@ CFCharacterSetRef CFCharacterSetGetPredefined(CFCharacterSetPredefinedSet theSet #if DEPLOYMENT_RUNTIME_SWIFT Boolean _CFCharacterSetInitWithCharactersInRange(CFMutableCharacterSetRef cset, CFRange theRange) { + __CFCSetValidateRange(theRange, __PRETTY_FUNCTION__); + if (theRange.length) { if (!__CFCSetGenericInit(cset, __kCFCharSetClassRange, false)) return false; __CFCSetPutRangeFirstChar(cset, theRange.location); @@ -1403,9 +1412,9 @@ Boolean _CFCharacterSetInitWithCharactersInRange(CFMutableCharacterSetRef cset, #endif CFCharacterSetRef CFCharacterSetCreateWithCharactersInRange(CFAllocatorRef allocator, CFRange theRange) { - CFMutableCharacterSetRef cset; - __CFCSetValidateRange(theRange, __PRETTY_FUNCTION__); + + CFMutableCharacterSetRef cset; if (theRange.length) { if (!(cset = __CFCSetGenericCreate(allocator, __kCFCharSetClassRange, false))) return NULL; diff --git a/CoreFoundation/String.subproj/CFString.c b/CoreFoundation/String.subproj/CFString.c index 53a1e6cc5b..ad23980722 100644 --- a/CoreFoundation/String.subproj/CFString.c +++ b/CoreFoundation/String.subproj/CFString.c @@ -46,6 +46,13 @@ #endif +CONST_STRING_DECL(_kCFStringFormatMetadataReplacementIndexKey, "Index"); +CONST_STRING_DECL(_kCFStringFormatMetadataSpecifierRangeLocationInFormatStringKey, "SpecLocation"); +CONST_STRING_DECL(_kCFStringFormatMetadataSpecifierRangeLengthInFormatStringKey, "SpecLength"); +CONST_STRING_DECL(_kCFStringFormatMetadataReplacementRangeLocationKey, "ReplacementLocation"); +CONST_STRING_DECL(_kCFStringFormatMetadataReplacementRangeLengthKey, "ReplacementLength"); +CONST_STRING_DECL(_kCFStringFormatMetadataArgumentObjectKey, "Object"); +CONST_STRING_DECL(_kCFStringFormatMetadataArgumentNumberKey, "Number"); #define USE_STRING_ROM 0 @@ -111,7 +118,7 @@ extern void __CFStrConvertBytesToUnicode(const uint8_t *bytes, UniChar *buffer, CF_PRIVATE uint32_t CFUniCharGetConditionalCaseMappingFlags(UTF32Char theChar, UTF16Char *buffer, CFIndex currentIndex, CFIndex length, uint32_t type, const uint8_t *langCode, uint32_t lastFlags); -static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, CFDictionaryRef stringsDictConfig, CFStringRef validFormatSpecifiers, CFStringRef formatString, CFIndex initialArgPosition, const void *origValues, CFIndex originalValuesSize, va_list args, CFErrorRef *errorPtr); +static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, CFDictionaryRef stringsDictConfig, CFStringRef validFormatSpecifiers, CFStringRef formatString, CFIndex initialArgPosition, const void *origValues, CFIndex originalValuesSize, va_list args, CFArrayRef *outReplacementMetadata, CFErrorRef *errorPtr); static inline const char * _CFStringGetCStringPtrInternal(CFStringRef str, CFStringEncoding encoding, Boolean requiresNullTermination, Boolean requiresBridgingCheck); @@ -639,6 +646,13 @@ CF_INLINE Boolean __CFCanUseLengthByte(CFIndex len) { #define __CFAssertIsNotNegative(idx) CFAssert2(idx >= 0, __kCFLogAssertion, "%s(): index %ld is negative", __PRETTY_FUNCTION__, idx) #define __CFAssertIfFixedLengthIsOK(cf, reqLen) CFAssert2(!__CFStrIsFixed(cf) || (reqLen <= __CFStrDesiredCapacity(cf)), __kCFLogAssertion, "%s(): length %ld too large", __PRETTY_FUNCTION__, reqLen) +#define CF_RETURN_IF_NOT_MUTABLE(cf) do { \ + if(!__CFStrIsMutable(cf)) {\ + fprintf(stderr, "CFString: %s(): Expect mutable string\n", __PRETTY_FUNCTION__);\ + return;\ + } \ +} while (0) + /* Basic algorithm is to shrink memory when capacity is SHRINKFACTOR times the required capacity or to allocate memory when the capacity is less than GROWFACTOR times the required capacity. This function will return -1 if the new capacity is just too big (> LONG_MAX). Additional complications are applied in the following order: @@ -1451,13 +1465,15 @@ CF_PRIVATE CFStringRef __CFStringCreateImmutableFunnel3( #if USE_STRING_ROM CFStringRef romResult = NULL; - if (stringSupportsROM) { // Disable the string ROM if necessary - static char sDisableStringROM = -1; - if (sDisableStringROM == -1) sDisableStringROM = !! getenv("CFStringDisableROM"); + static Boolean sDisableStringROM = false; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sDisableStringROM = getenv("CFStringDisableROM") != NULL; + }); - if (sDisableStringROM == 0) romResult = __CFSearchStringROM((const char *)realBytes, realNumBytes); + if (!sDisableStringROM) romResult = __CFSearchStringROM((const char *)realBytes, realNumBytes); } /* if we get a result from our ROM, and noCopy is set, then deallocate the buffer immediately */ if (romResult) { @@ -1648,7 +1664,7 @@ CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t * CFStringRef CFStringCreateStringWithValidatedFormat(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef validFormatSpecifiers, CFStringRef format, va_list arguments, CFErrorRef *errorPtr) { CFMutableStringRef outputString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); //should use alloc if no copy/release __CFStrSetDesiredCapacity(outputString, 120); // Given this will be tightened later, choosing a larger working string is fine - if (__CFStringAppendFormatCore(outputString, NULL, NULL, formatOptions, NULL, validFormatSpecifiers, format, 0, NULL, 0, arguments, errorPtr)) { + if (__CFStringAppendFormatCore(outputString, NULL, NULL, formatOptions, NULL, validFormatSpecifiers, format, 0, NULL, 0, arguments, NULL, errorPtr)) { // ??? copy/release should not be necessary here -- just make immutable, compress if possible // (However, this does make the string inline, and cause the supplied allocator to be used...) CFStringRef str = (CFStringRef)CFStringCreateCopy(alloc, outputString); @@ -1662,15 +1678,20 @@ CFStringRef CFStringCreateStringWithValidatedFormat(CFAllocatorRef alloc, CFDict } CFStringRef CFStringCreateWithFormatAndArguments(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments) { + return _CFStringCreateWithFormatAndArgumentsAux2(alloc, NULL, NULL, formatOptions, format, arguments); } CFStringRef _CFStringCreateWithFormatAndArgumentsAux2(CFAllocatorRef alloc, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool , bool *), CFDictionaryRef formatOptions, CFStringRef format, va_list arguments) { + return _CFStringCreateWithFormatAndArgumentsReturningMetadata(alloc, copyDescFunc, contextDescFunc, formatOptions, NULL, format, NULL, arguments); +} + +CFStringRef _CFStringCreateWithFormatAndArgumentsReturningMetadata(CFAllocatorRef alloc, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool , bool *), CFDictionaryRef formatOptions, CFDictionaryRef formatConfiguration, CFStringRef format, CFArrayRef *outMetadata, va_list arguments) { CFStringRef str = NULL; CFMutableStringRef outputString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); //should use alloc if no copy/release __CFStrSetDesiredCapacity(outputString, 120); // Given this will be tightened later, choosing a larger working string is fine CFErrorRef error; - if (__CFStringAppendFormatCore(outputString, copyDescFunc, contextDescFunc, formatOptions, NULL, NULL, format, 0, NULL, 0, arguments, &error)) { + if (__CFStringAppendFormatCore(outputString, copyDescFunc, contextDescFunc, formatOptions, formatConfiguration, NULL, format, 0, NULL, 0, arguments, outMetadata, &error)) { // ??? copy/release should not be necessary here -- just make immutable, compress if possible // (However, this does make the string inline, and cause the supplied allocator to be used...) str = (CFStringRef)CFStringCreateCopy(alloc, outputString); @@ -1714,7 +1735,7 @@ CFStringRef CFStringCreateWithSubstring(CFAllocatorRef alloc, CFStringRef str, C return __CFStringCreateImmutableFunnel3(alloc, contents + range.location, range.length * sizeof(UniChar), kCFStringEncodingUnicode, false, true, false, false, false, ALLOCATORSFREEFUNC, 0); } } - + static CFStringRef _CFStringSlowPathCopyBundleUnloadingProtectedString(CFStringRef str) { CFIndex const len = CFStringGetLength(str); if (len == 0) { @@ -1883,7 +1904,9 @@ CFStringRef __CFStringMakeConstantString(const char *cStr) { CFDictionaryAddValue(constantStringTable, key, result); if (CFDictionaryGetCount(constantStringTable) == count) { // add did nothing, someone already put it there result = (CFStringRef)CFDictionaryGetValue(constantStringTable, key); - } else if (!isTaggedPointerString) { + } else if (!isTaggedPointerString && !__CFRuntimeIsConstant(result)) { + // As of rdar://22175031 constant strings in CF are in read-only memory in the dyld shared cache + // So don't call this if __CFRuntimeIsConstant is true for cases like kCFEmptyString or strings in the string ROM __CFRuntimeSetRC(result, 0); } __CFUnlock(&_CFSTRLock); @@ -1929,7 +1952,9 @@ void __CFStringCleanup (void) { // Can pass in NSString as replacement string // Call with numRanges > 0, and incrementing ranges -static void __CFStringReplaceMultiple(CFMutableStringRef str, CFRange *ranges, CFIndex numRanges, CFStringRef replacement) { +static int __CFStringReplaceMultiple(CFMutableStringRef str, CFRange *ranges, CFIndex numRanges, CFStringRef replacement) { + if (!__CFStrIsMutable(str)) return _CFStringErrNotMutable; + int cnt; CFStringRef copy = NULL; if (replacement == str) copy = replacement = CFStringCreateCopy(kCFAllocatorSystemDefault, replacement); // Very special and hopefully rare case @@ -1960,6 +1985,7 @@ static void __CFStringReplaceMultiple(CFMutableStringRef str, CFRange *ranges, C } } if (copy) CFRelease(copy); + return _CFStringErrNone; } // Can pass in NSString as replacement string @@ -2064,7 +2090,7 @@ CF_PRIVATE void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len) { CFIndex CFStringGetLength(CFStringRef str) { CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFString, CFIndex, (CFSwiftRef)str, NSString.length); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, CFIndex, (NSString *)str, length); - + __CFAssertIsString(str); return __CFStrLength(str); } @@ -2290,7 +2316,10 @@ Boolean CFStringGetCString(CFStringRef str, char *buffer, CFIndex bufferSize, CF len = __CFStrLength2(str, contents); if (__CFStrIsEightBit(str) && ((__CFStringGetEightBitStringEncoding() == encoding) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII && __CFStringEncodingIsSupersetOfASCII(encoding)))) { // Requested encoding is equal to the encoding in string - if (len >= bufferSize) return false; + if (len >= bufferSize) { + buffer[0] = 0; + return false; + } memmove(buffer, contents + __CFStrSkipAnyLengthByte(str), len); buffer[len] = 0; return true; @@ -2311,17 +2340,27 @@ Boolean CFStringGetCString(CFStringRef str, char *buffer, CFIndex bufferSize, CF } } -extern Boolean __CFLocaleGetNullLocale(struct __CFLocale *locale); -extern void __CFLocaleSetNullLocale(struct __CFLocale *locale); +extern Boolean __CFLocaleGetDoesNotRequireSpecialCaseHandling(struct __CFLocale *locale); +extern void __CFLocaleSetDoesNotRequireSpecialCaseHandling(struct __CFLocale *locale); -static const char *_CFStrGetLanguageIdentifierForLocale(CFLocaleRef locale, bool collatorOnly) { +// Returns the language code if the given locale is one of the "special" languages that +// requires special handing during case mapping: +// - "az": Azerbaijani +// - "lt": Lithuanian +// - "tr": Turkish +// - "nl": Dutch +// - "el": Greek +// For all other locales such as en_US, this function returs NULL. +// See `CFUniCharMapCaseTo` +// See https://www.unicode.org/Public/UNIDATA/SpecialCasing.txt +static const char *_CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(CFLocaleRef locale, bool collatorOnly) { CFStringRef localeID; const char *langID = NULL; static const void *lastLocale = NULL; static const char *lastLangID = NULL; static CFLock_t lock = CFLockInit; - if (__CFLocaleGetNullLocale((struct __CFLocale *)locale)) return NULL; + if (__CFLocaleGetDoesNotRequireSpecialCaseHandling((struct __CFLocale *)locale)) return NULL; __CFLock(&lock); if ((NULL != lastLocale) && (lastLocale == locale)) { @@ -2330,15 +2369,12 @@ static const char *_CFStrGetLanguageIdentifierForLocale(CFLocaleRef locale, bool } __CFUnlock(&lock); - localeID = (CFStringRef)CFLocaleGetValue(locale, __kCFLocaleCollatorID); - CFIndex length = CFStringGetLength(localeID); - - if (!collatorOnly) { - if ((length < 2) || ((4 == length) && CFEqual(localeID, CFSTR("root")))) { - localeID = (CFStringRef)CFLocaleGetIdentifier(locale); - length = CFStringGetLength(localeID); - } + if (collatorOnly) { + localeID = (CFStringRef)CFLocaleGetValue(locale, __kCFLocaleCollatorID); + } else { + localeID = (CFStringRef)CFLocaleGetIdentifier(locale); } + CFIndex length = CFStringGetLength(localeID); if (length > 1) { uint8_t buffer[2]; @@ -2362,7 +2398,7 @@ static const char *_CFStrGetLanguageIdentifierForLocale(CFLocaleRef locale, bool } } - if (langID == NULL) __CFLocaleSetNullLocale((struct __CFLocale *)locale); + if (langID == NULL) __CFLocaleSetDoesNotRequireSpecialCaseHandling((struct __CFLocale *)locale); __CFLock(&lock); lastLocale = locale; @@ -2396,7 +2432,7 @@ CF_INLINE bool _CFCanUseLocale(CFLocaleRef locale) { // Returns the length of characters filled into outCharacters. If no change, returns 0. maxBufLen shoule be at least 8 -static CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStringInlineBuffer *buffer, CFIndex index, CFOptionFlags flags, const uint8_t *langCode, UTF32Char *outCharacters, CFIndex maxBufferLength, CFIndex *consumedLength) { +static CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStringInlineBuffer *buffer, CFIndex index, CFOptionFlags flags, const uint8_t *langCode, UTF32Char *outCharacters, CFIndex maxBufferLength, CFIndex *consumedLength, bool *insufficientBufferSpace) { CFIndex filledLength = 0, currentIndex = index; if (0 != character) { @@ -2447,6 +2483,8 @@ static CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStri } else if (0 == (flags & kCFCompareNonliteral)) { character = original; filledLength = 0; + } else if (filledLength == 0 && NULL != insufficientBufferSpace) { + *insufficientBufferSpace = true; } } } @@ -2544,7 +2582,11 @@ static CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStri if ((0 == (flags & kCFCompareDiacriticInsensitive)) || (nonBaseCharacter > 0x050F)) { if (CFUniCharIsMemberOfBitmap(nonBaseCharacter, decompBitmap)) { - filledLength += CFUniCharDecomposeCharacter(nonBaseCharacter, &(outCharacters[filledLength]), maxBufferLength - filledLength); + CFIndex decomposedLength = CFUniCharDecomposeCharacter(nonBaseCharacter, &(outCharacters[filledLength]), maxBufferLength - filledLength); + filledLength += decomposedLength; + if (decomposedLength == 0 && NULL != insufficientBufferSpace) { + *insufficientBufferSpace = true; + } } else { outCharacters[filledLength++] = nonBaseCharacter; } @@ -2556,6 +2598,7 @@ static CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStri } } + bool endedCharacterCluster = false; while (filledLength < maxBufferLength) { // do the rest character = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex); @@ -2585,9 +2628,14 @@ static CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStri } currentIndex += ((nonBaseBitmap == graphemeBMP) ? 1 : 2); } else { + endedCharacterCluster = true; break; } } + + if (!endedCharacterCluster && NULL != insufficientBufferSpace) { + *insufficientBufferSpace = true; + } if (filledLength > 1) { UTF32Char *sortCharactersLimit = outCharacters + filledLength; @@ -2612,7 +2660,10 @@ static bool __CFStringFillCharacterSetInlineBuffer(CFCharacterSetInlineBuffer *b if (NULL == nonAlnumChars) { CFMutableCharacterSetRef cset = CFCharacterSetCreateMutableCopy(kCFAllocatorSystemDefault, CFCharacterSetGetPredefined(kCFCharacterSetAlphaNumeric)); CFCharacterSetInvert(cset); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" if (!OSAtomicCompareAndSwapPtrBarrier(NULL, cset, (void **)&nonAlnumChars)) CFRelease(cset); +#pragma GCC diagnostic pop } CFCharacterSetInitInlineBuffer(nonAlnumChars, buffer); @@ -2624,6 +2675,37 @@ static bool __CFStringFillCharacterSetInlineBuffer(CFCharacterSetInlineBuffer *b } #define kCFStringStackBufferLength (__kCFStringInlineBufferLength) + +static const u_char __ASCII_LOWERCASE_TABLE[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, + + /* The A-Z range should become a-z */ + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + + 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, +}; + +// This function is an implementation of strncasecmp_l that does not stop comparing at embedded null bytes +// We are not calling to LibC APIs such as tolower_l here because calling to those APIs (as compared to using a lookup table) introduced significant performance regressions +CF_INLINE int __CFStringCompareASCIICaseInsensitive(const u_char *str1, const u_char *str2, size_t n) { + if (n != 0) { + do { + u_char a = __ASCII_LOWERCASE_TABLE[*str1++]; + u_char b = __ASCII_LOWERCASE_TABLE[*str2++]; + if (a != b) { + return a - b; + } + } while (--n != 0); + } + return 0; +} CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStringRef string2, CFRange rangeToCompare, CFStringCompareFlags compareOptions, CFLocaleRef locale) { /* No objc dispatch needed here since CFStringInlineBuffer works with both CFString and NSString */ @@ -2654,7 +2736,7 @@ CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStr freeLocale = true; } - langCode = ((NULL == locale) ? NULL : (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(locale, true)); + langCode = ((NULL == locale) ? NULL : (const uint8_t *)_CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(locale, true)); if (__CFStringFillCharacterSetInlineBuffer(&csetBuffer, compareOptions)) { ignoredChars = &csetBuffer; @@ -2672,7 +2754,8 @@ CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStr if ((kCFStringEncodingASCII == eightBitEncoding) && (false == forceOrdering)) { if (caseInsensitive) { - int cmpResult = strncasecmp_l((const char *)str1Bytes + rangeToCompare.location, (const char *)str2Bytes, __CFMin(rangeToCompare.length, str2Len), NULL); + // Here we call our own __CFStringCompareASCIICaseInsensitive rather than strncasecmp_l to continue comparing after embedded null bytes + int cmpResult = __CFStringCompareASCIICaseInsensitive(str1Bytes + rangeToCompare.location, str2Bytes, __CFMin(rangeToCompare.length, str2Len)); if (0 == cmpResult) cmpResult = rangeToCompare.length - str2Len; @@ -2755,6 +2838,9 @@ CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStr compareOptions &= ~kCFCompareDiacriticInsensitive; } } + + CFIndex preventStr1FoldingUntil = 0, preventStr2FoldingUntil = 0; + while ((str1Index < rangeToCompare.length) && (str2Index < str2Len)) { if (strBuf1Len == 0) { str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); @@ -2883,12 +2969,19 @@ CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStr } if (str1Char != str2Char) { - if (0 == strBuf1Len) { - strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, &str1UsedLen); + if (0 == strBuf1Len && (preventStr1FoldingUntil == 0 || preventStr1FoldingUntil == str1Index)) { + preventStr1FoldingUntil = 0; + bool insufficientBuffer = false; + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, &str1UsedLen, &insufficientBuffer); if (strBuf1Len > 0) { str1Char = *strBuf1; strBuf1Index = 1; } + if (insufficientBuffer) { + // We have a character cluster larger than our maximum folding size. This is likely a malformed string, so do not fold the remainder of this cluster + CFRange currentCluster = CFStringGetRangeOfCharacterClusterAtIndex(string, str1Index, kCFStringGraphemeCluster); + preventStr1FoldingUntil = currentCluster.location + currentCluster.length; + } } if ((0 == strBuf1Len) && (0 < strBuf2Len)) { @@ -2899,8 +2992,10 @@ CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStr return compareResult; } - if ((0 == strBuf2Len) && ((0 == strBuf1Len) || (str1Char != str2Char))) { - strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, &str2UsedLen); + if ((0 == strBuf2Len) && ((0 == strBuf1Len) || (str1Char != str2Char)) && (preventStr2FoldingUntil == 0 || preventStr2FoldingUntil == str2Index)) { + preventStr2FoldingUntil = 0; + bool insufficientBuffer = false; + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, &str2UsedLen, &insufficientBuffer); if (strBuf2Len > 0) { str2Char = *strBuf2; strBuf2Index = 1; @@ -2912,6 +3007,11 @@ CFComparisonResult CFStringCompareWithOptionsAndLocale(CFStringRef string, CFStr } return compareResult; } + if (insufficientBuffer) { + // We have a character cluster larger than our maximum folding size. This is likely a malformed string, so do not fold the remainder of this cluster + CFRange currentCluster = CFStringGetRangeOfCharacterClusterAtIndex(string2, str2Index, kCFStringGraphemeCluster); + preventStr2FoldingUntil = currentCluster.location + currentCluster.length; + } } } @@ -3008,6 +3108,8 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT CFIndex str1Index, str2Index; CFIndex strBuf1Len, strBuf2Len; CFIndex maxStr1Index = (rangeToSearch.location + rangeToSearch.length); + CFIndex lastStr1FoldIndex = 0, lastStr1FoldLength = 0, lastStr1FoldUsed = 0, preventStr1FoldingUntil = 0; + CFIndex lastStr2FoldIndex = 0, lastStr2FoldLength = 0, lastStr2FoldUsed = 0, preventStr2FoldingUntil = 0; bool equalityOptions = ((lengthVariants || (compareOptions & kCFCompareWidthInsensitive)) ? true : false); bool caseInsensitive = ((compareOptions & kCFCompareCaseInsensitive) ? true : false); bool forwardAnchor = ((kCFCompareAnchored == (compareOptions & (kCFCompareBackwards|kCFCompareAnchored))) ? true : false); @@ -3017,11 +3119,11 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT if (NULL == locale) { if (compareOptions & kCFCompareLocalized) { CFLocaleRef currentLocale = CFLocaleCopyCurrent(); - langCode = (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(currentLocale, true); + langCode = (const uint8_t *)_CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(currentLocale, true); CFRelease(currentLocale); } } else { - langCode = (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(locale, true); + langCode = (const uint8_t *)_CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(locale, true); } _CFStringInitInlineBufferInternal(string, &inlineBuf1, CFRangeMake(0, rangeToSearch.location + rangeToSearch.length), true); @@ -3056,7 +3158,7 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT strBuf1Len = 1; } else { str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); - strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL); + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL, NULL); if (1 > strBuf1Len) { *strBuf1 = str1Char; strBuf1Len = 1; @@ -3074,7 +3176,7 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT strBuf2Len = 1; } else { str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index); - strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL); + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL, NULL); if (1 > strBuf2Len) { *strBuf2 = str2Char; strBuf2Len = 1; @@ -3103,7 +3205,7 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT charactersLimit = characters + delta; while (characters < charactersLimit) { - strBuf1Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1), &inlineBuf1, str1Index + 1, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL); + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1), &inlineBuf1, str1Index + 1, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL, NULL); if ((strBuf1Len > 0) || (*characters != *strBuf1)) break; ++characters; ++str1Index; } @@ -3117,7 +3219,7 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT charactersLimit = characters + delta; while (characters < charactersLimit) { - strBuf2Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str1Index + 1), &inlineBuf2, str2Index + 1, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL); + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str1Index + 1), &inlineBuf2, str2Index + 1, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL, NULL); if ((strBuf2Len > 0) || (*characters != *strBuf2)) break; ++characters; ++str2Index; } @@ -3170,6 +3272,7 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT while (1) { str1Index = fromLoc; str2Index = 0; + lastStr2FoldIndex = lastStr2FoldUsed = lastStr2FoldLength = preventStr2FoldingUntil = 0; strBuf1Len = strBuf2Len = 0; @@ -3237,20 +3340,66 @@ Boolean CFStringFindWithOptionsAndLocale(CFStringRef string, CFStringRef stringT } if (str1Char != str2Char) { - if (0 == strBuf1Len) { - strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, &str1UsedLen); - if (strBuf1Len > 0) { - str1Char = *strBuf1; - strBuf1Index = 1; + if (0 == strBuf1Len && (preventStr1FoldingUntil == 0 || preventStr1FoldingUntil == str1Index)) { + preventStr1FoldingUntil = 0; + + // Check `strBuf1Index` isn't going to be larger than `strBuf1` + strBuf1Index = str1Index - lastStr1FoldIndex + 1; + if (lastStr1FoldLength > 0 && str1Index >= lastStr1FoldIndex && str1Index < lastStr1FoldIndex + lastStr1FoldUsed && strBuf1Index < lastStr1FoldLength) { + strBuf1Len = lastStr1FoldLength; + str1Char = strBuf1[strBuf1Index - 1]; + } else { + bool insufficientBuffer = false; + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, &str1UsedLen, &insufficientBuffer); + if (strBuf1Len > 0) { + str1Char = *strBuf1; + strBuf1Index = 1; + } + lastStr1FoldLength = strBuf1Len; + lastStr1FoldIndex = str1Index; + lastStr1FoldUsed = str1UsedLen; + if (insufficientBuffer) { + // We have a character cluster larger than our maximum folding size. This is likely a malformed string, so do not fold the remainder of this cluster + CFRange currentCluster = CFStringGetRangeOfCharacterClusterAtIndex(string, str1Index, kCFStringGraphemeCluster); + if (delta == 1) { + preventStr1FoldingUntil = currentCluster.location + currentCluster.length; + } else { + preventStr1FoldingUntil = MAX(currentCluster.location - 1, 1); + } + } } } if ((0 == strBuf1Len) && (0 < strBuf2Len)) break; if ((0 == strBuf2Len) && ((0 == strBuf1Len) || (str1Char != str2Char))) { - strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, &str2UsedLen); - if ((0 == strBuf2Len) || (str1Char != *strBuf2)) break; - strBuf2Index = 1; + if (preventStr2FoldingUntil == 0 || preventStr2FoldingUntil == str2Index) { + preventStr2FoldingUntil = 0; + + // Check `strBuf2Index` isn't going to be larger than `strBuf2` + strBuf2Index = str2Index - lastStr2FoldIndex + 1; + if (lastStr2FoldLength > 0 && str2Index >= lastStr2FoldIndex && str2Index < lastStr2FoldIndex + lastStr2FoldUsed && strBuf2Index < lastStr2FoldLength) { + strBuf2Len = lastStr2FoldLength; + str2Char = strBuf2[strBuf2Index - 1]; + if (str1Char != str2Char) break; + } else { + bool insufficientBuffer = false; + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, &str2UsedLen, &insufficientBuffer); + lastStr2FoldLength = strBuf2Len; + lastStr2FoldIndex = str2Index; + lastStr2FoldUsed = str2UsedLen; + if (insufficientBuffer) { + // We have a character cluster larger than our maximum folding size. This is likely a malformed string, so do not fold the remainder of this cluster + CFRange currentCluster = CFStringGetRangeOfCharacterClusterAtIndex(stringToFind, str2Index, kCFStringGraphemeCluster); + preventStr2FoldingUntil = currentCluster.location + currentCluster.length; + } + + if ((0 == strBuf2Len) || (str1Char != *strBuf2)) break; + strBuf2Index = 1; + } + } else { + if (str1Char != str2Char) break; + } } } @@ -4310,6 +4459,17 @@ CFRange CFStringGetRangeOfCharacterClusterAtIndex(CFStringRef string, CFIndex ch otherIndex = currentIndex + __CFTranscodingHintLength[(character - 0xF860)] + 1; if (otherIndex >= (range.location + range.length)) { if (otherIndex <= length) { + for (CFIndex checkIndex = currentIndex + 1; checkIndex < otherIndex;) { + CFRange checkRange = _CFStringInlineBufferGetComposedRange(&stringBuffer, checkIndex, type, bmpBitmap, csetType); + checkIndex = checkRange.location + checkRange.length; + + // Don't include any part of a composed range that extends beyond the hint range + if (checkIndex > otherIndex) { + otherIndex = checkRange.location; + break; + } + } + range.location = currentIndex; range.length = otherIndex - currentIndex; } @@ -4614,7 +4774,7 @@ CFStringRef CFStringCreateByCombiningStrings(CFAllocatorRef alloc, CFArrayRef ar if (stringCount == 0) { return CFStringCreateWithCharacters(alloc, NULL, 0); } else if (stringCount == 1) { - return (CFStringRef)CFStringCreateCopy(alloc, (CFStringRef)CFArrayGetValueAtIndex(array, 0)); + return CFStringCreateCopy(alloc, (CFStringRef)CFArrayGetValueAtIndex(array, 0)); } if (alloc == NULL) alloc = __CFGetDefaultAllocator(); @@ -4825,6 +4985,7 @@ double CFStringGetDoubleValue(CFStringRef str) { void CFStringSetExternalCharactersNoCopy(CFMutableStringRef string, UniChar *chars, CFIndex length, CFIndex capacity) { __CFAssertIsNotNegative(length); __CFAssertIsStringAndExternalMutable(string); + CF_RETURN_IF_NOT_MUTABLE(string); CFAssert4((length <= capacity) && ((capacity == 0) || ((capacity > 0) && chars)), __kCFLogAssertion, "%s(): Invalid args: characters %p length %ld capacity %ld", __PRETTY_FUNCTION__, chars, length, capacity); __CFStrSetContentPtr(string, chars); __CFStrSetExplicitLength(string, length); @@ -4837,7 +4998,7 @@ void CFStringSetExternalCharactersNoCopy(CFMutableStringRef string, UniChar *cha void CFStringInsert(CFMutableStringRef str, CFIndex idx, CFStringRef insertedStr) { CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (CFSwiftRef)str, NSMutableString.insertString, idx, (CFSwiftRef)insertedStr); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)str, insertString:(NSString *)insertedStr atIndex:(NSUInteger)idx); - __CFAssertIsStringAndMutable(str); + CF_RETURN_IF_NOT_MUTABLE(str); CFAssert3(idx >= 0 && idx <= __CFStrLength(str), __kCFLogAssertion, "%s(): string index %ld out of bounds (length %ld)", __PRETTY_FUNCTION__, idx, __CFStrLength(str)); __CFStringReplace(str, CFRangeMake(idx, 0), insertedStr); } @@ -4846,7 +5007,7 @@ void CFStringInsert(CFMutableStringRef str, CFIndex idx, CFStringRef insertedStr void CFStringDelete(CFMutableStringRef str, CFRange range) { CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (CFSwiftRef)str, NSMutableString.deleteCharactersInRange, range); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)str, deleteCharactersInRange:NSMakeRange(range.location, range.length)); - __CFAssertIsStringAndMutable(str); + CF_RETURN_IF_NOT_MUTABLE(str); __CFAssertRangeIsInStringBounds(str, range.location, range.length); __CFStringChangeSize(str, range, 0, false); } @@ -4855,7 +5016,7 @@ void CFStringDelete(CFMutableStringRef str, CFRange range) { void CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement) { CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (CFSwiftRef)str, NSMutableString.replaceCharactersInRange, range, (CFSwiftRef)replacement); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)str, replaceCharactersInRange:NSMakeRange(range.location, range.length) withString:(NSString *)replacement); - __CFAssertIsStringAndMutable(str); + CF_RETURN_IF_NOT_MUTABLE(str); __CFAssertRangeIsInStringBounds(str, range.location, range.length); __CFStringReplace(str, range, replacement); } @@ -4864,7 +5025,7 @@ void CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef replacem void CFStringReplaceAll(CFMutableStringRef str, CFStringRef replacement) { CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (CFSwiftRef)str, NSMutableString.setString, (CFSwiftRef)replacement); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)str, setString:(NSString *)replacement); - __CFAssertIsStringAndMutable(str); + CF_RETURN_IF_NOT_MUTABLE(str); __CFStringReplace(str, CFRangeMake(0, __CFStrLength(str)), replacement); } @@ -4872,7 +5033,7 @@ void CFStringReplaceAll(CFMutableStringRef str, CFStringRef replacement) { void CFStringAppend(CFMutableStringRef str, CFStringRef appended) { CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (CFSwiftRef)str, NSMutableString.appendString, (CFSwiftRef)appended); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)str, appendString:(NSString *)appended); - __CFAssertIsStringAndMutable(str); + CF_RETURN_IF_NOT_MUTABLE(str); __CFStringReplace(str, CFRangeMake(__CFStrLength(str), 0), appended); } @@ -4884,7 +5045,7 @@ void CFStringAppendCharacters(CFMutableStringRef str, const UniChar *chars, CFIn CF_SWIFT_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (CFSwiftRef)str, NSMutableString.appendCharacters, chars, appendedLength); CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)str, appendCharacters:chars length:(NSUInteger)appendedLength); - __CFAssertIsStringAndMutable(str); + CF_RETURN_IF_NOT_MUTABLE(str); strLength = __CFStrLength(str); if (__CFStrIsUnicode(str)) { @@ -4991,15 +5152,18 @@ void __CFStringAppendBytes(CFMutableStringRef str, const char *cStr, CFIndex app } void CFStringAppendPascalString(CFMutableStringRef str, ConstStringPtr pStr, CFStringEncoding encoding) { + CF_RETURN_IF_NOT_MUTABLE(str); __CFStringAppendBytes(str, (const char *)(pStr + 1), (CFIndex)*pStr, encoding); } void CFStringAppendCString(CFMutableStringRef str, const char *cStr, CFStringEncoding encoding) { + CF_RETURN_IF_NOT_MUTABLE(str); __CFStringAppendBytes(str, cStr, strlen(cStr), encoding); } void CFStringAppendFormat(CFMutableStringRef str, CFDictionaryRef formatOptions, CFStringRef format, ...) { + CF_RETURN_IF_NOT_MUTABLE(str); va_list argList; va_start(argList, format); @@ -5019,7 +5183,6 @@ CFIndex CFStringFindAndReplace(CFMutableStringRef string, CFStringRef stringToFi CFIndex foundCount = 0; CFIndex capacity = MAX_RANGES_ON_STACK; - __CFAssertIsStringAndMutable(string); __CFAssertRangeIsInStringBounds(string, rangeToSearch.location, rangeToSearch.length); // Note: This code is very similar to the one in CFStringCreateArrayWithFindResults(). @@ -5056,7 +5219,12 @@ CFIndex CFStringFindAndReplace(CFMutableStringRef string, CFStringRef stringToFi tail--; } } - __CFStringReplaceMultiple(string, ranges, foundCount, replacementString); + + int err = __CFStringReplaceMultiple(string, ranges, foundCount, replacementString); + if (err == _CFStringErrNotMutable) { + os_log_fault(_CFOSLog(), "CFString: %s(): Expect mutable string", __PRETTY_FUNCTION__); + } + if (ranges != rangeBuffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ranges); } @@ -5097,7 +5265,7 @@ void CFStringPad(CFMutableStringRef string, CFStringRef padString, CFIndex lengt CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)string, _cfPad:padString length:(uint32_t)length padIndex:(uint32_t)indexIntoPad); - __CFAssertIsStringAndMutable(string); + CF_RETURN_IF_NOT_MUTABLE(string); originalLength = __CFStrLength(string); if (length < originalLength) { @@ -5148,8 +5316,7 @@ void CFStringTrim(CFMutableStringRef string, CFStringRef trimString) { CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)string, _cfTrim:trimString); - __CFAssertIsStringAndMutable(string); - __CFAssertIsString(trimString); + CF_RETURN_IF_NOT_MUTABLE(string); newStartIndex = 0; length = __CFStrLength(string); @@ -5182,7 +5349,7 @@ void CFStringTrimWhitespace(CFMutableStringRef string) { CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)string, _cfTrimWS); - __CFAssertIsStringAndMutable(string); + CF_RETURN_IF_NOT_MUTABLE(string); newStartIndex = 0; length = __CFStrLength(string); @@ -5218,11 +5385,11 @@ void CFStringLowercase(CFMutableStringRef string, CFLocaleRef locale) { CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)string, _cfLowercase:(const void *)locale); - __CFAssertIsStringAndMutable(string); + CF_RETURN_IF_NOT_MUTABLE(string); length = __CFStrLength(string); - langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale, false) : NULL); + langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(locale, false) : NULL); if (!langCode && isEightBit) { uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); @@ -5309,11 +5476,11 @@ void CFStringUppercase(CFMutableStringRef string, CFLocaleRef locale) { CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)string, _cfUppercase:(const void *)locale); - __CFAssertIsStringAndMutable(string); + CF_RETURN_IF_NOT_MUTABLE(string); length = __CFStrLength(string); - langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale, false) : NULL); + langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(locale, false) : NULL); if (!langCode && isEightBit) { uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); @@ -5403,13 +5570,13 @@ void CFStringCapitalize(CFMutableStringRef string, CFLocaleRef locale) { CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)string, _cfCapitalize:(const void *)locale); - __CFAssertIsStringAndMutable(string); + CF_RETURN_IF_NOT_MUTABLE(string); length = __CFStrLength(string); caseIgnorableForBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCaseIgnorableCharacterSet, 0); - langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale, false) : NULL); + langCode = (const uint8_t *)(_CFCanUseLocale(locale) ? _CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(locale, false) : NULL); if (!langCode && isEightBit) { uint8_t *contents = (uint8_t *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); @@ -5542,7 +5709,7 @@ void CFStringNormalize(CFMutableStringRef string, CFStringNormalizationForm theF CF_OBJC_FUNCDISPATCHV(_kCFRuntimeIDCFString, void, (NSMutableString *)string, _cfNormalize:theForm); - __CFAssertIsStringAndMutable(string); + CF_RETURN_IF_NOT_MUTABLE(string); length = __CFStrLength(string); @@ -5840,6 +6007,8 @@ void CFStringNormalize(CFMutableStringRef string, CFStringNormalizationForm theF } void CFStringFold(CFMutableStringRef theString, CFStringCompareFlags theFlags, CFLocaleRef locale) { + CF_RETURN_IF_NOT_MUTABLE(theString); + CFStringInlineBuffer stringBuffer; CFIndex length = CFStringGetLength(theString); CFIndex currentIndex = 0; @@ -5860,7 +6029,7 @@ void CFStringFold(CFMutableStringRef theString, CFStringCompareFlags theFlags, C if ((0 == theFlags) || (0 == length)) goto bail; // nothing to do - langCode = ((NULL == theLocale) ? NULL : (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(theLocale, true)); + langCode = ((NULL == theLocale) ? NULL : (const uint8_t *)_CFStrGetSpecialCaseHandlingLanguageIdentifierForLocale(theLocale, true)); eightBitEncoding = __CFStringGetEightBitStringEncoding(); cString = (const uint8_t *)_CFStringGetCStringPtrInternal(theString, eightBitEncoding, false, isObjcOrSwift); @@ -5884,7 +6053,7 @@ void CFStringFold(CFMutableStringRef theString, CFStringCompareFlags theFlags, C } } } else { - if ((bufferLength = __CFStringFoldCharacterClusterAtIndex((UTF32Char)__CFCharToUniCharTable[*cStringPtr], &stringBuffer, cStringPtr - cString, theFlags, langCode, buffer, kCFStringStackBufferLength, NULL)) > 0) { + if ((bufferLength = __CFStringFoldCharacterClusterAtIndex((UTF32Char)__CFCharToUniCharTable[*cStringPtr], &stringBuffer, cStringPtr - cString, theFlags, langCode, buffer, kCFStringStackBufferLength, NULL, NULL)) > 0) { if ((*buffer > 0x7F) || (bufferLength > 1) || (NULL == cStringContents)) break; cStringContents[cStringPtr - cString] = *buffer; } @@ -5898,16 +6067,19 @@ void CFStringFold(CFMutableStringRef theString, CFStringCompareFlags theFlags, C if (currentIndex < length) { UTF16Char *contents; + CFMutableStringRef cfString = NULL; + CFRange range; if (isObjcOrSwift) { - CFMutableStringRef cfString; - CFRange range = CFRangeMake(currentIndex, length - currentIndex); + range = CFRangeMake(currentIndex, length - currentIndex); contents = (UTF16Char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UTF16Char) * range.length, 0); CFStringGetCharacters(theString, range, contents); cfString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, contents, range.length, range.length, NULL); - + } + + if (cfString) { CFStringFold(cfString, theFlags, theLocale); CFStringReplace(theString, range, cfString); @@ -5950,7 +6122,7 @@ void CFStringFold(CFMutableStringRef theString, CFStringCompareFlags theFlags, C if (CFUniCharIsSurrogateLowCharacter(lowSurrogate)) character = CFUniCharGetLongCharacterForSurrogatePair(character, lowSurrogate); } - bufferLength = __CFStringFoldCharacterClusterAtIndex(character, &stringBuffer, currentIndex, theFlags, langCode, buffer, kCFStringStackBufferLength, &consumedLength); + bufferLength = __CFStringFoldCharacterClusterAtIndex(character, &stringBuffer, currentIndex, theFlags, langCode, buffer, kCFStringStackBufferLength, &consumedLength, NULL); } if (consumedLength > 0) { @@ -6029,13 +6201,14 @@ static bool _CFStringHasStrongRTL(CFStringRef str, CFRange range) { /* String formatting */ enum { - kCFStringFormatZeroFlag = (1 << 0), // if not, padding is space char - kCFStringFormatMinusFlag = (1 << 1), // if not, no flag implied - kCFStringFormatPlusFlag = (1 << 2), // if not, no flag implied, overrides space - kCFStringFormatSpaceFlag = (1 << 3), // if not, no flag implied - kCFStringFormatExternalSpecFlag = (1 << 4), // using config dict - kCFStringFormatLocalizable = (1 << 5), // explicitly mark the specs we can localize - kCFStringFormatEntityMarkerFlag = (1 << 6) // using entity marker + kCFStringFormatZeroFlag = (1 << 0), // if not, padding is space char + kCFStringFormatMinusFlag = (1 << 1), // if not, no flag implied + kCFStringFormatPlusFlag = (1 << 2), // if not, no flag implied, overrides space + kCFStringFormatSpaceFlag = (1 << 3), // if not, no flag implied + kCFStringFormatExternalSpecFlag = (1 << 4), // using config dict + kCFStringFormatLocalizable = (1 << 5), // explicitly mark the specs we can localize + kCFStringFormatEntityMarkerFlag = (1 << 6), // using entity marker + kCFStringFormatPercentReplacementFlag = (1 << 7), // marks a '%%' replacement, used for metadata collection }; typedef struct { @@ -6094,15 +6267,16 @@ enum { CFFormatLongType = 33, CFFormatDoubleType = 34, CFFormatPointerType = 35, - CFFormatCFType = 37, /* handled specially; this is the general object type */ - CFFormatUnicharsType = 38, /* handled specially */ - CFFormatCharsType = 39, /* handled specially */ - CFFormatPascalCharsType = 40, /* handled specially */ - CFFormatSingleUnicharType = 41, /* handled specially */ - CFFormatDummyPointerType = 42 /* special case for %n */ + CFFormatCFType = 37, /* handled specially; this is the general object type */ + CFFormatUnicharsType = 38, /* handled specially */ + CFFormatCharsType = 39, /* handled specially */ + CFFormatPascalCharsType = 40, /* handled specially */ + CFFormatSingleUnicharType = 41, /* handled specially */ + CFFormatDummyPointerType = 42, /* special case for %n */ + CFFormatIncompleteSpecifierType = 43 /* special case for a trailing incomplete specifier */ }; -#if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_LINUX || TARGET_OS_BSD +#if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_LINUX /* Only come in here if spec->type is CFFormatLongType or CFFormatDoubleType. Pass in 0 for width or precision if not specified. Returns false if couldn't do the format (with the assumption the caller falls back to unlocalized). */ static Boolean __CFStringFormatLocalizedNumber(CFMutableStringRef output, CFLocaleRef locale, const CFPrintValue *values, const CFFormatSpec *spec, SInt32 width, SInt32 precision, Boolean hasPrecision) { @@ -6365,7 +6539,10 @@ CF_INLINE Boolean __CFParseFormatSpec(const UniChar *uformat, const uint8_t *cfo for (;;) { UniChar ch; - if (fmtLen <= *fmtIdx) return true; /* no type */ + if (fmtLen <= *fmtIdx) { /* no type */ + spec->type = CFFormatIncompleteSpecifierType; + return true; + } if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++]; if (keyIndex >= 0) { @@ -6621,11 +6798,109 @@ reswtch:switch (ch) { }} #endif +CF_INLINE void _CFStringFormatReplacementDictionaryAppendRange(CFMutableDictionaryRef replacement, SInt32 specLoc, SInt32 specLen, CFIndex lengthBefore, CFIndex lengthAfter) { + CFNumberRef specLocation = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, &specLoc); + CFDictionarySetValue(replacement, _kCFStringFormatMetadataSpecifierRangeLocationInFormatStringKey, specLocation); + CFRelease(specLocation); + + CFNumberRef specLength = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt32Type, &specLen); + CFDictionarySetValue(replacement, _kCFStringFormatMetadataSpecifierRangeLengthInFormatStringKey, specLength); + CFRelease(specLength); + + CFNumberRef rangeLocationObject = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberCFIndexType, &lengthBefore); + CFDictionarySetValue(replacement, _kCFStringFormatMetadataReplacementRangeLocationKey, rangeLocationObject); + CFRelease(rangeLocationObject); + + CFIndex length = MAX(lengthAfter - lengthBefore, 0); + CFNumberRef rangeLengthObject = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberCFIndexType, &length); + CFDictionarySetValue(replacement, _kCFStringFormatMetadataReplacementRangeLengthKey, rangeLengthObject); + CFRelease(rangeLengthObject); +} + +CF_INLINE void _CFStringFormatReplacementDictionaryAppendArgumentIndex(CFMutableDictionaryRef replacement, int16_t type, int8_t mainArgNum, CFIndex valuesCount) { + if (mainArgNum < 0 || mainArgNum >= valuesCount || type == CFFormatLiteralType) { + return; + } + + CFIndex userVisibleIndex = mainArgNum + 1; + CFNumberRef indexObject = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberCFIndexType, &userVisibleIndex); + CFDictionarySetValue(replacement, _kCFStringFormatMetadataReplacementIndexKey, indexObject); + CFRelease(indexObject); +} + +CF_INLINE void _CFStringFormatReplacementDictionaryAppendArgumentValue(CFMutableDictionaryRef replacement, CFPrintValue *values, int16_t type, int8_t mainArgNum, CFIndex valuesCount) { + if (mainArgNum < 0 || mainArgNum >= valuesCount) { + return; + } + + CFNumberRef numberValue = NULL; + CFPrintValue const value = values[mainArgNum]; + + switch (value.type) { + case CFFormatCFType: + if (value.value.pointerValue) { + CFDictionarySetValue(replacement, _kCFStringFormatMetadataArgumentObjectKey, value.value.pointerValue); + } + break; + + case CFFormatLongType: + numberValue = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, &value.value.int64Value); + CFDictionarySetValue(replacement, _kCFStringFormatMetadataArgumentNumberKey, numberValue); + CFRelease(numberValue); + break; + + case CFFormatDoubleType: + numberValue = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberDoubleType, &value.value.doubleValue); + CFDictionarySetValue(replacement, _kCFStringFormatMetadataArgumentNumberKey, numberValue); + CFRelease(numberValue); + break; + + default: + break; + } +} + +static void _CFStringFormatAppendMetadata(CFMutableArrayRef *outReplacementMetadata, CFIndex specsCount, CFPrintValue *values, CFIndex valuesCount, CFFormatSpec spec, CFIndex lengthBefore, CFIndex lengthAfter) { + if (!outReplacementMetadata) { + return; + } + + if (spec.type == CFFormatLiteralType) { + if ((spec.flags & kCFStringFormatPercentReplacementFlag) == 0) { + // This is a literal, unreplaced chunk. Do not add it as a replacement. + return; + } + } else if (spec.type != CFFormatIncompleteSpecifierType) { + if (spec.mainArgNum < 0) { + return; + } + + assert(spec.mainArgNum < valuesCount); + if (spec.mainArgNum >= valuesCount) { + return; + } + } + + if (!*outReplacementMetadata) { + *outReplacementMetadata = CFArrayCreateMutable(kCFAllocatorSystemDefault, specsCount, &kCFTypeArrayCallBacks); + } + + CFMutableDictionaryRef replacement = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + _CFStringFormatReplacementDictionaryAppendRange(replacement, spec.loc, spec.len, lengthBefore, lengthAfter); + _CFStringFormatReplacementDictionaryAppendArgumentIndex(replacement, spec.type, spec.mainArgNum, valuesCount); + _CFStringFormatReplacementDictionaryAppendArgumentValue(replacement, values, spec.type, spec.mainArgNum, valuesCount); + + CFArrayAppendValue(*outReplacementMetadata, replacement); + CFRelease(replacement); +} + /* These three functions are the external entry points for string formatting. */ void CFStringAppendFormatAndArguments(CFMutableStringRef outputString, CFDictionaryRef formatOptions, CFStringRef formatString, va_list args) { + CF_RETURN_IF_NOT_MUTABLE(outputString); CFErrorRef error; - if (!__CFStringAppendFormatCore(outputString, NULL, NULL, formatOptions, NULL, NULL, formatString, 0, NULL, 0, args, &error)) { + if (!__CFStringAppendFormatCore(outputString, NULL, NULL, formatOptions, NULL, NULL, formatString, 0, NULL, 0, args, NULL, &error)) { CFLog(kCFLogLevelError, CFSTR("ERROR: Failed to format string: %@"), error); if (error) CFRelease(error); } @@ -6633,7 +6908,7 @@ void CFStringAppendFormatAndArguments(CFMutableStringRef outputString, CFDiction void _CFStringAppendFormatAndArgumentsAux2(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, CFStringRef formatString, va_list args) { CFErrorRef error; - if (!__CFStringAppendFormatCore(outputString, copyDescFunc, contextDescFunc, formatOptions, NULL, NULL, formatString, 0, NULL, 0, args, &error)) { + if (!__CFStringAppendFormatCore(outputString, copyDescFunc, contextDescFunc, formatOptions, NULL, NULL, formatString, 0, NULL, 0, args, NULL, &error)) { CFLog(kCFLogLevelError, CFSTR("ERROR: Failed to format string: %@"), error); if (error) CFRelease(error); } @@ -6865,18 +7140,19 @@ static CFIndex __CFStringValidateFormat(CFStringRef expected, CFStringRef untrus copyDescFunc: Callback for formatting strings. Can be NULL. Foundation calls with a function that will invoke descriptionWithLocale: or description. Second argument is the locale (a dictionary or locale). contextDescFunc: Callback for doing context-based formatting. Can be NULL. <> formatOptions: Locale specific info. Used to be a CFDictionary, now CFLocale, but either is still possible. If !NULL, localized formatting is assumed. - stringsDictConfig: Only used for recursive calls when doing stringsDict formatting. Otherwise NULL. <> + stringsDictConfig: Only used for recursive calls when doing stringsDict formatting, or as a fallback for when the config is not stored in the passed formatString (formatting an NSAttributedString). Otherwise NULL. validFormatSpecifiers: Only used to validate the format specifiers in the formatString. A string that contains an in order sequence of the valid format specifiers. formatString: The actual format string. initialArgPosition: Only used for recursive calls when doing stringsDict formatting. Otherwise 0. <> origValues: Only used for recursive calls when doing stringsDict formatting. Otherwise NULL. <> originalValuesSize: Only used for recursive calls when doing stringsDict formatting. Otherwise 0. <> args: The arguments to be formatted. + outReplacementMetadata: On return, contains information on the replacements applied for the specifiers. If NULL, no metadata is generated. errorPtr: Only used when validating the formatString against valid format specfiers. Nil unless the validation fails. If error is set, return value is false. ??? %s depends on handling of encodings by __CFStringAppendBytes */ -static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, CFDictionaryRef stringsDictConfig, CFStringRef validFormatSpecifiers, CFStringRef formatString, CFIndex initialArgPosition, const void *origValues, CFIndex originalValuesSize, va_list args, CFErrorRef *errorPtr) { +static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, CFDictionaryRef stringsDictConfig, CFStringRef validFormatSpecifiers, CFStringRef formatString, CFIndex initialArgPosition, const void *origValues, CFIndex originalValuesSize, va_list args, CFArrayRef *outReplacementMetadata, CFErrorRef *errorPtr) { int32_t numSpecs, sizeSpecs, sizeArgNum, formatIdx, curSpec, argNum; CFIndex formatLen; const uint8_t *cformat = NULL; @@ -6895,6 +7171,9 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr CFAllocatorRef tmpAlloc = NULL; bool localizedFormatting = formatOptions && (CFGetTypeID(formatOptions) == CFLocaleGetTypeID()); + CFMutableArrayRef metadataStorage = NULL; + CFMutableArrayRef *metadata = outReplacementMetadata ? &metadataStorage : NULL; + intmax_t dummyLocation; // A place for %n to do its thing in; should be the widest possible int value numSpecs = 0; @@ -7039,6 +7318,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr if (CFFormatLiteralType == specs[curSpec].type) { specs[curSpec].loc = formatIdx + 1; specs[curSpec].len = 1; + specs[curSpec].flags |= kCFStringFormatPercentReplacementFlag; } else { specs[curSpec].len = newFmtIdx - formatIdx; } @@ -7050,8 +7330,19 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr } numSpecs = curSpec; - // Max of three args per spec, reasoning thus: 1 width, 1 prec, 1 value - sizeArgNum = ((NULL == originalValues) ? (3 * sizeSpecs + 1) : originalValuesSize); + if (originalValues == NULL) { + // Max of three args per spec, reasoning thus: 1 width, 1 prec, 1 value + sizeArgNum = 3 * sizeSpecs + 1; + } else { +#define MAX_SIZE_ORIGINAL_VALUES (0x2ffffe) /* Max size of original values is (3 * MAX_SIZE_SPECS + 1) */ + if (originalValuesSize > MAX_SIZE_ORIGINAL_VALUES) { + if (errorPtr) *errorPtr = __CFCreateOverflowError(); + success = false; + goto cleanup; + } + + sizeArgNum = originalValuesSize; + } values = (sizeArgNum > VPRINTF_BUFFER_LEN) ? (CFPrintValue *)CFAllocatorAllocate(tmpAlloc, sizeArgNum * sizeof(CFPrintValue), 0) : localValuesBuffer; if (values != localValuesBuffer && __CFOASafe) __CFSetLastAllocationEventName(values, "CFString (temp)"); @@ -7067,6 +7358,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr SInt32 newMaxArgNum; if (0 == specs[curSpec].type) continue; if (CFFormatLiteralType == specs[curSpec].type) continue; + if (CFFormatIncompleteSpecifierType == specs[curSpec].type) continue; newMaxArgNum = sizeArgNum; if (newMaxArgNum < specs[curSpec].mainArgNum) { newMaxArgNum = specs[curSpec].mainArgNum; @@ -7078,8 +7370,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr newMaxArgNum = specs[curSpec].widthArgNum; } if (sizeArgNum < newMaxArgNum) { - if (numConfigs > 0) va_end(copiedArgs); - if (specs != localSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, specs); + if (specs != localSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, specs); if (values != localValuesBuffer) CFAllocatorDeallocate(tmpAlloc, values); if (formatChars && (formatChars != localFormatBuffer)) CFAllocatorDeallocate(tmpAlloc, formatChars); @@ -7117,6 +7408,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr if ((NULL != originalValues) && (0 == values[argNum].type)) values[argNum] = originalValues[argNum]; switch (values[argNum].type) { case 0: + case CFFormatIncompleteSpecifierType: case CFFormatLiteralType: break; case CFFormatLongType: @@ -7195,12 +7487,19 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr hasPrecision = true; } +#define __CFStringFormatOutputLengthIfNeeded() (metadata ? CFStringGetLength(outputString) : 0) + CFIndex oldLength = 0; + switch (specs[curSpec].type) { case CFFormatLongType: case CFFormatDoubleType: -#if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_LINUX || TARGET_OS_BSD +#if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_LINUX if (localizedFormatting && (specs[curSpec].flags & kCFStringFormatLocalizable)) { // We have a locale, so we do localized formatting - if (__CFStringFormatLocalizedNumber(outputString, (CFLocaleRef)formatOptions, values, &specs[curSpec], width, precision, hasPrecision)) break; + oldLength = __CFStringFormatOutputLengthIfNeeded(); + if (__CFStringFormatLocalizedNumber(outputString, (CFLocaleRef)formatOptions, values, &specs[curSpec], width, precision, hasPrecision)) { + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded()); + break; + } } /* Otherwise fall-thru to the next case! */ #endif @@ -7252,6 +7551,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr // Should modify format buffer here if necessary; for example, to translate %qd to // the equivalent, on architectures which do not have %q. buffer[bufferSize - 1] = '\0'; + oldLength = __CFStringFormatOutputLengthIfNeeded(); switch (specs[curSpec].type) { case CFFormatLongType: if (CFFormatSize8 == specs[curSpec].size) { @@ -7296,6 +7596,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr break; } if (!appended) CFStringAppendCString(outputString, (const char *)buffer, __CFStringGetEightBitStringEncoding()); + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded()); if (dynamicBuffer) { CFAllocatorDeallocate(kCFAllocatorSystemDefault, dynamicBuffer); } @@ -7305,14 +7606,21 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr } break; case CFFormatLiteralType: + oldLength = __CFStringFormatOutputLengthIfNeeded(); if (cformat) { __CFStringAppendBytes(outputString, (const char *)(cformat+specs[curSpec].loc), specs[curSpec].len, __CFStringGetEightBitStringEncoding()); } else { CFStringAppendCharacters(outputString, uformat+specs[curSpec].loc, specs[curSpec].len); } + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded()); break; + case CFFormatIncompleteSpecifierType: + oldLength = __CFStringFormatOutputLengthIfNeeded(); + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, oldLength); + break; case CFFormatPascalCharsType: case CFFormatCharsType: + oldLength = __CFStringFormatOutputLengthIfNeeded(); if (values[specs[curSpec].mainArgNum].value.pointerValue == NULL) { CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII); } else { @@ -7351,12 +7659,16 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr __CFStringAppendBytes(outputString, str, len, __CFStringGetSystemEncoding()); } } + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded()); break; case CFFormatSingleUnicharType: + oldLength = __CFStringFormatOutputLengthIfNeeded(); ch = (UniChar)values[specs[curSpec].mainArgNum].value.int64Value; CFStringAppendCharacters(outputString, &ch, 1); + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded()); break; case CFFormatUnicharsType: + oldLength = __CFStringFormatOutputLengthIfNeeded(); up = (UniChar *)values[specs[curSpec].mainArgNum].value.pointerValue; if (NULL == up) { CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII); @@ -7386,8 +7698,10 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr CFStringAppendCharacters(outputString, up, len); } } + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded()); break; case CFFormatCFType: + oldLength = __CFStringFormatOutputLengthIfNeeded(); if (specs[curSpec].configDictIndex != -1) { // config dict CFTypeRef object = NULL; switch (values[specs[curSpec].mainArgNum].type) { @@ -7494,7 +7808,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr } if (NULL != object) CFRelease(object); - + } else if (NULL != values[specs[curSpec].mainArgNum].value.pointerValue) { CFStringRef str = NULL; if (contextDescFunc) { @@ -7525,6 +7839,7 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr } else { CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII); } + _CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded()); break; } if (validFormatSpecifiers && specs[curSpec].type != CFFormatLiteralType && specs[curSpec].configDictIndex == -1) { @@ -7554,6 +7869,11 @@ static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStr if (configs != localConfigs) CFAllocatorDeallocate(tmpAlloc, configs); if (formattingConfig != NULL) CFRelease(formattingConfig); + if (metadataStorage && outReplacementMetadata) { + *outReplacementMetadata = CFArrayCreateCopy(kCFAllocatorSystemDefault, metadataStorage); + CFRelease(metadataStorage); + } + return success; } diff --git a/CoreFoundation/String.subproj/CFString.h b/CoreFoundation/String.subproj/CFString.h index 058a02b924..a01462a6a5 100644 --- a/CoreFoundation/String.subproj/CFString.h +++ b/CoreFoundation/String.subproj/CFString.h @@ -162,7 +162,7 @@ CF_EXPORT void *_CF_CONSTANT_STRING_SWIFT_CLASS[]; struct __CFConstStr { struct { - uintptr_t _cfisa; + __ptrauth_cf_objc_isa_pointer uintptr_t _cfisa; uintptr_t _swift_rc; uint64_t _cfinfoa; } _base; diff --git a/CoreFoundation/String.subproj/CFStringEncodings.c b/CoreFoundation/String.subproj/CFStringEncodings.c index 727b6fce79..8fff5d3e1d 100644 --- a/CoreFoundation/String.subproj/CFStringEncodings.c +++ b/CoreFoundation/String.subproj/CFStringEncodings.c @@ -42,7 +42,7 @@ CFStringEncodingCheapEightBitToUnicodeProc __CFCharToUniCharFunc = NULL; // To avoid early initialization issues, we just initialize this here // This should not be const as it is changed -UniChar __CFCharToUniCharTable[256] = { +static UniChar __CFMutableCharToUniCharTable[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, @@ -59,19 +59,189 @@ UniChar __CFCharToUniCharTable[256] = { 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 -}; +}; +UniChar const * __CFCharToUniCharTable = __CFMutableCharToUniCharTable; + +UniChar const __CFIdempotentCharToUniCharTable[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, +112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, +128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, +144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, +160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, +176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, +192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, +208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, +224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, +240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +#if TARGET_OS_OSX || TARGET_OS_IPHONE +UniChar const __CFMacRomanCharToUnicharTable[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, +112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, +0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ +0x00C5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ +0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ +0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ +0x00D1, /* LATIN CAPITAL LETTER N WITH TILDE */ +0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ +0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ +0x00E1, /* LATIN SMALL LETTER A WITH ACUTE */ +0x00E0, /* LATIN SMALL LETTER A WITH GRAVE */ +0x00E2, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ +0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ +0x00E3, /* LATIN SMALL LETTER A WITH TILDE */ +0x00E5, /* LATIN SMALL LETTER A WITH RING ABOVE */ +0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ +0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ +0x00E8, /* LATIN SMALL LETTER E WITH GRAVE */ +0x00EA, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */ +0x00EB, /* LATIN SMALL LETTER E WITH DIAERESIS */ +0x00ED, /* LATIN SMALL LETTER I WITH ACUTE */ +0x00EC, /* LATIN SMALL LETTER I WITH GRAVE */ +0x00EE, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ +0x00EF, /* LATIN SMALL LETTER I WITH DIAERESIS */ +0x00F1, /* LATIN SMALL LETTER N WITH TILDE */ +0x00F3, /* LATIN SMALL LETTER O WITH ACUTE */ +0x00F2, /* LATIN SMALL LETTER O WITH GRAVE */ +0x00F4, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ +0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ +0x00F5, /* LATIN SMALL LETTER O WITH TILDE */ +0x00FA, /* LATIN SMALL LETTER U WITH ACUTE */ +0x00F9, /* LATIN SMALL LETTER U WITH GRAVE */ +0x00FB, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ +0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ +0x2020, /* DAGGER */ +0x00B0, /* DEGREE SIGN */ +0x00A2, /* CENT SIGN */ +0x00A3, /* POUND SIGN */ +0x00A7, /* SECTION SIGN */ +0x2022, /* BULLET */ +0x00B6, /* PILCROW SIGN */ +0x00DF, /* LATIN SMALL LETTER SHARP S */ +0x00AE, /* REGISTERED SIGN */ +0x00A9, /* COPYRIGHT SIGN */ +0x2122, /* TRADE MARK SIGN */ +0x00B4, /* ACUTE ACCENT */ +0x00A8, /* DIAERESIS */ +0x2260, /* NOT EQUAL TO */ +0x00C6, /* LATIN CAPITAL LIGATURE AE */ +0x00D8, /* LATIN CAPITAL LETTER O WITH STROKE */ +0x221E, /* INFINITY */ +0x00B1, /* PLUS-MINUS SIGN */ +0x2264, /* LESS-THAN OR EQUAL TO */ +0x2265, /* GREATER-THAN OR EQUAL TO */ +0x00A5, /* YEN SIGN */ +0x00B5, /* MICRO SIGN */ +0x2202, /* PARTIAL DIFFERENTIAL */ +0x2211, /* N-ARY SUMMATION */ +0x220F, /* N-ARY PRODUCT */ +0x03C0, /* GREEK SMALL LETTER PI */ +0x222B, /* INTEGRAL */ +0x00AA, /* FEMININE ORDINAL INDICATOR */ +0x00BA, /* MASCULINE ORDINAL INDICATOR */ +0x03A9, /* OHM SIGN (Canonical mapping) */ +0x00E6, /* LATIN SMALL LIGATURE AE */ +0x00F8, /* LATIN SMALL LETTER O WITH STROKE */ +0x00BF, /* INVERTED QUESTION MARK */ +0x00A1, /* INVERTED EXCLAMATION MARK */ +0x00AC, /* NOT SIGN */ +0x221A, /* SQUARE ROOT */ +0x0192, /* LATIN SMALL LETTER F WITH HOOK */ +0x2248, /* ALMOST EQUAL TO */ +0x2206, /* INCREMENT */ +0x00AB, /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ +0x00BB, /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ +0x2026, /* HORIZONTAL ELLIPSIS */ +0x00A0, /* NO-BREAK SPACE */ +0x00C0, /* LATIN CAPITAL LETTER A WITH GRAVE */ +0x00C3, /* LATIN CAPITAL LETTER A WITH TILDE */ +0x00D5, /* LATIN CAPITAL LETTER O WITH TILDE */ +0x0152, /* LATIN CAPITAL LIGATURE OE */ +0x0153, /* LATIN SMALL LIGATURE OE */ +0x2013, /* EN DASH */ +0x2014, /* EM DASH */ +0x201C, /* LEFT DOUBLE QUOTATION MARK */ +0x201D, /* RIGHT DOUBLE QUOTATION MARK */ +0x2018, /* LEFT SINGLE QUOTATION MARK */ +0x2019, /* RIGHT SINGLE QUOTATION MARK */ +0x00F7, /* DIVISION SIGN */ +0x25CA, /* LOZENGE */ +0x00FF, /* LATIN SMALL LETTER Y WITH DIAERESIS */ +0x0178, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ +0x2044, /* FRACTION SLASH */ +0x20AC, /* EURO SIGN */ +0x2039, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK */ +0x203A, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */ +0xFB01, /* LATIN SMALL LIGATURE FI */ +0xFB02, /* LATIN SMALL LIGATURE FL */ +0x2021, /* DOUBLE DAGGER */ +0x00B7, /* MIDDLE DOT */ +0x201A, /* SINGLE LOW-9 QUOTATION MARK */ +0x201E, /* DOUBLE LOW-9 QUOTATION MARK */ +0x2030, /* PER MILLE SIGN */ +0x00C2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ +0x00CA, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ +0x00C1, /* LATIN CAPITAL LETTER A WITH ACUTE */ +0x00CB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ +0x00C8, /* LATIN CAPITAL LETTER E WITH GRAVE */ +0x00CD, /* LATIN CAPITAL LETTER I WITH ACUTE */ +0x00CE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ +0x00CF, /* LATIN CAPITAL LETTER I WITH DIAERESIS */ +0x00CC, /* LATIN CAPITAL LETTER I WITH GRAVE */ +0x00D3, /* LATIN CAPITAL LETTER O WITH ACUTE */ +0x00D4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ +0xF8FF, /* Apple logo */ +0x00D2, /* LATIN CAPITAL LETTER O WITH GRAVE */ +0x00DA, /* LATIN CAPITAL LETTER U WITH ACUTE */ +0x00DB, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ +0x00D9, /* LATIN CAPITAL LETTER U WITH GRAVE */ +0x0131, /* LATIN SMALL LETTER DOTLESS I */ +0x02C6, /* MODIFIER LETTER CIRCUMFLEX ACCENT */ +0x02DC, /* SMALL TILDE */ +0x00AF, /* MACRON */ +0x02D8, /* BREVE */ +0x02D9, /* DOT ABOVE */ +0x02DA, /* RING ABOVE */ +0x00B8, /* CEDILLA */ +0x02DD, /* DOUBLE ACUTE ACCENT */ +0x02DB, /* OGONEK */ +0x02C7, /* CARON */ +}; +#endif CF_PRIVATE void __CFSetCharToUniCharFunc(CFStringEncodingCheapEightBitToUnicodeProc _Nullable func) { if (__CFCharToUniCharFunc != func) { - int ch; - __CFCharToUniCharFunc = func; if (func) { - for (ch = 128; ch < 256; ch++) { - UniChar uch; - __CFCharToUniCharTable[ch] = (__CFCharToUniCharFunc(0, ch, &uch) ? uch : 0xFFFD); +#if TARGET_OS_OSX || TARGET_OS_IPHONE + const CFStringEncodingConverter *converter = CFStringEncodingGetConverter(kCFStringEncodingMacRoman); + if (converter && converter->toUnicode.cheapEightBit == func) { + __CFCharToUniCharTable = __CFMacRomanCharToUnicharTable; + } else +#endif + { + __CFCharToUniCharTable = __CFMutableCharToUniCharTable; + for (int ch = 128; ch < 256; ch++) { + UniChar uch; + __CFMutableCharToUniCharTable[ch] = (func(0, ch, &uch) ? uch : 0xFFFD); + } } - } else { // If we have no __CFCharToUniCharFunc, assume 128..255 return the value as-is - for (ch = 128; ch < 256; ch++) __CFCharToUniCharTable[ch] = ch; + __CFCharToUniCharFunc = func; + } else { + // If we have no __CFCharToUniCharFunc, assume 128..255 return the value as-is + __CFCharToUniCharTable = __CFIdempotentCharToUniCharTable; } } } @@ -320,12 +490,12 @@ Boolean __CFStringDecodeByteStream3(const uint8_t *bytes, CFIndex len, CFStringE memmove(buffer->chars.ascii, chars, len * sizeof(uint8_t)); } else { CFIndex numDone; + static dispatch_once_t onceToken; static CFStringEncodingToUnicodeProc __CFFromUTF8 = NULL; - - if (!__CFFromUTF8) { - const CFStringEncodingConverter *converter = CFStringEncodingGetConverter(kCFStringEncodingUTF8); - __CFFromUTF8 = converter->toUnicode.standard; - } + dispatch_once(&onceToken, ^{ + // This decoder is built in, no need to check it more than once + __CFFromUTF8 = CFStringEncodingGetConverter(kCFStringEncodingUTF8)->toUnicode.standard; + }); buffer->shouldFreeChars = !buffer->chars.unicode && (len <= MAX_LOCAL_UNICHARS) ? false : true; buffer->chars.unicode = (buffer->chars.unicode ? buffer->chars.unicode : (len <= MAX_LOCAL_UNICHARS) ? (UniChar *)buffer->localBuffer : (UniChar *)CFAllocatorAllocate(buffer->allocator, len * sizeof(UniChar), 0)); @@ -510,12 +680,13 @@ CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex const UniChar *unichars; if (encoding == kCFStringEncodingUTF8 && (unichars = CFStringGetCharactersPtr(string))) { + static dispatch_once_t onceToken; static CFStringEncodingToBytesProc __CFToUTF8 = NULL; + dispatch_once(&onceToken, ^{ + // Thiis encoder is built-in, no need to check it more than once + __CFToUTF8 = CFStringEncodingGetConverter(kCFStringEncodingUTF8)->toBytes.standard; + }); - if (!__CFToUTF8) { - const CFStringEncodingConverter *utf8Converter = CFStringEncodingGetConverter(kCFStringEncodingUTF8); - __CFToUTF8 = utf8Converter->toBytes.standard; - } numCharsProcessed = __CFToUTF8((generatingExternalFile ? kCFStringEncodingPrependBOM : 0), unichars + rangeLoc, rangeLen, buffer, (buffer ? max : 0), &totalBytesWritten); } else if (encoding == kCFStringEncodingNonLossyASCII) { @@ -643,7 +814,10 @@ CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex const unsigned char *cString = NULL; Boolean isASCIISuperset = __CFStringEncodingIsSupersetOfASCII(encoding); - if (!CFStringEncodingIsValidEncoding(encoding)) return 0; + if (!CFStringEncodingIsValidEncoding(encoding)) { + if (usedBufLen) *usedBufLen = 0; + return 0; + } if (!CF_IS_OBJC(_kCFRuntimeIDCFString, string) && isASCIISuperset) { // Checking for NSString to avoid infinite recursion const unsigned char *ptr; @@ -703,6 +877,11 @@ CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex } } + // At this level, only a NULL buffer is an indicator that this operation should be a "dry run". However, we're about to call CFStringEncodingUnicodeToBytes() which infers that behavior only from its maxByteLen parameter being 0. Hence the following line that forces `max` to 0 if `buffer` is NULL. However, because of that difference in behavior, we DON'T want to proceed with CFStringEncodingUnicodeToBytes if our `buffer` is non-NULL and `max` is 0. Doing so would mislead the caller into believing that the string was successfully converted and potentially result in bugs like rdar://problem/70764833. + if (buffer && max == 0) { + if (usedBufLen) *usedBufLen = 0; + return 0; + } if (!buffer) max = 0; // Special case for Foundation. When lossByte == 0xFF && encoding kCFStringEncodingASCII, we do the default ASCII fallback conversion @@ -930,7 +1109,6 @@ Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, uint8_t *buffer */ void _CFStringGetUserDefaultEncoding(UInt32 *oScriptValue, UInt32 *oRegionValue) { char *stringValue; - char buffer[__kCFMaxDefaultEncodingFileLength]; int uid = _CFGetEUID(); if ((stringValue = (char *)__CFgetenv(__kCFUserEncodingEnvVariableName)) != NULL) { @@ -941,6 +1119,10 @@ void _CFStringGetUserDefaultEncoding(UInt32 *oScriptValue, UInt32 *oRegionValue) } } +#if TARGET_OS_OSX + // The .CFUserTextEncoding file (__kCFUserEncodingFileName) is only written out on mac. + // We should also consider deprecating it: 29116894 + char buffer[__kCFMaxDefaultEncodingFileLength]; if ((stringValue == NULL) && ((uid > 0) || __CFgetenv("HOME"))) { char passwdExtraBuf[1000 + MAXPATHLEN]; // Extra memory buffer for getpwuid_r(); no clue as to how large this should be... struct passwd passwdBuf, *passwdp = NULL; @@ -988,6 +1170,14 @@ void _CFStringGetUserDefaultEncoding(UInt32 *oScriptValue, UInt32 *oRegionValue) if (-1 != no_hang_fd) close(no_hang_fd); } } +#else + // Fallback to smRoman/verUS + if (stringValue == NULL && uid > 0) { + char encoding[32]; + snprintf(encoding, sizeof(encoding), "0x%X:0:0", uid); + setenv(__kCFUserEncodingEnvVariableName, encoding, 1); + } +#endif if (stringValue) { *oScriptValue = strtol_l(stringValue, &stringValue, 0, NULL); diff --git a/CoreFoundation/String.subproj/CFStringUtilities.c b/CoreFoundation/String.subproj/CFStringUtilities.c index 8f20ba34f1..cb60e4d690 100644 --- a/CoreFoundation/String.subproj/CFStringUtilities.c +++ b/CoreFoundation/String.subproj/CFStringUtilities.c @@ -59,11 +59,12 @@ CFStringRef CFStringGetNameOfEncoding(CFStringEncoding theEncoding) { CFStringRef theName = NULL; + os_unfair_lock_lock(&mappingTableLock); if (mappingTable) { - os_unfair_lock_lock(&mappingTableLock); + // Once added, the value is not removed, so no need to add an additional retain here (plus this function is a 'get'). theName = (CFStringRef)CFDictionaryGetValue(mappingTable, (const void*)(uintptr_t)theEncoding); - os_unfair_lock_unlock(&mappingTableLock); } + os_unfair_lock_unlock(&mappingTableLock); if (!theName) { const char *encodingName = __CFStringEncodingGetName(theEncoding); @@ -153,7 +154,7 @@ CFStringRef CFStringConvertEncodingToIANACharSetName(CFStringEncoding encoding) return name; } -enum { +CF_ENUM(CFStringEncoding) { NSASCIIStringEncoding = 1, /* 0..127 only */ NSNEXTSTEPStringEncoding = 2, NSJapaneseEUCStringEncoding = 3, @@ -175,7 +176,7 @@ enum { NSProprietaryStringEncoding = 65536 /* Installation-specific encoding */ }; -#define NSENCODING_MASK (1 << 31) +#define NSENCODING_MASK ((CFStringEncoding)1 << 31) unsigned long CFStringConvertEncodingToNSStringEncoding(CFStringEncoding theEncoding) { //These two are frequently used in situations that are otherwise very fast (e.g. tagged strings), so check them first @@ -391,28 +392,29 @@ static UCollator *__CFStringCreateCollator(CFLocaleRef compareLocale) { static UCollator *__CFDefaultCollators[kCFMaxCachedDefaultCollators]; static CFIndex __CFDefaultCollatorsCount = 0; static const void *__CFDefaultCollatorLocale = NULL; -static CFLock_t __CFDefaultCollatorLock = CFLockInit; +static os_unfair_lock __CFDefaultCollatorLock = OS_UNFAIR_LOCK_INIT; static UCollator *__CFStringCopyDefaultCollator(CFLocaleRef compareLocale) { CFLocaleRef currentLocale = NULL; UCollator * collator = NULL; + os_unfair_lock_lock_with_options(&__CFDefaultCollatorLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); if (compareLocale != __CFDefaultCollatorLocale) { currentLocale = CFLocaleCopyCurrent(); if (compareLocale != currentLocale) { CFRelease(currentLocale); + os_unfair_lock_unlock(&__CFDefaultCollatorLock); return NULL; } } - __CFLock(&__CFDefaultCollatorLock); if ((NULL != currentLocale) && (__CFDefaultCollatorLocale != currentLocale)) { while (__CFDefaultCollatorsCount > 0) ucol_close(__CFDefaultCollators[--__CFDefaultCollatorsCount]); __CFDefaultCollatorLocale = CFRetain(currentLocale); } if (__CFDefaultCollatorsCount > 0) collator = __CFDefaultCollators[--__CFDefaultCollatorsCount]; - __CFUnlock(&__CFDefaultCollatorLock); + os_unfair_lock_unlock(&__CFDefaultCollatorLock); if (NULL == collator) { collator = __CFStringCreateCollator(compareLocale); @@ -428,12 +430,12 @@ static void __collatorFinalize(UCollator *collator) { CFLocaleRef locale = _CFGetTSD(__CFTSDKeyCollatorLocale); _CFSetTSD(__CFTSDKeyCollatorUCollator, NULL, NULL); _CFSetTSD(__CFTSDKeyCollatorLocale, NULL, NULL); - __CFLock(&__CFDefaultCollatorLock); + os_unfair_lock_lock_with_options(&__CFDefaultCollatorLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION); if ((__CFDefaultCollatorLocale == locale) && (__CFDefaultCollatorsCount < kCFMaxCachedDefaultCollators)) { __CFDefaultCollators[__CFDefaultCollatorsCount++] = collator; collator = NULL; } - __CFUnlock(&__CFDefaultCollatorLock); + os_unfair_lock_unlock(&__CFDefaultCollatorLock); if (NULL != collator) ucol_close(collator); #ifndef __clang_analyzer__ // This release is unbalanced from perspective of analyzer, but it is retained when __CFTSDKeyCollatorLocale is set diff --git a/CoreFoundation/String.subproj/CFString_Internal.h b/CoreFoundation/String.subproj/CFString_Internal.h index 54778bf90c..3074777e26 100644 --- a/CoreFoundation/String.subproj/CFString_Internal.h +++ b/CoreFoundation/String.subproj/CFString_Internal.h @@ -15,7 +15,7 @@ CF_ASSUME_NONNULL_BEGIN CF_PRIVATE void __CFSetCharToUniCharFunc(CFStringEncodingCheapEightBitToUnicodeProc _Nullable func); -CF_PRIVATE UniChar __CFCharToUniCharTable[256]; +CF_PRIVATE UniChar const * __CFCharToUniCharTable; CF_PRIVATE CFIndex CFUniCharCompatibilityDecompose(UTF32Char *convertedChars, CFIndex length, CFIndex maxBufferLength); __attribute__((cold)) CF_PRIVATE void __CFStringHandleOutOfMemory(CFTypeRef _Nullable obj) CLANG_ANALYZER_NORETURN; diff --git a/CoreFoundation/String.subproj/CFString_Private.h b/CoreFoundation/String.subproj/CFString_Private.h new file mode 100644 index 0000000000..87e1e0ea80 --- /dev/null +++ b/CoreFoundation/String.subproj/CFString_Private.h @@ -0,0 +1,57 @@ +/* CFString_Private.h + Copyright (c) 2020, Apple Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTRING_PRIVATE__) +#define __COREFOUNDATION_CFSTRING_PRIVATE__ 1 + +#include +#include + +CF_ASSUME_NONNULL_BEGIN +CF_EXTERN_C_BEGIN + +CF_EXPORT CFStringRef _Nullable _CFStringCreateTaggedPointerString(const uint8_t *bytes, CFIndex numBytes); + +// Returns a string containing the vocative case of \c givenName based on the language and region of \c locale. +// Not all languages or regions use the vocative case, so very often, this will return \c givenName as-is. +CF_EXPORT CFStringRef _Nullable _CFStringCopyVocativeCaseOfGivenName(CFStringRef givenName, CFLocaleRef locale) API_UNAVAILABLE(macos, ios, watchos, tvos); + +#if __OBJC__ + +/* + Should only be used by CFString or Swift String. + Preconditions: + • buffer is guaranteed to be TAGGED_STRING_CONTAINER_LEN bytes + • str is guaranteed to be tagged + + No encoding conversion will be done, you're expected to already know that you wanted ascii/utf8/latin1 + + Adding additional arguments to this function should be done with care; it's intentionally minimal to avoid pushing a stack frame. + */ +CF_EXPORT CFIndex _NSTaggedPointerStringGetBytes(CFStringRef str, uint8_t * _Nullable buffer) + API_UNAVAILABLE(macos, ios, watchos, tvos); + +/* + Should only be used by CFString or Swift String. + Preconditions: + • str is guaranteed to be tagged + */ +CF_EXPORT CFIndex _NSTaggedPointerStringGetLength(CFStringRef str) + API_UNAVAILABLE(macos, ios, watchos, tvos); + +#endif + +/* + If a process is loading strings manually from an Apple bundle, that process should use this call to ensure that any Markdown is parsed and inflected before using the string. If a process is using CFCopyLocalizedString…, CFBundleCopyLocalizedString, or the Foundation counterparts, this step is unnecessary, as those calls will do it for you if needed. + + Note that only strings from Apple bundles need inflection; all others will just be returned retained. + */ +CF_EXPORT CFStringRef _CFStringCreateByParsingMarkdownAndInflectingIfNeeded(CFStringRef source, CFBundleRef _Nullable originBundle, CFURLRef _Nullable sourceStringsFileURLIfAny) + API_UNAVAILABLE(macos, ios, watchos, tvos); + +CF_EXTERN_C_END +CF_ASSUME_NONNULL_END + +#endif /* ! __COREFOUNDATION_CFSTRING_PRIVATE__ */ + diff --git a/CoreFoundation/StringEncodings.subproj/CFBuiltinConverters.c b/CoreFoundation/StringEncodings.subproj/CFBuiltinConverters.c index b551ecb658..724a790d56 100644 --- a/CoreFoundation/StringEncodings.subproj/CFBuiltinConverters.c +++ b/CoreFoundation/StringEncodings.subproj/CFBuiltinConverters.c @@ -279,139 +279,9 @@ static bool __CFToMacRoman(uint32_t flags, UniChar character, uint8_t *byte) { } } -static const UniChar macRoman_to_uni[128] = { - 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ - 0x00C5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ - 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ - 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ - 0x00D1, /* LATIN CAPITAL LETTER N WITH TILDE */ - 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ - 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ - 0x00E1, /* LATIN SMALL LETTER A WITH ACUTE */ - 0x00E0, /* LATIN SMALL LETTER A WITH GRAVE */ - 0x00E2, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ - 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ - 0x00E3, /* LATIN SMALL LETTER A WITH TILDE */ - 0x00E5, /* LATIN SMALL LETTER A WITH RING ABOVE */ - 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ - 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ - 0x00E8, /* LATIN SMALL LETTER E WITH GRAVE */ - 0x00EA, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */ - 0x00EB, /* LATIN SMALL LETTER E WITH DIAERESIS */ - 0x00ED, /* LATIN SMALL LETTER I WITH ACUTE */ - 0x00EC, /* LATIN SMALL LETTER I WITH GRAVE */ - 0x00EE, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ - 0x00EF, /* LATIN SMALL LETTER I WITH DIAERESIS */ - 0x00F1, /* LATIN SMALL LETTER N WITH TILDE */ - 0x00F3, /* LATIN SMALL LETTER O WITH ACUTE */ - 0x00F2, /* LATIN SMALL LETTER O WITH GRAVE */ - 0x00F4, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ - 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ - 0x00F5, /* LATIN SMALL LETTER O WITH TILDE */ - 0x00FA, /* LATIN SMALL LETTER U WITH ACUTE */ - 0x00F9, /* LATIN SMALL LETTER U WITH GRAVE */ - 0x00FB, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ - 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ - 0x2020, /* DAGGER */ - 0x00B0, /* DEGREE SIGN */ - 0x00A2, /* CENT SIGN */ - 0x00A3, /* POUND SIGN */ - 0x00A7, /* SECTION SIGN */ - 0x2022, /* BULLET */ - 0x00B6, /* PILCROW SIGN */ - 0x00DF, /* LATIN SMALL LETTER SHARP S */ - 0x00AE, /* REGISTERED SIGN */ - 0x00A9, /* COPYRIGHT SIGN */ - 0x2122, /* TRADE MARK SIGN */ - 0x00B4, /* ACUTE ACCENT */ - 0x00A8, /* DIAERESIS */ - 0x2260, /* NOT EQUAL TO */ - 0x00C6, /* LATIN CAPITAL LIGATURE AE */ - 0x00D8, /* LATIN CAPITAL LETTER O WITH STROKE */ - 0x221E, /* INFINITY */ - 0x00B1, /* PLUS-MINUS SIGN */ - 0x2264, /* LESS-THAN OR EQUAL TO */ - 0x2265, /* GREATER-THAN OR EQUAL TO */ - 0x00A5, /* YEN SIGN */ - 0x00B5, /* MICRO SIGN */ - 0x2202, /* PARTIAL DIFFERENTIAL */ - 0x2211, /* N-ARY SUMMATION */ - 0x220F, /* N-ARY PRODUCT */ - 0x03C0, /* GREEK SMALL LETTER PI */ - 0x222B, /* INTEGRAL */ - 0x00AA, /* FEMININE ORDINAL INDICATOR */ - 0x00BA, /* MASCULINE ORDINAL INDICATOR */ - 0x03A9, /* OHM SIGN (Canonical mapping) */ - 0x00E6, /* LATIN SMALL LIGATURE AE */ - 0x00F8, /* LATIN SMALL LETTER O WITH STROKE */ - 0x00BF, /* INVERTED QUESTION MARK */ - 0x00A1, /* INVERTED EXCLAMATION MARK */ - 0x00AC, /* NOT SIGN */ - 0x221A, /* SQUARE ROOT */ - 0x0192, /* LATIN SMALL LETTER F WITH HOOK */ - 0x2248, /* ALMOST EQUAL TO */ - 0x2206, /* INCREMENT */ - 0x00AB, /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ - 0x00BB, /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ - 0x2026, /* HORIZONTAL ELLIPSIS */ - 0x00A0, /* NO-BREAK SPACE */ - 0x00C0, /* LATIN CAPITAL LETTER A WITH GRAVE */ - 0x00C3, /* LATIN CAPITAL LETTER A WITH TILDE */ - 0x00D5, /* LATIN CAPITAL LETTER O WITH TILDE */ - 0x0152, /* LATIN CAPITAL LIGATURE OE */ - 0x0153, /* LATIN SMALL LIGATURE OE */ - 0x2013, /* EN DASH */ - 0x2014, /* EM DASH */ - 0x201C, /* LEFT DOUBLE QUOTATION MARK */ - 0x201D, /* RIGHT DOUBLE QUOTATION MARK */ - 0x2018, /* LEFT SINGLE QUOTATION MARK */ - 0x2019, /* RIGHT SINGLE QUOTATION MARK */ - 0x00F7, /* DIVISION SIGN */ - 0x25CA, /* LOZENGE */ - 0x00FF, /* LATIN SMALL LETTER Y WITH DIAERESIS */ - 0x0178, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ - 0x2044, /* FRACTION SLASH */ - 0x20AC, /* EURO SIGN */ - 0x2039, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK */ - 0x203A, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */ - 0xFB01, /* LATIN SMALL LIGATURE FI */ - 0xFB02, /* LATIN SMALL LIGATURE FL */ - 0x2021, /* DOUBLE DAGGER */ - 0x00B7, /* MIDDLE DOT */ - 0x201A, /* SINGLE LOW-9 QUOTATION MARK */ - 0x201E, /* DOUBLE LOW-9 QUOTATION MARK */ - 0x2030, /* PER MILLE SIGN */ - 0x00C2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ - 0x00CA, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ - 0x00C1, /* LATIN CAPITAL LETTER A WITH ACUTE */ - 0x00CB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ - 0x00C8, /* LATIN CAPITAL LETTER E WITH GRAVE */ - 0x00CD, /* LATIN CAPITAL LETTER I WITH ACUTE */ - 0x00CE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ - 0x00CF, /* LATIN CAPITAL LETTER I WITH DIAERESIS */ - 0x00CC, /* LATIN CAPITAL LETTER I WITH GRAVE */ - 0x00D3, /* LATIN CAPITAL LETTER O WITH ACUTE */ - 0x00D4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ - 0xF8FF, /* Apple logo */ - 0x00D2, /* LATIN CAPITAL LETTER O WITH GRAVE */ - 0x00DA, /* LATIN CAPITAL LETTER U WITH ACUTE */ - 0x00DB, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ - 0x00D9, /* LATIN CAPITAL LETTER U WITH GRAVE */ - 0x0131, /* LATIN SMALL LETTER DOTLESS I */ - 0x02C6, /* MODIFIER LETTER CIRCUMFLEX ACCENT */ - 0x02DC, /* SMALL TILDE */ - 0x00AF, /* MACRON */ - 0x02D8, /* BREVE */ - 0x02D9, /* DOT ABOVE */ - 0x02DA, /* RING ABOVE */ - 0x00B8, /* CEDILLA */ - 0x02DD, /* DOUBLE ACUTE ACCENT */ - 0x02DB, /* OGONEK */ - 0x02C7, /* CARON */ -}; - +#if TARGET_OS_MAC static bool __CFFromMacRoman(uint32_t flags, uint8_t byte, UniChar *character) { - *character = (byte < 0x80 ? (UniChar)byte : macRoman_to_uni[byte - 0x80]); + *character = __CFMacRomanCharToUnicharTable[byte]; return true; } @@ -441,6 +311,7 @@ const CFStringEncodingConverter __CFConverterMacRoman = { .toBytesPrecompose = __CFToMacRomanPrecompose, .isValidCombiningChar = CFStringEncodingIsValidCombiningCharacterForLatin1, }; +#endif /* Win Latin1 (ANSI CodePage 1252) */ #define NUM_1252_FROM_UNI 27 diff --git a/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverter.c b/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverter.c index 6fb1cadb22..a3aca9bb5d 100644 --- a/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverter.c +++ b/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverter.c @@ -557,9 +557,11 @@ CF_INLINE const CFStringEncodingConverter *__CFStringEncodingConverterGetDefinit case kCFStringEncodingUTF8: return &__CFConverterUTF8; +#if TARGET_OS_MAC case kCFStringEncodingMacRoman: return &__CFConverterMacRoman; - +#endif + case kCFStringEncodingWindowsLatin1: return &__CFConverterWinLatin1; @@ -712,7 +714,19 @@ uint32_t CFStringEncodingUnicodeToBytes(uint32_t encoding, uint32_t flags, const if (toBytesPrecompose) { CFIndex localUsedLen = usedLen; - while (isValidCombiningChar(characters[--usedLen])); + while (usedLen > 0) { + usedLen -= 1; + if (usedLen < 0) { + theResult = kCFStringEncodingInvalidInputStream; + break; + } + if (!isValidCombiningChar(characters[usedLen])) { + break; + } + } + if (theResult == kCFStringEncodingInvalidInputStream) { + break; + } theUsedByteLen += localUsedByteLen; if (converter->definition->maxBytesPerChar > 1) { TO_BYTE(converter, flags, characters + usedLen, localUsedLen - usedLen, NULL, 0, &localUsedByteLen); @@ -731,7 +745,11 @@ uint32_t CFStringEncodingUnicodeToBytes(uint32_t encoding, uint32_t flags, const uint8_t lossyByte = CFStringEncodingMaskToLossyByte(flags); if (lossyByte) { - while (isValidCombiningChar(characters[++usedLen])); + while (++usedLen < numChars) { + if (!isValidCombiningChar(characters[usedLen])) { + break; + } + } localUsedByteLen = 1; if (maxByteLen) *(bytes + theUsedByteLen) = lossyByte; } else { @@ -1026,7 +1044,10 @@ CF_PRIVATE const CFStringEncoding *CFStringEncodingListOfAvailableEncodings(void CFQSortArray(list, numSlots, sizeof(CFStringEncoding), (CFComparatorFunction)__CFStringEncodingComparator, NULL); __CFStringEncodingFliterDupes(list, numSlots); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" if (!OSAtomicCompareAndSwapPtrBarrier(NULL, list, (void * volatile *)&encodings) && (list != __CFBuiltinEncodings)) CFAllocatorDeallocate(NULL, list); +#pragma GCC diagnostic pop } return encodings; diff --git a/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverterPriv.h b/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverterPriv.h index 0c5a867f9a..2bf3b60dc0 100644 --- a/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverterPriv.h +++ b/CoreFoundation/StringEncodings.subproj/CFStringEncodingConverterPriv.h @@ -15,11 +15,14 @@ CF_PRIVATE const CFStringEncodingConverter __CFConverterASCII; CF_PRIVATE const CFStringEncodingConverter __CFConverterISOLatin1; -CF_PRIVATE const CFStringEncodingConverter __CFConverterMacRoman; CF_PRIVATE const CFStringEncodingConverter __CFConverterWinLatin1; CF_PRIVATE const CFStringEncodingConverter __CFConverterNextStepLatin; CF_PRIVATE const CFStringEncodingConverter __CFConverterUTF8; +#if TARGET_OS_MAC +CF_PRIVATE const CFStringEncodingConverter __CFConverterMacRoman; +#endif + CF_PRIVATE CFStringEncoding *__CFStringEncodingCreateListOfAvailablePlatformConverters(CFAllocatorRef allocator, CFIndex *numberOfConverters); CF_PRIVATE const CFStringEncodingConverter *__CFStringEncodingGetExternalConverter(uint32_t encoding); CF_PRIVATE CFIndex __CFStringEncodingPlatformUnicodeToBytes(uint32_t encoding, uint32_t flags, const UniChar *characters, CFIndex numChars, CFIndex *usedCharLen, uint8_t *bytes, CFIndex maxByteLen, CFIndex *usedByteLen); diff --git a/CoreFoundation/StringEncodings.subproj/CFUniChar.c b/CoreFoundation/StringEncodings.subproj/CFUniChar.c index 0f632f152f..120e1af7ab 100644 --- a/CoreFoundation/StringEncodings.subproj/CFUniChar.c +++ b/CoreFoundation/StringEncodings.subproj/CFUniChar.c @@ -633,7 +633,7 @@ CF_PRIVATE uint8_t CFUniCharGetBitmapForPlane(uint32_t charset, uint32_t plane, numBytes /= 4; // for 32bit while (numBytes-- > 0) { - unaligned_store32(bitmap, value); + _CFUnalignedStore32(bitmap, value); #if defined (__cplusplus) bitmap = (uint8_t *)bitmap + sizeof(uint32_t); #else @@ -745,7 +745,7 @@ CF_PRIVATE const void *CFUniCharGetMappingData(uint32_t type) { headerSize = *((uint8_t *)bytes); bytes = (uint8_t *)bytes + sizeof(uint32_t); #else bytes += 4; // Skip Unicode version - headerSize = unaligned_load32(bytes); + headerSize = _CFUnalignedLoad32(bytes); bytes += sizeof(uint32_t); #endif headerSize -= (sizeof(uint32_t) * 2); @@ -759,7 +759,7 @@ CF_PRIVATE const void *CFUniCharGetMappingData(uint32_t type) { #if defined (__cplusplus) __CFUniCharMappingTables[idx] = (char *)bodyBase + *((uint32_t *)bytes); bytes = (uint8_t *)bytes + sizeof(uint32_t); #else - __CFUniCharMappingTables[idx] = (char *)bodyBase + unaligned_load32(bytes); + __CFUniCharMappingTables[idx] = (char *)bodyBase + _CFUnalignedLoad32(bytes); bytes += sizeof(uint32_t); #endif } @@ -786,8 +786,8 @@ typedef struct { static uint32_t __CFUniCharGetMappedCase(const __CFUniCharCaseMappings *theTable, uint32_t numElem, UTF32Char character) { const __CFUniCharCaseMappings *p, *q, *divider; -#define READ_KEY(x) unaligned_load32(((uint8_t *)x) + offsetof(__CFUniCharCaseMappings, _key)) -#define READ_VALUE(x) unaligned_load32(((uint8_t *)x) + offsetof(__CFUniCharCaseMappings, _value)) +#define READ_KEY(x) _CFUnalignedLoad32(((uint8_t *)x) + offsetof(__CFUniCharCaseMappings, _key)) +#define READ_VALUE(x) _CFUnalignedLoad32(((uint8_t *)x) + offsetof(__CFUniCharCaseMappings, _value)) if ((character < READ_KEY(&theTable[0])) || (character > READ_KEY(&theTable[numElem-1]))) { return 0; @@ -827,9 +827,9 @@ static bool __CFUniCharLoadCaseMappingTable(void) { __CFUniCharCaseMappingExtraTable = (const uint32_t **)__CFUniCharCaseMappingTable + NUM_CASE_MAP_DATA; for (idx = 0;idx < NUM_CASE_MAP_DATA;idx++) { - countArray[idx] = unaligned_load32(__CFUniCharMappingTables[idx]) / (sizeof(uint32_t) * 2); + countArray[idx] = _CFUnalignedLoad32(__CFUniCharMappingTables[idx]) / (sizeof(uint32_t) * 2); __CFUniCharCaseMappingTable[idx] = ((uint32_t *)__CFUniCharMappingTables[idx]) + 1; - __CFUniCharCaseMappingExtraTable[idx] = (const uint32_t *)((char *)__CFUniCharCaseMappingTable[idx] + unaligned_load32(__CFUniCharMappingTables[idx])); + __CFUniCharCaseMappingExtraTable[idx] = (const uint32_t *)((char *)__CFUniCharCaseMappingTable[idx] + _CFUnalignedLoad32(__CFUniCharMappingTables[idx])); } __CFUniCharCaseMappingTableCounts = countArray; @@ -1043,7 +1043,7 @@ CFIndex CFUniCharMapCaseTo(UTF32Char theChar, UTF16Char *convertedChar, CFIndex } else { CFIndex idx; - for (idx = 0;idx < count;idx++) *(convertedChar++) = (UTF16Char)unaligned_load32(extraMapping++); + for (idx = 0;idx < count;idx++) *(convertedChar++) = (UTF16Char)_CFUnalignedLoad32(extraMapping++); return count; } } @@ -1250,7 +1250,7 @@ const void *CFUniCharGetUnicodePropertyDataForPlane(uint32_t propertyType, uint3 headerSize = CFSwapInt32BigToHost(*((uint32_t *)bytes)); bytes = (uint8_t *)bytes + sizeof(uint32_t); #else bytes += 4; // Skip Unicode version - headerSize = unaligned_load32be(bytes); + headerSize = _CFUnalignedLoad32BE(bytes); bytes += sizeof(uint32_t); #endif @@ -1285,7 +1285,7 @@ const void *CFUniCharGetUnicodePropertyDataForPlane(uint32_t propertyType, uint3 bodyBase = (const uint8_t *)bodyBase + (CFSwapInt32BigToHost(*(uint32_t *)bytes)); ((uint32_t *&)bytes) ++; #else - bodyBase += unaligned_load32be(bytes++); + bodyBase += _CFUnalignedLoad32BE(bytes++); #endif } diff --git a/CoreFoundation/StringEncodings.subproj/CFUnicodeDecomposition.c b/CoreFoundation/StringEncodings.subproj/CFUnicodeDecomposition.c index 832a328831..40c3e62ede 100644 --- a/CoreFoundation/StringEncodings.subproj/CFUnicodeDecomposition.c +++ b/CoreFoundation/StringEncodings.subproj/CFUnicodeDecomposition.c @@ -24,24 +24,20 @@ static UTF32Char *__CFUniCharMultipleDecompositionTable = NULL; static const uint8_t *__CFUniCharDecomposableBitmapForBMP = NULL; static const uint8_t *__CFUniCharHFSPlusDecomposableBitmapForBMP = NULL; -static os_unfair_lock __CFUniCharDecompositionTableLock = OS_UNFAIR_LOCK_INIT; +static dispatch_once_t once; static const uint8_t **__CFUniCharCombiningPriorityTable = NULL; static uint8_t __CFUniCharCombiningPriorityTableNumPlane = 0; static void __CFUniCharLoadDecompositionTable(void) { - - os_unfair_lock_lock(&__CFUniCharDecompositionTableLock); - - if (NULL == __CFUniCharDecompositionTable) { + dispatch_once(&once, ^{ const uint32_t *bytes = (uint32_t *)CFUniCharGetMappingData(kCFUniCharCanonicalDecompMapping); if (NULL == bytes) { - os_unfair_lock_unlock(&__CFUniCharDecompositionTableLock); return; } - __CFUniCharDecompositionTableLength = unaligned_load32(bytes++); + __CFUniCharDecompositionTableLength = _CFUnalignedLoad32(bytes++); __CFUniCharDecompositionTable = (UTF32Char *)bytes; __CFUniCharMultipleDecompositionTable = (UTF32Char *)((intptr_t)bytes + __CFUniCharDecompositionTableLength); @@ -54,9 +50,7 @@ static void __CFUniCharLoadDecompositionTable(void) { __CFUniCharCombiningPriorityTableNumPlane = CFUniCharGetNumberOfPlanesForUnicodePropertyData(kCFUniCharCombiningProperty); __CFUniCharCombiningPriorityTable = (const uint8_t **)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(uint8_t *) * __CFUniCharCombiningPriorityTableNumPlane, 0); for (idx = 0;idx < __CFUniCharCombiningPriorityTableNumPlane;idx++) __CFUniCharCombiningPriorityTable[idx] = (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, idx); - } - - os_unfair_lock_unlock(&__CFUniCharDecompositionTableLock); + }); } static CFLock_t __CFUniCharCompatibilityDecompositionTableLock = CFLockInit; @@ -102,8 +96,8 @@ typedef struct { static uint32_t __CFUniCharGetMappedValue(const __CFUniCharDecomposeMappings *theTable, uint32_t numElem, UTF32Char character) { const __CFUniCharDecomposeMappings *p, *q, *divider; -#define READ_KEY(x) unaligned_load32(((uint8_t *)x) + offsetof(__CFUniCharDecomposeMappings, _key)) -#define READ_VALUE(x) unaligned_load32(((uint8_t *)x) + offsetof(__CFUniCharDecomposeMappings, _value)) +#define READ_KEY(x) _CFUnalignedLoad32(((uint8_t *)x) + offsetof(__CFUniCharDecomposeMappings, _key)) +#define READ_VALUE(x) _CFUnalignedLoad32(((uint8_t *)x) + offsetof(__CFUniCharDecomposeMappings, _value)) if ((character < READ_KEY(&theTable[0])) || (character > READ_KEY(&theTable[numElem-1]))) { return 0; @@ -168,7 +162,7 @@ static CFIndex __CFUniCharRecursivelyDecomposeCharacter(UTF32Char character, UTF usedLength += length; - while (length--) *(convertedChars++) = unaligned_load32(mappings++); + while (length--) *(convertedChars++) = _CFUnalignedLoad32(mappings++); return usedLength; } @@ -184,7 +178,7 @@ static CFIndex __CFUniCharRecursivelyDecomposeCharacter(UTF32Char character, UTF #define HANGUL_NCOUNT (HANGUL_VCOUNT * HANGUL_TCOUNT) CFIndex CFUniCharDecomposeCharacter(UTF32Char character, UTF32Char *convertedChars, CFIndex maxBufferLength) { - if (NULL == __CFUniCharDecompositionTable) __CFUniCharLoadDecompositionTable(); + __CFUniCharLoadDecompositionTable(); if (character >= HANGUL_SBASE && character <= (HANGUL_SBASE + HANGUL_SCOUNT)) { CFIndex length; @@ -223,7 +217,7 @@ bool CFUniCharDecomposeWithErrorLocation(const UTF16Char *src, CFIndex length, C // kCFNotFound indicates an insufficiently sized buffer, which is the default failure case. if (charIndex) *charIndex = kCFNotFound; - if (NULL == __CFUniCharDecompositionTable) __CFUniCharLoadDecompositionTable(); + __CFUniCharLoadDecompositionTable(); while ((length - segmentLength) > 0) { currentChar = *(src++); diff --git a/CoreFoundation/URL.subproj/CFURL.c b/CoreFoundation/URL.subproj/CFURL.c index 5aae169ebc..b6ec08f766 100644 --- a/CoreFoundation/URL.subproj/CFURL.c +++ b/CoreFoundation/URL.subproj/CFURL.c @@ -16,16 +16,16 @@ #include "CFRuntime_Internal.h" #include #include -#include #include #include #include #include #include -#if TARGET_OS_MAC || TARGET_OS_LINUX +#if TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD #if TARGET_OS_OSX #include #endif +#include #include #include #include @@ -953,7 +953,7 @@ static CFStringRef CreateStringFromFileSystemRepresentationByAddingPercentEscape CF_INLINE CFStringRef _replacePathIllegalCharacters(CFStringRef str, CFAllocatorRef alloc, Boolean preserveSlashes) CF_RETURNS_RETAINED { CFStringRef result = NULL; CFIndex strlength = CFStringGetLength(str); - CFIndex bufferSize = CFStringGetMaximumSizeForEncoding(((strlength != 0) ? strlength : 1), kCFStringEncodingUTF8); + CFIndex bufferSize = CFStringGetMaximumSizeForEncoding(((strlength != 0) ? strlength : 1), kCFStringEncodingUTF8) + 1; STACK_BUFFER_DECL(char, stackBuffer, STACK_BUFFER_SIZE); char *bufferPtr; if ( bufferSize <= STACK_BUFFER_SIZE ) { @@ -2413,9 +2413,64 @@ CFURLRef _CFURLCopyFileURL(CFURLRef url) return ( result ); } +// CreateURLStringWithBytes is used by CFURLCreateWithBytes and CFURLCreateAbsoluteURLWithBytes create the URL string from bytes. It ensures: +// 1. The encoding is a superset of ASCII. If the encoding is not a superset of ASCII, URLs created with bytes will not work with CFURLGetByteRangeForComponent. This is documented in CFURL.h and in official documentation. +// 2. The bytes returned by CFURLGetBytes will be exactly the same bytes passed to CFURLCreateWithBytes and CFURLCreateAbsoluteURLWithBytes. +static CFStringRef _CFURLCreateURLStringWithBytes(CFAllocatorRef allocator, const uint8_t *bytes, CFIndex length, CFStringEncoding encoding) +{ + CFStringRef result = NULL; + // guarantee the encoding is a superset of ASCII + if ( __CFStringEncodingIsSupersetOfASCII(encoding) ) { + result = CFStringCreateWithBytes(allocator, bytes, length, encoding, false); + if ( result ) { + Boolean freeBuffer = false; + // attempt to get to the bytes cheaply with CFStringGetCStringPtr + UInt8 *buffer = (UInt8 *)CFStringGetCStringPtr(result, encoding); + if ( buffer ) { + // guarantee length in == length out + if ( length != strlen((char *)buffer) ) { + // length was different - set buffer to NULL + buffer = NULL; + } + } + else { + // get the bytes out of the string the more expensive way + CFIndex stringLength = CFStringGetLength(result); + CFIndex bufferSize = CFStringGetMaximumSizeForEncoding(stringLength, encoding) + 1; // make sure the buffer is at least 1 byte in size + buffer = (UInt8 *)malloc(bufferSize); + if ( buffer ) { + CFIndex usedLength = 0; + CFStringGetBytes(result, CFRangeMake(0, stringLength), encoding, 0 /* lossByte */, false /* isExternalRepresentation */, buffer, bufferSize, &usedLength); + // guarantee length in == length out + if ( length != usedLength ) { + // length was different - free buffer and set it to NULL + free(buffer); + buffer = NULL; + } + else { + freeBuffer = true; + } + } + } + // guarantee the bytes used to create the URL string are the same bytes that come out of the URL string + if ( (buffer == NULL) || (memcmp(bytes, buffer, length) != 0) ) { + // the length or bytes were different + CFRelease(result); + result = NULL; + } + + if ( freeBuffer ) { + free(buffer); + } + } + } + return ( result ); +} + // encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes. CFURLRef CFURLCreateWithBytes(CFAllocatorRef allocator, const uint8_t *URLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL) { - CFStringRef urlString = CFStringCreateWithBytes(allocator, URLBytes, length, encoding, false); + CFStringRef urlString; + urlString = _CFURLCreateURLStringWithBytes(allocator, URLBytes, length, encoding); CFURLRef result = NULL; if ( urlString ) { result = _CFURLCreateWithURLString(allocator, urlString, false /* checkForLegalCharacters */, baseURL); @@ -2515,8 +2570,7 @@ CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *rela Boolean absStringIsMutable = false; CFURLRef absURL; CFStringRef relativeString; - - relativeString = CFStringCreateWithBytes(alloc, relativeURLBytes, length, encoding, false); + relativeString = _CFURLCreateURLStringWithBytes(alloc, relativeURLBytes, length, encoding); if ( relativeString != NULL ) { if (!baseURL) { absString = relativeString; @@ -2975,6 +3029,23 @@ CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) { } } +CFIndex CFURLGetBytesUsingEncoding(CFURLRef url, UInt8 *buffer, CFIndex bufferLength, CFStringEncoding enc) { + CFIndex length, charsConverted, usedLength; + CFStringRef string; + if (CF_IS_OBJC(CFURLGetTypeID(), url)) { + string = CFURLGetString(url); + } else { + string = url->_string; + } + length = CFStringGetLength(string); + charsConverted = CFStringGetBytes(string, CFRangeMake(0, length), enc, 0, false, buffer, bufferLength, &usedLength); + if (charsConverted != length) { + return -1; + } else { + return usedLength; + } +} + CFURLRef CFURLGetBaseURL(CFURLRef anURL) { CF_OBJC_FUNCDISPATCHV(CFURLGetTypeID(), CFURLRef, (NSURL *)anURL, baseURL); return anURL->_base; @@ -4463,13 +4534,20 @@ CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLR if ( relPath ) { // relPath is not absolute if it is zero length or doesn't start with a slash CFIndex length = CFStringGetLength(relPath); - Boolean relPathIsRelative = TRUE; + Boolean relPathIsRelative = true; switch (fsType) { case kCFURLPOSIXPathStyle: - relPathIsRelative = length > 0 && CFStringGetCharacterAtIndex(relPath, 0) != '/'; + if (length > 0) { + relPathIsRelative = CFStringGetCharacterAtIndex(relPath, 0) != '/'; + } break; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" case kCFURLHFSPathStyle: - relPathIsRelative = length > 0 && CFStringGetCharacterAtIndex(relPath, 0) == ':'; +#pragma clang diagnostic pop + if (length > 0) { + relPathIsRelative = CFStringGetCharacterAtIndex(relPath, 0) == ':'; + } break; case kCFURLWindowsPathStyle: // In theory, on TARGET_DEPLOYMENT_WINDOWS we could use @@ -4477,14 +4555,14 @@ CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLR // UNC paths are always absolute if (length > 2 && CFStringGetCharacterAtIndex(relPath, 0) == '\\' && CFStringGetCharacterAtIndex(relPath, 1) == '\\') - relPathIsRelative = FALSE; + relPathIsRelative = false; else if (length > 1 && CFStringGetCharacterAtIndex(relPath, 1) == ':') // This is not really ideal, but, since a drive letter a single // character, and ':' is not a valid path character, and ':' is // reserved for the ADS separator, this works. - relPathIsRelative = FALSE; + relPathIsRelative = false; else - relPathIsRelative = TRUE; + relPathIsRelative = true; break; } @@ -4794,7 +4872,7 @@ CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRe #endif CFMutableStringRef newString; - CFStringRef newComp = NULL; + CFStringRef newComp; CFRange pathRg; if (!(url->_flags & HAS_PATH)) { result = NULL; @@ -4813,7 +4891,7 @@ CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRe newComp = CFURLCreateStringByAddingPercentEscapes(allocator, pathComponent, NULL, CFSTR(";?"), url->_encoding); #pragma GCC diagnostic pop } - if ( newComp && CFStringGetLength(newComp) > 0 ) { + if ( newComp ) { pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH); if ( (!pathRg.length || CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') && (CFStringGetCharacterAtIndex(newComp, 0) != '/') ) { CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/")); @@ -4823,9 +4901,9 @@ CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRe if (isDirectory) { CFStringInsert(newString, pathRg.location + pathRg.length + CFStringGetLength(newComp), CFSTR("/")); } + CFRelease(newComp); result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); } - if (newComp) CFRelease(newComp); CFRelease(newString); } } @@ -5166,7 +5244,7 @@ CFURLRef CFURLCreateFilePathURL(CFAllocatorRef alloc, CFURLRef url, CFErrorRef * Boolean hasScheme; if (!_CFURLHasFileURLScheme(url, &hasScheme)) { if ( !hasScheme ) { - CFLog(kCFLogLevelWarning, CFSTR("CFURLCreateFilePathURL failed because it was passed an URL which has no scheme")); + CFLog(kCFLogLevelWarning, CFSTR("CFURLCreateFilePathURL failed because it was passed a URL which has no scheme")); } if ( error ) { *error = CFErrorCreate( kCFAllocatorDefault, kCFErrorDomainCocoa, kCFURLReadUnsupportedSchemeError, NULL ); @@ -5403,6 +5481,8 @@ CONST_STRING_DECL(kCFURLVolumeSupportsSwapRenamingKey, "NSURLVolumeSupportsSwapR CONST_STRING_DECL(kCFURLVolumeSupportsExclusiveRenamingKey, "NSURLVolumeSupportsExclusiveRenamingKey"); CONST_STRING_DECL(kCFURLVolumeSupportsImmutableFilesKey, "NSURLVolumeSupportsImmutableFilesKey"); CONST_STRING_DECL(kCFURLVolumeSupportsAccessPermissionsKey, "NSURLVolumeSupportsAccessPermissionsKey"); +CONST_STRING_DECL(kCFURLVolumeSupportsFileProtectionKey, "NSURLVolumeSupportsFileProtectionKey"); +CONST_STRING_DECL(_kCFURLVolumeSupportsFileProtectionKey, "NSURLVolumeSupportsFileProtectionKey"); CONST_STRING_DECL(kCFURLIsUbiquitousItemKey, "NSURLIsUbiquitousItemKey"); CONST_STRING_DECL(kCFURLUbiquitousItemHasUnresolvedConflictsKey, "NSURLUbiquitousItemHasUnresolvedConflictsKey"); CONST_STRING_DECL(kCFURLUbiquitousItemIsDownloadedKey, "NSURLUbiquitousItemIsDownloadedKey"); @@ -5414,6 +5494,7 @@ CONST_STRING_DECL(kCFURLUbiquitousItemPercentDownloadedKey, "NSURLUbiquitousItem CONST_STRING_DECL(kCFURLUbiquitousItemPercentUploadedKey, "NSURLUbiquitousItemPercentUploadedKey"); CONST_STRING_DECL(kCFURLUbiquitousItemDownloadingErrorKey, "NSURLUbiquitousItemDownloadingErrorKey"); CONST_STRING_DECL(kCFURLUbiquitousItemUploadingErrorKey, "NSURLUbiquitousItemUploadingErrorKey"); +CONST_STRING_DECL(kCFURLUbiquitousItemIsExcludedFromSyncKey, "NSURLUbiquitousItemIsExcludedFromSyncKey"); CONST_STRING_DECL(kCFURLUbiquitousItemDownloadingStatusNotDownloaded, "NSURLUbiquitousItemDownloadingStatusNotDownloaded"); CONST_STRING_DECL(kCFURLUbiquitousItemDownloadingStatusDownloaded, "NSURLUbiquitousItemDownloadingStatusDownloaded"); CONST_STRING_DECL(kCFURLUbiquitousItemDownloadingStatusCurrent, "NSURLUbiquitousItemDownloadingStatusCurrent"); diff --git a/CoreFoundation/URL.subproj/CFURL.h b/CoreFoundation/URL.subproj/CFURL.h index e0390929f7..fb8ca57d4a 100644 --- a/CoreFoundation/URL.subproj/CFURL.h +++ b/CoreFoundation/URL.subproj/CFURL.h @@ -286,7 +286,13 @@ CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef /* are placed in buffer. If buffer is NULL, the needed length is */ /* computed and returned. The returned bytes are the original bytes */ /* from which the URL was created; if the URL was created from a */ -/* string, the bytes will be the bytes of the string encoded via UTF-8 */ +/* string, the bytes will be the bytes of the string encoded via UTF-8. */ +/* */ +/* Note: Due to incompatibilities between encodings, it might be impossible to */ +/* generate bytes from the base URL in the encoding of the relative URL or relative bytes, */ +/* which will cause this method to fail and return -1, even if a NULL buffer is passed. */ +/* To avoid this scenario, use UTF-8, UTF-16, or UTF-32 encodings exclusively, or */ +/* use one non-Unicode encoding exclusively. */ CF_EXPORT CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength); @@ -738,6 +744,26 @@ CF_EXPORT const CFStringRef kCFURLAttributeModificationDateKey API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); /* The time the resource's attributes were last modified (Read-only, value type CFDate) */ +CF_EXPORT +const CFStringRef kCFURLFileContentIdentifierKey API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + /* A 64-bit value assigned by APFS that identifies a file's content data stream. Only cloned files and their originals can have the same identifier. (CFNumber) */ + +CF_EXPORT +const CFStringRef kCFURLMayShareFileContentKey API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + /* True for cloned files and their originals that may share all, some, or no data blocks. (CFBoolean) */ + +CF_EXPORT +const CFStringRef kCFURLMayHaveExtendedAttributesKey API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + /* True if the file has extended attributes. False guarantees there are none. (CFBoolean) */ + +CF_EXPORT +const CFStringRef kCFURLIsPurgeableKey API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + /* True if the file can be deleted by the file system when asked to free space. (CFBoolean) */ + +CF_EXPORT +const CFStringRef kCFURLIsSparseKey API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + /* True if the file has sparse regions. (CFBoolean) */ + CF_EXPORT const CFStringRef kCFURLLinkCountKey API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); /* Number of hard links to the resource (Read-only, value type CFNumber) */ @@ -751,7 +777,7 @@ const CFStringRef kCFURLVolumeURLKey API_AVAILABLE(macos(10.6), ios(4.0), watcho /* URL of the volume on which the resource is stored (Read-only, value type CFURL) */ CF_EXPORT -const CFStringRef kCFURLTypeIdentifierKey API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); +const CFStringRef kCFURLTypeIdentifierKey API_DEPRECATED("Use NSURLContentTypeKey instead", macos(10.6, API_TO_BE_DEPRECATED), ios(4.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED)); /* Uniform type identifier (UTI) for the resource (Read-only, value type CFString) */ CF_EXPORT @@ -1077,6 +1103,10 @@ CF_EXPORT const CFStringRef kCFURLVolumeSupportsAccessPermissionsKey API_AVAILABLE(macosx(10.13), ios(11.0), watchos(4.0), tvos(11.0)); /* true if the volume supports setting POSIX access permissions with the kCFURLFileSecurityKey property (Read-only, value type CFBoolean) */ +CF_EXPORT +const CFStringRef kCFURLVolumeSupportsFileProtectionKey API_AVAILABLE(macosx(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + /* true if the volume supports data protection for files (see kCFURLFileProtectionKey). (Read-only, value type CFBoolean) */ + /* UbiquitousItem Properties */ CF_EXPORT @@ -1123,6 +1153,10 @@ CF_EXPORT const CFStringRef kCFURLUbiquitousItemUploadingErrorKey API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0)); /* returns the error when uploading the item to iCloud failed. See the NSUbiquitousFile section in FoundationErrors.h. (Read-only, value type CFError) */ +CF_EXPORT +const CFStringRef kCFURLUbiquitousItemIsExcludedFromSyncKey API_AVAILABLE(macos(11.3), ios(14.5), watchos(7.4), tvos(14.5)); + /* the item is excluded from sync, which means it is locally on disk but won't be available on the server. An excluded item is no longer ubiquitous. */ + /* The values returned for kCFURLUbiquitousItemDownloadingStatusKey */ CF_EXPORT @@ -1144,6 +1178,7 @@ typedef CF_OPTIONS(CFOptionFlags, CFURLBookmarkCreationOptions) { kCFURLBookmarkCreationSuitableForBookmarkFile = ( 1UL << 10 ), // include the properties required by CFURLWriteBookmarkDataToFile() in the bookmark data created kCFURLBookmarkCreationWithSecurityScope API_AVAILABLE(macos(10.7)) API_UNAVAILABLE(ios, watchos, tvos) = ( 1UL << 11 ), // Mac OS X 10.7.3 and later, include information in the bookmark data which allows the same sandboxed process to access the resource after being relaunched kCFURLBookmarkCreationSecurityScopeAllowOnlyReadAccess API_AVAILABLE(macos(10.7)) API_UNAVAILABLE(ios, watchos, tvos) = ( 1UL << 12 ), // Mac OS X 10.7.3 and later, if used with kCFURLBookmarkCreationWithSecurityScope, at resolution time only read access to the resource will be granted + kCFURLBookmarkCreationWithoutImplicitSecurityScope API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)) = (1 << 29), // Disable automatic embedding of an implicit security scope. The resolving process will not be able gain access to the resource by security scope, either implicitly or explicitly, through the returned URL. Not applicable to security-scoped bookmarks. // deprecated kCFURLBookmarkCreationPreferFileIDResolutionMask @@ -1154,7 +1189,8 @@ typedef CF_OPTIONS(CFOptionFlags, CFURLBookmarkResolutionOptions) { kCFURLBookmarkResolutionWithoutUIMask = ( 1UL << 8 ), // don't perform any user interaction during bookmark resolution kCFURLBookmarkResolutionWithoutMountingMask = ( 1UL << 9 ), // don't mount a volume during bookmark resolution kCFURLBookmarkResolutionWithSecurityScope API_AVAILABLE(macos(10.7)) API_UNAVAILABLE(ios, watchos, tvos) = ( 1UL << 10 ), // Mac OS X 10.7.3 and later, use the secure information included at creation time to provide the ability to access the resource in a sandboxed process. - + kCFURLBookmarkResolutionWithoutImplicitStartAccessing API_AVAILABLE(macos(11.2), ios(14.2), watchos(7.2), tvos(14.2)) = ( 1 << 15 ), // Disable implicitly starting access of the ephemeral security-scoped resource during resolution. Instead, call `CFURLStartAccessingSecurityScopedResource` on the returned URL when ready to use the resource. Not applicable to security-scoped bookmarks. + kCFBookmarkResolutionWithoutUIMask = kCFURLBookmarkResolutionWithoutUIMask, kCFBookmarkResolutionWithoutMountingMask = kCFURLBookmarkResolutionWithoutMountingMask, } API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); @@ -1196,7 +1232,7 @@ Boolean CFURLWriteBookmarkDataToFile( CFDataRef bookmarkRef, CFURLRef fileURL, C /* Returns bookmark data derived from an alias record. */ CF_EXPORT -CFDataRef CFURLCreateBookmarkDataFromAliasRecord ( CFAllocatorRef allocatorRef, CFDataRef aliasRecordDataRef ) API_AVAILABLE(macos(10.6)) API_UNAVAILABLE(ios, watchos, tvos); +CFDataRef CFURLCreateBookmarkDataFromAliasRecord ( CFAllocatorRef allocatorRef, CFDataRef aliasRecordDataRef ) API_DEPRECATED("The Carbon Alias Manager is deprecated. This function should only be used to convert Carbon AliasRecords to bookmark data.", macos(10.6,10.16)) API_UNAVAILABLE(ios, watchos, tvos); CF_IMPLICIT_BRIDGING_ENABLED diff --git a/CoreFoundation/URL.subproj/CFURLAccess.c b/CoreFoundation/URL.subproj/CFURLAccess.c index 608d91eb5e..b404741173 100644 --- a/CoreFoundation/URL.subproj/CFURLAccess.c +++ b/CoreFoundation/URL.subproj/CFURLAccess.c @@ -24,7 +24,7 @@ CFData read/write routines #include #include #include -#if TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_LINUX || TARGET_OS_BSD || TARGET_OS_WASI +#if TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD || TARGET_OS_WASI #include #include #include @@ -43,7 +43,7 @@ CFData read/write routines #else #error Unknown or unspecified DEPLOYMENT_TARGET #endif -#if TARGET_OS_OSX || TARGET_OS_IOS +#if TARGET_OS_MAC #include #endif @@ -236,7 +236,7 @@ static Boolean _CFFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef CFNumberRef modeNum = (CFNumberRef)value; CFNumberGetValue(modeNum, kCFNumberSInt32Type, &mode); } else { -#if TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_LINUX || TARGET_OS_BSD || TARGET_OS_WASI +#if TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD || TARGET_OS_WASI #define MODE_TYPE mode_t #elif TARGET_OS_WIN32 #define MODE_TYPE uint16_t diff --git a/CoreFoundation/URL.subproj/CFURLPriv.h b/CoreFoundation/URL.subproj/CFURLPriv.h index a600fc5acf..d61acc909b 100644 --- a/CoreFoundation/URL.subproj/CFURLPriv.h +++ b/CoreFoundation/URL.subproj/CFURLPriv.h @@ -24,6 +24,12 @@ CF_EXTERN_C_BEGIN +/* Like CFURLGetBytes(), but allows the output encoding to be specified. */ +CF_EXPORT +CFIndex CFURLGetBytesUsingEncoding(CFURLRef url, UInt8 *buffer, CFIndex bufferLength, CFStringEncoding encoding); + +#pragma mark - FileURL + // The kCFURLxxxxError enums are error codes in the Cocoa error domain and they mirror the exact same codes in (i.e. kCFURLReadNoPermissionError = NSFileReadNoPermissionError = 257). They were added to CFURLPriv.h so that CarbonCore and later CoreServicesInternal could return these error codes in the Cocoa error domain. If your code links with Foundation, you should use the codes in , not these codes. enum { // Resource I/O related errors, with kCFErrorURLKey containing URL @@ -75,6 +81,9 @@ CF_EXPORT const CFStringRef _kCFURLNameExtensionKey API_AVAILABLE(macos(10.6), i CF_EXPORT const CFStringRef _kCFURLFinderInfoKey API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); /* A 16-byte Finder Info structure immediately followed by a 16-byte Extended Finder Info structure (CFData) */ +CF_EXPORT const CFStringRef _kCFURLHFSTypeCodeKey API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0)); + /* A legacy 4-character code which identifies the file type. (CFNumber) */ + CF_EXPORT const CFStringRef _kCFURLIsUserNoDumpKey API_AVAILABLE(macos(10.11), ios(9.0), watchos(2.0), tvos(9.0)); /* True if resource's UF_NODUMP flag is set (CFBoolean) */ @@ -123,6 +132,12 @@ CF_EXPORT const CFStringRef _kCFURLIsApplicationKey API_DEPRECATED("Use kCFURLIs CF_EXPORT const CFStringRef _kCFURLApplicationIsAppletKey API_AVAILABLE(macos(10.11)) API_UNAVAILABLE(ios, watchos, tvos); /* The item is an OSA or Automator applet. Only applies to applications. (Read-only, value type CFBoolean) */ +CF_EXPORT const CFStringRef _kCFURLApplicationIsPlaceholderKey API_AVAILABLE(macos(11.2), ios(14.2), watchos(7.2), tvos(14.2)); +/* The item is a placeholder while an app installs or is an uninstalled iOS system app. Only applies to applications. (Read-only, value type CFBoolean) */ + +CF_EXPORT const CFStringRef _kCFURLApplicationIsBetaKey API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0)); +/* The item is a TestFlight beta app. (Read-only, value type CFBoolean) */ + CF_EXPORT const CFStringRef _kCFURLApplicationHasSupportedFormatKey API_AVAILABLE(macos(10.11)) API_UNAVAILABLE(ios, watchos, tvos); /* The item is an application that can be executed on the current system. (Read-only, value type CFBoolean) */ @@ -241,6 +256,13 @@ CF_EXPORT const CFStringRef _kCFURLApplicationPrefersExternalGPUKey API_AVAILABL CF_EXPORT const CFStringRef _kCFURLCanSetApplicationPrefersExternalGPUKey API_AVAILABLE(macos(10.14)) API_UNAVAILABLE(ios, watchos, tvos); /* False if app’s Info.plist specifies a eGPU policy, True if app does not specify an policy. Finder does not show a checkbox when this value is false. (Read-only, CFBoolean) */ +#if !RC_HIDE_J316 +CF_EXPORT const CFStringRef _kCFURLApplicationPrefersSafeApertureSystemFullScreenCompatibilityKey API_AVAILABLE(macos(12.0)) API_UNAVAILABLE(ios, watchos, tvos); +CF_EXPORT const CFStringRef _kCFURLApplicationPrefersSafeApertureAppFullScreenCompatibilityKey API_AVAILABLE(macos(12.0)) API_UNAVAILABLE(ios, watchos, tvos); +CF_EXPORT const CFStringRef _kCFURLApplicationPrefersSafeApertureWindowedCompatibilityKey API_AVAILABLE(macos(12.0)) API_UNAVAILABLE(ios, watchos, tvos); +CF_EXPORT const CFStringRef _kCFURLCanSetApplicationPrefersSafeApertureWindowedCompatibilityKey API_AVAILABLE(macos(12.0)) API_UNAVAILABLE(ios, watchos, tvos); +#endif // RC_HIDE_J316 + CF_EXPORT const CFStringRef _kCFURLApplicationDeviceManagementPolicyKey API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)); /* For app bundle URLs, value is the Device Management framework's policy for the application. If the value is unavailable, returns DMFPolicyOK. For non-app URLs, value is nil. The calling process must be properly entitled with the Device Management framework to use this property. (Read-only, value type CFNumber) */ @@ -253,6 +275,9 @@ CF_EXPORT const CFStringRef _kCFURLIsExcludedFromUnencryptedBackupKey API_AVAILA CF_EXPORT const CFStringRef _kCFURLDeviceRefNumKey API_AVAILABLE(macos(10.15)) API_UNAVAILABLE(ios, watchos, tvos); /* an unique per-volume non-persistent identifier for volumes (much like _kCFURLVolumeRefNumKey) that is also unique per-device when the volume is really two devices (i.e. ROSP) (64-bit integer CFNumber). */ +CF_EXPORT const CFStringRef _kCFURLContentTypeKey API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); + /* the file type of the resource (CFTypeRef/UTType *). */ + /* Additional volume properties */ CF_EXPORT const CFStringRef _kCFURLVolumeRefNumKey API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); @@ -300,6 +325,9 @@ CF_EXPORT const CFStringRef _kCFURLDiskImageBackingURLKey API_AVAILABLE(macos(10 CF_EXPORT const CFStringRef _kCFURLVolumeIsFileVaultKey API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); /* Volume uses File Vault encryption (CFBoolean) */ +CF_EXPORT const CFStringRef _kCFURLVolumeSupportsFileProtectionKey API_DEPRECATED("Use the kCFURLVolumeSupportsFileProtectionKey or NSURLVolumeSupportsFileProtectionKey public property keys", macosx(10.16, 10.16), ios(14.0, 14.0), watchos(7.0, 7.0), tvos(14.0, 14.0)); + /* true if the volume supports data protection for files. (Read-only, value type CFBoolean) */ + CF_EXPORT const CFStringRef _kCFURLVolumeIsiDiskKey API_DEPRECATED("No supported", macos(10.6,10.9), ios(4.0,7.0), watchos(2.0,2.0), tvos(9.0,9.0)); /* Deprecated and scheduled for removal in 10.10/8.0 - there are no more iDisks */ @@ -552,7 +580,9 @@ typedef CF_OPTIONS(unsigned long long, CFURLVolumePropertyFlags) { = 0x4000000LL, kCFURLVolumeIsEncrypted API_AVAILABLE(macos(10.11), ios(9.0), watchos(2.0), tvos(9.0)) = 0x8000000LL, - + kCFURLVolumeSupportsFileProtection API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)) + = 0x10000000LL, + // IMPORTANT: The values of the following flags must stay in sync with the // VolumeCapabilities flags in CarbonCore (FileIDTreeStorage.h) kCFURLVolumeSupportsPersistentIDs = 0x100000000LL, @@ -740,10 +770,18 @@ void _CFURLAttachSecurityScopeToFileURL(CFURLRef url, CFDataRef sandboxExtension CF_EXPORT CFDataRef _CFURLCopySecurityScopeFromFileURL(CFURLRef url) API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); +/** _CFURLNoteSecurityScopedResourceMove should be called preserve access to a scoped resource after it has been moved on the file system. + The process will lose access to sourceURL and gain access to destinationURL. For use by only FileCoordination! + */ +CF_EXPORT +Boolean _CFURLNoteSecurityScopedResourceMoved(CFURLRef sourceURL, CFURLRef destinationURL) API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0)); + CF_EXPORT void _CFURLSetPermanentResourcePropertyForKey(CFURLRef url, CFStringRef key, CFTypeRef propertyValue) API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); +#pragma mark - Bookmarks + // Returns a string describing the bookmark data. For debugging purposes only. CF_EXPORT CFStringRef _CFURLBookmarkCopyDescription(CFDataRef bookmarkRef) API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); @@ -751,6 +789,7 @@ CFStringRef _CFURLBookmarkCopyDescription(CFDataRef bookmarkRef) API_AVAILABLE(m #if TARGET_OS_MAC || TARGET_OS_IPHONE // private CFURLBookmarkCreationOptions enum { + kCFURLBookmarkCreationSecurityScopeRevocable API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos) = ( 1 << 25 ), // if used with kCFURLBookmarkCreationWithSecurityScope, the bookmark can be revoked on a per-app basis. kCFURLBookmarkCreationWithFileProvider API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)) = ( 1UL << 26 ), // private option to create bookmarks with file provider string. The file provider string overrides the rest of the bookmark data at resolution time. kCFURLBookmarkOperatingInsideScopedBookmarksAgent = (1UL << 27), // private option used internally by ScopedBookmarkAgent to prevent recursion between the agent and the framework code. Available 10_7, NA kCFURLBookmarkCreationAllowCreationIfResourceDoesNotExistMask = ( 1UL << 28 ), // allow creation of a bookmark to a file: scheme with a CFURLRef of item which may not exist. If the filesystem item does not exist, the created bookmark contains essentially no properties beyond the url string. Available 10_7, 5_0. @@ -772,6 +811,8 @@ enum { kCFBookmarkResolutionPerformRelativeResolutionFirstMask API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0)) = ( 1UL << 11 ), // perform relative resolution before absolute resolution. If this bit is set, for this to be useful a relative URL must also have been passed in and the bookmark when created must have been created relative to another url. kCFURLBookmarkResolutionAllowingPromisedItem API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)) = ( 1UL << 12 ), // If kCFURLBookmarkResolutionAllowingPromisedItem is set, resolving a bookmark may return promise item URL if the target has been evicted to the cloud (instead of downloading the evicted document during bookmark resolution). Clients must use NSPromisedItems and NSFileCoordinator API to access promised item URLs. kCFURLBookmarkResolutionAllowingPromisedItem is ignored when resolving security-scoped bookmarks. kCFBookmarkResolutionQuarantineMountedNetworkVolumesMask API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)) = ( 1UL << 13 ), // quarantine any network volume mounted during resolution + kCFURLBookmarkResolutionFailPromisedItem API_AVAILABLE(macosx(11.0), ios(14.0), watchos(7.0), tvos(14.0)) = ( 1UL << 14 ), // If kCFURLBookmarkResolutionFailPromisedItem is set, resolving a bookmark will fail if the target has been evicted to the cloud (instead of downloading the evicted document during bookmark resolution). + kCFURLBookmarkResolutionWithoutExtendingAccess API_AVAILABLE(macos(11.2), ios(14.2), watchos(7.2), tvos(14.2)) = ( 1 << 15 ), // Disable automatic ephemeral extension of the sandbox during resolution. Instead, call `CFURLStartAccessingSecurityScopedResource` on the returned URL when ready to use the resource. }; typedef CF_ENUM(CFIndex, CFURLBookmarkMatchResult) { @@ -805,7 +846,32 @@ extern const CFStringRef kCFURLBookmarkOriginalVolumeNameKey API_AVAILABLE(macos extern const CFStringRef kCFURLBookmarkOriginalVolumeCreationDateKey API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)); extern const CFStringRef kCFURLBookmarkFileProviderStringKey API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); extern const CFStringRef _kCFURLBookmarkURLStringKey API_AVAILABLE(macosx(10.13), ios(11.0), watchos(4.0), tvos(11.0)); -#endif +#endif // TARGET_OS_MAC || TARGET_OS_IPHONE + +#pragma mark - Revocable Bookmarks + +CF_EXPORT const CFStringRef _kCFURLRevocableBookmarkBundleIdentifierKey API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); // CFStringRef +CF_EXPORT const CFStringRef _kCFURLRevocableBookmarkAppIdentifierKey API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); // CFStringRef +CF_EXPORT const CFStringRef _kCFURLRevocableBookmarkActiveStatusKey API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); // CFBooleanRef +CF_EXPORT const CFStringRef _kCFURLRevocableBookmarkSaltKey API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); // CFDataRef + +/** Fetch a list of clients. + * + * Returns an array of dictionaries, one per client app. Keys from the namespace _kCFURLRevocableBookmarkKey. + */ +CF_EXPORT CFArrayRef _CFURLRevocableBookmarksCopyClients(void) API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); + +/** Fetch a list of bundle identifiers for active clients. */ +CF_EXPORT CFArrayRef _CFURLRevocableBookmarksCopyClientBundleIdentifiers(Boolean includeInactive) API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); + +/** Set the active state of the app with the given bundle identifier. This does not delete the security token for the app, thus is less secure than revoking the bundle identifier. */ +CF_EXPORT Boolean _CFURLRevocableBookmarksSetActiveStatusForBundleIdentifier(CFStringRef identifier, Boolean active) API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); + +/** Securely revokes all bookmarks for the bundie identifier. This is not reversable. */ +CF_EXPORT Boolean _CFURLRevocableBookmarksRevokeForBundleIdentifier(CFStringRef identifier) API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); + +/** Notification sent when the set of active clients changes. */ +CF_EXPORT const CFNotificationName _kCFURLRevocableBookmarksClientsDidChangeNotification API_AVAILABLE(macos(10.16)) API_UNAVAILABLE(ios, watchos, tvos); CF_EXTERN_C_END diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj index d5e9a35106..b61f4d6dd3 100644 --- a/Foundation.xcodeproj/project.pbxproj +++ b/Foundation.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 0383A1751D2E558A0052E5D1 /* TestStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0383A1741D2E558A0052E5D1 /* TestStream.swift */; }; 03B6F5841F15F339004F25AF /* TestURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B6F5831F15F339004F25AF /* TestURLProtocol.swift */; }; + 151023B426C3336F009371F3 /* CFPropertyList_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 151023B326C3336F009371F3 /* CFPropertyList_Internal.h */; }; 1513A8432044893F00539722 /* FileManager+XDG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1513A8422044893F00539722 /* FileManager+XDG.swift */; }; 1520469B1D8AEABE00D02E36 /* HTTPServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1520469A1D8AEABE00D02E36 /* HTTPServer.swift */; }; 1539391422A07007006DFF4F /* TestCachedURLResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1539391322A07007006DFF4F /* TestCachedURLResponse.swift */; }; @@ -72,6 +73,10 @@ 15B8043F228F38C700B30FF6 /* URLResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = EADE0B851BD15DFF00C49C64 /* URLResponse.swift */; }; 15BB952A250988C50071BD20 /* CFAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15BB9529250988C50071BD20 /* CFAccess.swift */; }; 15CA750A24F8336A007DF6C1 /* NSCFTypeShims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15CA750924F8336A007DF6C1 /* NSCFTypeShims.swift */; }; + 15E72A5426C470AE0035CAF8 /* CFListFormatter.c in Sources */ = {isa = PBXBuildFile; fileRef = 15E72A5226C470AE0035CAF8 /* CFListFormatter.c */; }; + 15E72A5526C470AE0035CAF8 /* CFListFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 15E72A5326C470AE0035CAF8 /* CFListFormatter.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 15E72A5826C472630035CAF8 /* CFRelativeDateTimeFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 15E72A5626C472620035CAF8 /* CFRelativeDateTimeFormatter.h */; }; + 15E72A5926C472630035CAF8 /* CFRelativeDateTimeFormatter.c in Sources */ = {isa = PBXBuildFile; fileRef = 15E72A5726C472630035CAF8 /* CFRelativeDateTimeFormatter.c */; }; 15F10CDC218909BF00D88114 /* TestNSCalendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F10CDB218909BF00D88114 /* TestNSCalendar.swift */; }; 15FCF4E323021C020095E52E /* TestSocketPort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15FCF4E223021C020095E52E /* TestSocketPort.swift */; }; 15FF004B229348F2004AD205 /* CFURLSessionInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B1FD9C11D6D160F0080E83C /* CFURLSessionInterface.c */; }; @@ -753,6 +758,7 @@ /* Begin PBXFileReference section */ 0383A1741D2E558A0052E5D1 /* TestStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestStream.swift; sourceTree = ""; }; 03B6F5831F15F339004F25AF /* TestURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestURLProtocol.swift; sourceTree = ""; }; + 151023B326C3336F009371F3 /* CFPropertyList_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFPropertyList_Internal.h; sourceTree = ""; }; 1513A8422044893F00539722 /* FileManager+XDG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+XDG.swift"; sourceTree = ""; }; 1520469A1D8AEABE00D02E36 /* HTTPServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPServer.swift; sourceTree = ""; }; 152EF3932283457B001E1269 /* TestNSSortDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSSortDescriptor.swift; sourceTree = ""; }; @@ -793,6 +799,10 @@ 15B8043A228F376000B30FF6 /* SwiftFoundationNetworking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftFoundationNetworking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 15BB9529250988C50071BD20 /* CFAccess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CFAccess.swift; sourceTree = ""; }; 15CA750924F8336A007DF6C1 /* NSCFTypeShims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSCFTypeShims.swift; sourceTree = ""; }; + 15E72A5226C470AE0035CAF8 /* CFListFormatter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CFListFormatter.c; sourceTree = ""; }; + 15E72A5326C470AE0035CAF8 /* CFListFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFListFormatter.h; sourceTree = ""; }; + 15E72A5626C472620035CAF8 /* CFRelativeDateTimeFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFRelativeDateTimeFormatter.h; sourceTree = ""; }; + 15E72A5726C472630035CAF8 /* CFRelativeDateTimeFormatter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CFRelativeDateTimeFormatter.c; sourceTree = ""; }; 15F10CDB218909BF00D88114 /* TestNSCalendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSCalendar.swift; sourceTree = ""; }; 15FCF4E223021C020095E52E /* TestSocketPort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSocketPort.swift; sourceTree = ""; }; 15FF00CA229348F2004AD205 /* libCFURLSessionInterface.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCFURLSessionInterface.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1579,6 +1589,8 @@ 158BCCAC2220A18F00750239 /* CFDateIntervalFormatter.c */, 158BCCAB2220A18F00750239 /* CFDateIntervalFormatter.h */, 5B6E11A81DA45EB5009B48A3 /* CFDateFormatter_Private.h */, + 15E72A5226C470AE0035CAF8 /* CFListFormatter.c */, + 15E72A5326C470AE0035CAF8 /* CFListFormatter.h */, 5B5D88AC1BBC974700234F36 /* CFLocale.c */, 5B5D88AB1BBC974700234F36 /* CFLocale.h */, 5B5D88AD1BBC974700234F36 /* CFLocaleIdentifier.c */, @@ -1588,6 +1600,8 @@ 5B5D89A91BBDC11100234F36 /* CFLocaleKeys.c */, 5BDC3F191BCC440100ED97BB /* CFICULogging.h */, 5B6E11A61DA451E7009B48A3 /* CFLocale_Private.h */, + 15E72A5726C472630035CAF8 /* CFRelativeDateTimeFormatter.c */, + 15E72A5626C472620035CAF8 /* CFRelativeDateTimeFormatter.h */, ); name = Locale; path = CoreFoundation/Locale.subproj; @@ -1669,6 +1683,7 @@ 5B5D88FB1BBC9B9500234F36 /* CFPropertyList.c */, 5B5D88FA1BBC9B9500234F36 /* CFPropertyList.h */, 5BF9B7F11FABBDB000EE1A7C /* CFPropertyList_Private.h */, + 151023B326C3336F009371F3 /* CFPropertyList_Internal.h */, 5B40F9EC1C124F45000E72E3 /* CFXMLInterface.h */, 15A619DF245A298C003C8C62 /* CFXMLInterface.c */, ); @@ -2352,12 +2367,15 @@ 5B7C8ADA1BEA80FC00C5B690 /* CFMessagePort.h in Headers */, 5B7C8ACF1BEA80FC00C5B690 /* CFDate.h in Headers */, 5B7C8AC51BEA80FC00C5B690 /* CFBitVector.h in Headers */, + 151023B426C3336F009371F3 /* CFPropertyList_Internal.h in Headers */, 5B7C8AEA1BEA81AC00C5B690 /* CFICULogging.h in Headers */, 5B7C8AF61BEA81AC00C5B690 /* CFStringEncodingConverter.h in Headers */, + 15E72A5526C470AE0035CAF8 /* CFListFormatter.h in Headers */, 5B7C8ADE1BEA80FC00C5B690 /* CFCharacterSet.h in Headers */, 5B7C8ACB1BEA80FC00C5B690 /* CFCalendar.h in Headers */, 5B7C8AF81BEA81AC00C5B690 /* CFStringEncodingConverterPriv.h in Headers */, 5B7C8AC71BEA80FC00C5B690 /* CFDictionary.h in Headers */, + 15E72A5826C472630035CAF8 /* CFRelativeDateTimeFormatter.h in Headers */, 5B7C8ADF1BEA80FC00C5B690 /* CFString.h in Headers */, 5B7C8ABD1BEA806100C5B690 /* CFBase.h in Headers */, 5B7C8AED1BEA81AC00C5B690 /* CFBundle_BinaryTypes.h in Headers */, @@ -3040,6 +3058,7 @@ 5B7C8AAF1BEA800D00C5B690 /* CFStringUtilities.c in Sources */, 1569BFAD2187D529009518FA /* CFDateComponents.c in Sources */, 5B7C8A891BEA7FDB00C5B690 /* CFNumberFormatter.c in Sources */, + 15E72A5426C470AE0035CAF8 /* CFListFormatter.c in Sources */, 5B7C8A8C1BEA7FE200C5B690 /* CFDate.c in Sources */, 5B7C8A751BEA7FCE00C5B690 /* CFRuntime.c in Sources */, 5B7C8A7C1BEA7FCE00C5B690 /* CFBasicHash.c in Sources */, @@ -3049,6 +3068,7 @@ 5B7C8A801BEA7FCE00C5B690 /* CFDictionary.c in Sources */, 5BC2C00F1C07833200CC214E /* CFStringTransform.c in Sources */, 158B66A62450F0C400892EFB /* CFBundle_SplitFileName.c in Sources */, + 15E72A5926C472630035CAF8 /* CFRelativeDateTimeFormatter.c in Sources */, 5B7C8AAE1BEA800D00C5B690 /* CFStringScanner.c in Sources */, 5B7C8A771BEA7FCE00C5B690 /* CFSystemDirectories.c in Sources */, 5B7C8AAD1BEA800D00C5B690 /* CFStringEncodings.c in Sources */,