fix: resolve EXC_BAD_ACCESS in SentryTracer/SentryNetworkTracker span lifecycle#8058
fix: resolve EXC_BAD_ACCESS in SentryTracer/SentryNetworkTracker span lifecycle#8058itaybre wants to merge 2 commits into
Conversation
… lifecycle Fix two crash paths in the SentryTracer → SentryNetworkTracker call chain that cause use-after-free crashes (SDK-CRASHES-COCOA-5, ~140K events/90d): 1. TOCTOU race in canBeFinished: hasUnfinishedChildSpansToWaitFor was checked outside @synchronized(self), allowing concurrent threads to both see "no unfinished children" and call finishInternal. Moved the check inside the synchronized block. 2. Volatile currentRequest re-reads: SentryTracePropagation and SentryNetworkTracker accessed sessionTask.currentRequest multiple times without retaining. The property can return a freed object if the task completes on another thread between reads. Snapshot the request once into a local variable in both methods. 3. Weak span dangling refs in TTD callback: SentryTimeToDisplayTracker's finishCallback accessed weak initialDisplaySpan/fullDisplaySpan properties repeatedly. Strongify into locals at callback entry with nil guards. Closes #8012
Instructions and example for changelogPlease add an entry to Example: ## Unreleased
### Fixes
- resolve EXC_BAD_ACCESS in SentryTracer/SentryNetworkTracker span lifecycle ([#8058](https://github.com/getsentry/sentry-cocoa/pull/8058))If none of the above apply, you can opt out of this check by adding |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8058 +/- ##
=============================================
- Coverage 87.306% 87.203% -0.104%
=============================================
Files 554 559 +5
Lines 31986 32172 +186
Branches 13139 13146 +7
=============================================
+ Hits 27926 28055 +129
- Misses 4012 4069 +57
Partials 48 48
... and 21 files with indirect coverage changes Continue to review full report in Codecov by Harness.
|
Cover the three crash paths fixed in the previous commit: - SentryTracerTests: concurrent child span finish racing with tracer.finish() to verify canBeFinished atomicity - SentryNetworkTrackerTests: concurrent resume + setState on the same task, and resume after task already completed - SentryTimeToDisplayTrackerTest: concurrent tracer finish with child span operations, and finish with no full display span - SentryTracePropagationTests: addBaggageHeader with nil currentRequest (task with no request set)
🚨 Detected changes in high risk code 🚨High-risk code can easily blow up and is hard to test. We had severe bugs in the past. Be extra careful when changing these files, and have an extra careful look at these:
|
Description
Fix two crash paths in the
SentryTracer→SentryNetworkTrackercall chain that causeEXC_BAD_ACCESScrashes. This is the most frequent crash pattern in SDK-CRASHES-COCOA-5 (43/100 sampled events), affecting 7 customer projects across SDK versions 8.36.0–9.9.0 with ~140K events in the last 90 days.Root causes and fixes
1. TOCTOU race in
SentryTracer.canBeFinished—hasUnfinishedChildSpansToWaitForwas evaluated outside@synchronized(self), so two concurrent threads could both observe "no unfinished children" and race intofinishInternal, operating on deallocated state. Moved the check inside the synchronized block.2. Volatile
currentRequestre-reads —SentryTracePropagation.addBaggageHeader:andSentryNetworkTracker.urlSessionTaskResume:accessedsessionTask.currentRequestmultiple times without retaining. The property can return a freed object if the task completes on another thread between reads (CFURLRequestSetHTTPRequestBodycrash). Both methods now snapshot the request once into a local variable.3. Weak span dangling refs in TTD callback —
SentryTimeToDisplayTracker'sfinishCallbackaccessed weakinitialDisplaySpan/fullDisplaySpanproperties repeatedly without strongifying. The spans could be zeroed mid-callback if the tracer's children are released concurrently. Now strongified into locals at callback entry with nil guards.How tested
make format— cleanmake analyze— cleanmake build-ios— succeedsmake test-ios ONLY_TESTING=SentryTests/SentryTracerTests— 86 tests passmake test-ios ONLY_TESTING=SentryTests/SentryTracePropagationTests,SentryTests/SentryNetworkTrackerTests,SentryTests/SentryTimeToDisplayTrackerTest— 100 tests passCloses #8012