Data Race on gIsNewDatabase in RCNConfigDBManager (FirebaseRemoteConfig)
Description
Hello Firebase team,
I'm encountering a data race warning when using FirebaseRemoteConfig. The issue is related to a global variable gIsNewDatabase being accessed from multiple threads without synchronization.
Details: SDK: firebase-ios-sdk Module: FirebaseRemoteConfig File: RCNConfigDBManager.m Line: 1222 (and method - (BOOL)isNewDatabase) Detected using: Thread Sanitizer (Xcode)
Reproducing the issue
No response
Firebase SDK Version
11.11.0
Xcode Version
16.1
Installation Method
Swift Package Manager
Firebase Product(s)
Analytics, Remote Config, Crashlytics, AB Testing
Targeted Platforms
iOS
Relevant Log Output
'gIsNewDatabase' is a global variable (0x1049a7f48)
/Users/admin/Library/Developer/Xcode/DerivedData/myProject-.../FirebaseRemoteConfig/Sources/RCNConfigDBManager.m:1222
Data race in -[RCNConfigDBManager isNewDatabase] at gIsNewDatabase
If using Swift Package Manager, the project's Package.resolved
Expand Package.resolved snippet
Replace this line with the contents of your Package.resolved.
If using CocoaPods, the project's Podfile.lock
Expand Podfile.lock snippet
Replace this line with the contents of your Podfile.lock!
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
Thanks for the report, @dooublemint. Could you provide the reproducing steps on how you encounter the issue?
I've confirmed this is a real issue, at least theoretically, while doing the Swift migration. I'll leave open. Please thumbs up if it impacts you and ideally provide a repro case.
Some Trace that might help:
==================
WARNING: ThreadSanitizer: data race (pid=30889)
Write of size 1 at 0x00010790a338 by thread T3:
#0 RemoteConfigCreateFilePathIfNotExist <null> (App:arm64+0x102175a1c)
#1 __42-[RCNConfigDBManager createOrOpenDatabase]_block_invoke <null> (App:arm64+0x1021754e4)
#2 __tsan::invoke_and_release_block(void*) <null> (libclang_rt.tsan_iossim_dynamic.dylib:arm64+0x7f8d0)
#3 _dispatch_client_callout <null> (libdispatch.dylib:arm64+0x1d794)
Previous read of size 1 at 0x00010790a338 by main thread (mutexes: write M0, write M1):
#0 -[RCNConfigDBManager isNewDatabase] <null> (App:arm64+0x102181c44)
#1 -[RCNConfigSettings initWithDatabaseManager:namespace:firebaseAppName:googleAppID:] <null> (App:arm64+0x102194f30)
#2 -[FIRRemoteConfig initWithAppName:FIROptions:namespace:DBManager:configContent:analytics:] <null> (App:arm64+0x102161acc)
#3 -[FIRRemoteConfigComponent remoteConfigForNamespace:] <null> (App:arm64+0x10216d680)
#4 -[FIRRemoteConfigComponent registerRolloutsStateSubscriber:for:] <null> (App:arm64+0x10216dfec)
#5 -[FIRCrashlytics initWithApp:appInfo:installations:analytics:sessions:remoteConfig:] <null> (App:arm64+0x1018b5ea4)
#6 __38+[FIRCrashlytics componentsToRegister]_block_invoke <null> (App:arm64+0x1018b69f0)
#7 -[FIRComponentContainer instantiateInstanceForProtocol:withBlock:] <null> (App:arm64+0x101943878)
#8 -[FIRComponentContainer instanceForProtocol:] <null> (App:arm64+0x101943d7c)
#9 -[FIRComponentContainer instantiateEagerComponents] <null> (App:arm64+0x101943624)
#10 +[FIRApp configureWithName:options:] <null> (App:arm64+0x10193b77c)
#11 +[FIRApp configureWithOptions:] <null> (App:arm64+0x10193adc4)
#12 (1) suspend resume partial function for App.FirebaseRepository.(initialize in _3E95AD6ADDB109205B372892D4B17379)() async -> () <null> (App:arm64+0x1012f722c)
#13 swift::runJobInEstablishedExecutorContext(swift::Job*) <null> (libswift_Concurrency.dylib:arm64+0x3e984)
#14 static App.AppDelegate.$main() -> () <null> (App:arm64+0x1001ed9b4)
#15 main <null> (App:arm64+0x1001f0550)
Location is global 'gIsNewDatabase' at 0x00010790a338 (App+0x10376a338)
Mutex M0 (0x00010781b848) created at:
#0 objc_sync_enter <null> (libclang_rt.tsan_iossim_dynamic.dylib:arm64+0x7d2d8)
#1 +[FIRApp configureWithName:options:] <null> (App:arm64+0x10193b638)
#2 +[FIRApp configureWithOptions:] <null> (App:arm64+0x10193adc4)
#3 (1) suspend resume partial function for App.FirebaseRepository.(initialize in _3E95AD6ADDB109205B372892D4B17379)() async -> () <null> (App:arm64+0x1012f722c)
#4 swift::runJobInEstablishedExecutorContext(swift::Job*) <null> (libswift_Concurrency.dylib:arm64+0x3e984)
#5 static App.AppDelegate.$main() -> () <null> (App:arm64+0x1001ed9b4)
#6 main <null> (App:arm64+0x1001f0550)
Mutex M1 (0x00011f966a50) created at:
#0 objc_sync_enter <null> (libclang_rt.tsan_iossim_dynamic.dylib:arm64+0x7d2d8)
#1 -[FIRComponentContainer instanceForProtocol:] <null> (App:arm64+0x101943c1c)
#2 +[FIRComponentType instanceForProtocol:inContainer:] <null> (App:arm64+0x101944cf0)
#3 +[FIRInstallations installationsWithApp:] <null> (App:arm64+0x10196c628)
#4 +[FIRInstallations installations] <null> (App:arm64+0x10196c4fc)
#5 +[FIRAnalytics updateFirebaseInstallationID] <null> (App:arm64+0x1029390cc)
#6 +[FIRApp addAppToAppDictionary:] <null> (App:arm64+0x10193c904)
#7 +[FIRApp configureWithName:options:] <null> (App:arm64+0x10193b740)
#8 +[FIRApp configureWithOptions:] <null> (App:arm64+0x10193adc4)
#9 (1) suspend resume partial function for App.FirebaseRepository.(initialize in _3E95AD6ADDB109205B372892D4B17379)() async -> () <null> (App:arm64+0x1012f722c)
#10 swift::runJobInEstablishedExecutorContext(swift::Job*) <null> (libswift_Concurrency.dylib:arm64+0x3e984)
#11 static App.AppDelegate.$main() -> () <null> (App:arm64+0x1001ed9b4)
#12 main <null> (App:arm64+0x1001f0550)
Thread T3 (tid=5032809, running) is a GCD worker thread
Hi all, apologies for the confusion, this bug was fixed in Firebase 12.6.0 via #15442. Please update to 12.6.0 or newer to get the fix. Thanks for the report!