firebase-ios-sdk
firebase-ios-sdk copied to clipboard
[Firestore] Fix crash fetching Auth and App Check tokens
Fix #15281
Gemini's analysis:
After analyzing firebase_auth_credentials_provider_apple.mm, I've identified the source of the crash.
The Problem
Here's the relevant section of the GetToken method in the Firestore SDK:
1 // ...
2 std::weak_ptr<Contents> weak_contents = contents_;
3 void (^get_token_callback)(NSString*, NSError*) =
4 ^(NSString* _Nullable token, NSError* _Nullable error) {
5 std::shared_ptr<Contents> contents = weak_contents.lock();
6 if (!contents) {
7 return;
8 }
9 // ... (rest of the logic)
10 };
11
12 if (contents_->auth) {
13 [contents_->auth getTokenForcingRefresh:force_refresh_
14 withCallback:get_token_callback];
15 }
16 // ...
The C++ object FirebaseAuthCredentialsProvider holds a shared pointer (std::shared_ptr) to its internal state called contents_.
- A std::weak_ptr (weak_contents) is created from contents_.
- An Objective-C block (get_token_callback) is created to serve as the completion handler.
- Crucially, this block captures the
std::weak_ptr. When the async getTokenForcingRefresh method is called, the block is passed along. - If the C++ FirebaseAuthCredentialsProvider object is deallocated before the token fetch completes, its contents_ member is destroyed along with it.
- When the get_token_callback block eventually runs, weak_contents.lock() correctly fails to acquire a shared_ptr because the object is gone. The if (!contents) { return; } check handles this correctly.
However, the crash isn't happening there. It's happening because of what the block doesn't capture.
The block implicitly captures the C++ completion object by value. This completion is a std::function, which itself holds a reference to the necessary context to continue the Firestore operation. When the FirebaseAuthCredentialsProvider is destroyed, the context that this completion function needs is also destroyed.
The race condition is:
- Firestore requests a token from Auth via FirebaseAuthCredentialsProvider.
- The FirebaseAuthCredentialsProvider object is deallocated for some reason (e.g., the Firestore instance is shut down).
- The async getToken operation completes on a background thread.
- The get_token_callback block is invoked.
- Inside the block, it tries to call the completion function, but the underlying object that completion needs to operate on has been destroyed.
- Crash. The objc_retain in your stack trace is likely from the Objective-C runtime trying to manage the memory of one of these captured objects as the block is executed, but the object is already a zombie.
The Solution
This is a bug within the Firestore SDK. The fix is to ensure that the completion object (and any other necessary state) is handled safely within the callback block, likely by capturing self or a shared_ptr to self and checking for its existence before proceeding.
I will now patch the file to fix this issue. I'll modify the block to capture a shared_ptr to the FirebaseAuthCredentialsProvider itself, ensuring it stays alive until the callback completes.