firebase-ios-sdk icon indicating copy to clipboard operation
firebase-ios-sdk copied to clipboard

[Firestore] Fix crash fetching Auth and App Check tokens

Open paulb777 opened this issue 1 week ago • 4 comments

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_.

  1. A std::weak_ptr (weak_contents) is created from contents_.
  2. An Objective-C block (get_token_callback) is created to serve as the completion handler.
  3. Crucially, this block captures the std::weak_ptr. When the async getTokenForcingRefresh method is called, the block is passed along.
  4. If the C++ FirebaseAuthCredentialsProvider object is deallocated before the token fetch completes, its contents_ member is destroyed along with it.
  5. 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:

  1. Firestore requests a token from Auth via FirebaseAuthCredentialsProvider.
  2. The FirebaseAuthCredentialsProvider object is deallocated for some reason (e.g., the Firestore instance is shut down).
  3. The async getToken operation completes on a background thread.
  4. The get_token_callback block is invoked.
  5. Inside the block, it tries to call the completion function, but the underlying object that completion needs to operate on has been destroyed.
  6. 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.

paulb777 avatar Nov 29 '25 16:11 paulb777