firebase / firebase/firebase-ios-sdk
[FirebasePerformance 12.12.0] Crash in +[FPRNetworkTrace networkTraceFromObject:] from download-task progress instrumentation (regression of #14734?)
- Dominant language
- C++
- Stars
- 6.7k
- Forks
- 1.8k
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 75
Description
### Description
We're seeing an intermittent `EXC_BAD_ACCESS (KERN_INVALID_ADDRESS)` crash in `FirebasePerformance` when the SDK reads the associated `FPRNetworkTrace` off an `NSURLSessionDownloadTask` from inside the swizzled `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:` block.
**Crash blame frame** (per Crashlytics): `+[FPRNetworkTrace networkTraceFromObject:]` at `FPRNetworkTrace.m:448` (FirebasePerformance **12.12.0**, CocoaPods tag `CocoaPods-12.12.0`).
This appears to be a regression / sibling of #14734 — that issue was closed Feb 2026 after a fix landed, but the fix does not seem to cover the **download-task progress** instrumentation path. The two stack traces are very similar in shape (same `+networkTraceFromObject:` callsite, same `objc_retain` failure) but reached through `__InstrumentURLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesExpectedToWrite_block_invoke` rather than the data-task completion path discussed in #14734. This pattern was also reported earlier in #9208 and #10807.
### What we believe is happening
`FPRNetworkTrace` is attached to the URLSessionTask via `objc_setAssociatedObject` (RETAIN_NONATOMIC). Inside the swizzled block at `FPRNSURLSessionDelegateInstrument.m:172`:
```objc
FPRNetworkTrace *trace = [FPRNetworkTrace networkTraceFromObject:downloadTask];
```
which calls into `FPRNetworkTrace.m:445` (head of `+networkTraceFromObject:`):
```objc
+ (FPRNetworkTrace *)networkTraceFromObject:(id)object {
FPRNetworkTrace *networkTrace = nil;
if (object != nil) {
id traceObject = [FPRObjectSwizzler // crashlytics blames line 448
getAssociatedObject:object
key:(__bridge const void *_Nonnull)kFPRNetworkTracePropertyName];
...
```
The crashing instruction is `objc_retain` on the value returned by `objc_getAssociatedObject`. This is consistent with a use-after-free of the stored `FPRNetworkTrace` — i.e. the associated value pointer is now dangling. Most likely scenarios:
1. The block at `FPRNSURLSessionDelegateInstrument.m:168-186` runs on an NSOperationQueue worker thread (visible in stack: `__NSOPERATION_IS_INVOKING_MAIN__` → `_dispatch_block_async_invoke2`). Between the `object != nil` check and the `objc_getAssociatedObject` return-value retain, another thread (or the task's own dealloc on a different queue) clears the association via `[FPRNetworkTrace removeNetworkTraceFromObject:]` (called on lines 179 of the same file, and from `didCompleteWithError`), which under RETAIN_NONATOMIC releases the old value. The current thread then retains a pointer that has just been freed.
2. Multiple `didWriteData:` callbacks for the same task can be dispatched concurrently on NSOperationQueue worker threads. One of them hits the `totalBytesWritten >= totalBytesExpectedToWrite` branch (lines 175–180), calls `removeNetworkTraceFromObject:`, while another is mid-`networkTraceFromObject:` read.
Either way, the access to the associated object is **not synchronized**, but `RETAIN_NONATOMIC` storage gives no inherent thread safety for the get/release pair.
The same code remains unchanged in `CocoaPods-12.12.1` — we verified it byte-identical against `CocoaPods-12.12.0` in the relevant span. So a bump to 12.12.1 is not a workaround.
A possible mitigation (without changing the public API) is to take a strong local reference *before* the call to `removeNetworkTraceFromObject:`, and/or to switch the association strategy so the FPRNetworkTrace owns its own removal under a lock that serializes against the read path.
### Reproducing the issue
We do not have a 100% reproducer. The single sample event in the last 7 days came from a real user, in foreground, on iOS 26.3.1, while an `NSURLSessionDownloadTask` was being instrumented. We see this signature historically appearing in our Crashlytics across iOS 17/18/26 (older signatures were closed-then-regressed; see issue notes).
### Firebase SDK Version
FirebasePerformance **12.12.0** (CocoaPods-12.12.0). Also confirmed the same crashing code path in CocoaPods-12.12.1.
### Xcode Version
26.4 (17E192)
### Installation Method
CocoaPods
### Firebase Product(s)
Performance
### Targeted Platforms
iOS
### Relevant Log Output
```
Crashed Thread (com.apple.NSURLSession-delegate / NSOperationQueue worker):
at objc_retain_x0
at objc_retain
at +[FPRNetworkTrace networkTraceFromObject:] (FPRNetworkTrace.m:448)
at __InstrumentURLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesExpectedToWrite_block_invoke
(FPRNSURLSessionDelegateInstrument.m:172)
at __123-[__NSCFURLSessionDelegateWrapper downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:completionHandler:]_block_invoke
at
at -[NSBlockOperation main]
at __NSOPERATION_IS_INVOKING_MAIN__
at -[NSOperation start]
at __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__
at __NSOQSchedule_f
at _dispatch_block_async_invoke2
at _dispatch_client_callout
at _dispatch_queue_override_invoke.cold.3
at _dispatch_queue_override_invoke
at _dispatch_root_queue_drain
at _dispatch_worker_thread2
at _pthread_wqthread
at start_wqthread
Exception: EXC_BAD_ACCESS (KERN_INVALID_ADDRESS)
Device: iPhone 14 Pro (iPhone15,2)
OS: iOS 26.3.1
Process state: FOREGROUND
Crashlytics SDK: 12.12.0
```
### If using CocoaPods, the project's Podfile.lock
Expand Podfile.lock (Firebase-relevant excerpt)
```yml
- FirebaseAnalytics (12.12.1):
- FirebaseCore (~> 12.12.0)
- FirebaseInstallations (~> 12.12.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseCore (12.12.1):
- FirebaseCoreInternal (~> 12.12.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreExtension (12.12.0):
- FirebaseCore (~> 12.12.0)
- FirebaseCoreInternal (12.12.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseCrashlytics (12.12.1):
- FirebaseCore (~> 12.12.0)
- FirebaseInstallations (~> 12.12.0)
- GoogleUtilities/Environment (~> 8.1)
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- FirebaseInstallations (12.12.0):
- FirebaseCore (~> 12.12.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4)
- FirebasePerformance (12.12.0):
- FirebaseCore (~> 12.12.0)
- FirebaseCoreExtension (~> 12.12.0)
- FirebaseInstallations (~> 12.12.0)
- FirebaseRemoteConfig (~> 12.12.0)
- FirebaseRemoteConfigInterop (~> 12.12.0)
- FirebaseSessions (~> 12.12.0)
- GoogleDataTransport (~> 10.1)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/ISASwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0)
```
### Related issues
- #14734 — closed Feb 2026 with a fix attempt; same `+networkTraceFromObject:` crash but the reporter stacks were the data-task completion path. The fix may not cover the download-task progress path shown here.
- #9208, #10807 — earlier instances of the same `+networkTraceFromObject:` crash on older SDKs.
Contributor guide
Assessment
This issue has not been assessed yet.