From d70841574c4997079c9a80079592ed92027af494 Mon Sep 17 00:00:00 2001 From: Charles Hu Date: Tue, 23 Sep 2025 13:37:44 -0700 Subject: [PATCH] Initialize _allBundles in _CFBundleCopyAllBundles before trying to copy it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The array `_allBundles` is lazily created when the first item is inserted into it in the `_CFBundleAddToTablesLocked` function. There’s a possibility that by the time `_CFBundleCopyAllBundles` is called, `_allBundles` hasn’t been initialized yet. In such cases, lazily initialize `_allBundles`. resolves: rdar://150846072 --- Sources/CoreFoundation/CFBundle.c | 31 +++++++++++++++++++----------- Sources/CoreFoundation/CFRuntime.c | 3 +++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Sources/CoreFoundation/CFBundle.c b/Sources/CoreFoundation/CFBundle.c index cbd5b1ddfb..1ebd7d23af 100644 --- a/Sources/CoreFoundation/CFBundle.c +++ b/Sources/CoreFoundation/CFBundle.c @@ -255,22 +255,26 @@ CF_PRIVATE CFURLRef _CFURLCreateResolvedDirectoryWithString(CFAllocatorRef alloc #pragma mark - #pragma mark Bundle Tables +static void __CFBundleCreateAllBundlesArrayLocked() { + CFArrayCallBacks callbacks = kCFTypeArrayCallBacks; +#if TARGET_OS_OSX + if (_useUnsafeUnretainedTables()) { + callbacks.retain = NULL; + callbacks.release = NULL; + } +#endif + // The _allBundles array holds a strong reference on the bundle. + // It does this to prevent a race on bundle deallocation / creation. See: CFBundle isn't thread-safe in RR mode + // Also, the existence of the CFBundleGetBundleWithIdentifier / CFBundleGetAllBundles API means that any bundle we hand out from there must be permanently retained, or callers will potentially have an object that can be deallocated out from underneath them. + _allBundles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &callbacks); +} + static void _CFBundleAddToTablesLocked(CFBundleRef bundle, CFStringRef bundleID) { if (bundle->_isUnique) return; // Add to the _allBundles list if (!_allBundles) { - CFArrayCallBacks callbacks = kCFTypeArrayCallBacks; -#if TARGET_OS_OSX - if (_useUnsafeUnretainedTables()) { - callbacks.retain = NULL; - callbacks.release = NULL; - } -#endif - // The _allBundles array holds a strong reference on the bundle. - // It does this to prevent a race on bundle deallocation / creation. See: CFBundle isn't thread-safe in RR mode - // Also, the existence of the CFBundleGetBundleWithIdentifier / CFBundleGetAllBundles API means that any bundle we hand out from there must be permanently retained, or callers will potentially have an object that can be deallocated out from underneath them. - _allBundles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &callbacks); + __CFBundleCreateAllBundlesArrayLocked(); } CFArrayAppendValue(_allBundles, bundle); @@ -1795,6 +1799,11 @@ CF_EXPORT CFArrayRef _CFBundleCopyAllBundles(void) { _CFBundleEnsureAllBundlesUpToDate(); CFBundleRef main = CFBundleGetMainBundle(); _CFMutexLock(&CFBundleGlobalDataLock); + // There is a chance that `_allBundles` haven't been initiated + // if no one called `CFBundleCreate()` yet + if (!_allBundles) { + __CFBundleCreateAllBundlesArrayLocked(); + } // _allBundles does not include the main bundle, so insert it here. CFMutableArrayRef bundles = CFArrayCreateMutableCopy(kCFAllocatorSystemDefault, CFArrayGetCount(_allBundles) + 1, _allBundles); _CFMutexUnlock(&CFBundleGlobalDataLock); diff --git a/Sources/CoreFoundation/CFRuntime.c b/Sources/CoreFoundation/CFRuntime.c index 3336852904..e7e2b94bc5 100644 --- a/Sources/CoreFoundation/CFRuntime.c +++ b/Sources/CoreFoundation/CFRuntime.c @@ -1810,6 +1810,9 @@ CF_CROSS_PLATFORM_EXPORT void _CFDeinit(CFTypeRef cf) { } bool _CFIsSwift(CFTypeID type, CFSwiftRef obj) { + if (obj == NULL) { + return false; + } if (type == _kCFRuntimeNotATypeID) { return false; }