Skip to content

[FSSDK-12729] fix: prevent EXC_BAD_ACCESS from premature URLSession deallocation#649

Open
muzahidul-opti wants to merge 3 commits into
masterfrom
fix/urlsession-deallocation
Open

[FSSDK-12729] fix: prevent EXC_BAD_ACCESS from premature URLSession deallocation#649
muzahidul-opti wants to merge 3 commits into
masterfrom
fix/urlsession-deallocation

Conversation

@muzahidul-opti

@muzahidul-opti muzahidul-opti commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Move session.finishTasksAndInvalidate() from defer block (synchronous scope exit) into async completion callbacks across all four network modules
  • Prevents EXC_BAD_ACCESS crash caused by URLSession being invalidated before the async callback fires
  • Particularly impactful when Firebase Performance Monitoring swizzles URLSession delegate methods, widening the race window

Root Cause

Swift defer executes when the enclosing scope exits — which for these methods is before the URLSession async callback fires. The session is invalidated while the network request is still in-flight, causing use-after-free crashes.

Changes

File Fix
DefaultDatafileHandler.swift Moved invalidation to end of downloadTask callback + guard-failure path
DefaultEventDispatcher.swift Moved invalidation to end of uploadTask callback
OdpEventApiManager.swift Moved invalidation into inner defer block after completionHandler
OdpSegmentApiManager.swift Moved invalidation into inner defer block after completionHandler

Test plan

  • swift build passes
  • Code review: session invalidated exactly once on every code path (success + error) in all 4 modules
  • No defer { session.finishTasksAndInvalidate() } remains in any affected file
  • Existing Xcode test suite passes unchanged

🤖 Generated with Claude Code

Issue

The defer block fires at synchronous scope exit — before the async
URLSession callback runs — deallocating the session while the request
is still in-flight. This causes EXC_BAD_ACCESS, especially when
Firebase Performance Monitoring swizzles URLSession delegate methods.

Move session.finishTasksAndInvalidate() from the outer defer into the
completion callback body (or inner defer for ODP modules) so the
session stays alive until the response is fully processed.

Affected modules:
- DefaultDatafileHandler.downloadDatafile()
- DefaultEventDispatcher.sendEvent()
- OdpEventApiManager.sendOdpEvents()
- OdpSegmentApiManager.fetchSegments()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@muzahidul-opti muzahidul-opti changed the title fix: prevent EXC_BAD_ACCESS from premature URLSession deallocation [FSSDK-12729] fix: prevent EXC_BAD_ACCESS from premature URLSession deallocation Jul 1, 2026
MockUrlSession used lock.async for both incrementing (init) and
decrementing (finishTasksAndInvalidate) validSessions. With the
URLSession fix moving invalidation into the async callback, the
decrement could still be pending when tearDown asserts the counter
is zero.

Change lock.async to lock.sync so counter updates complete before
the caller proceeds, eliminating the race between finishTasksAndInvalidate
and the test assertion in tearDown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a crash risk (EXC_BAD_ACCESS) caused by invalidating ephemeral URLSession instances too early (via defer on the calling scope), by moving finishTasksAndInvalidate() to run only after each async network callback completes.

Changes:

  • Move session.finishTasksAndInvalidate() from synchronous defer blocks into URLSession completion callbacks in Customization + ODP network code.
  • Ensure the datafile handler also invalidates the session on the guard-failure path.
  • Update the test MockUrlSession to update its validSessions counter synchronously.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Tests/TestUtils/MockUrlSession.swift Makes validSessions counter updates synchronous for tests.
Sources/Customization/DefaultDatafileHandler.swift Invalidates session after download completion and on request-creation failure.
Sources/Customization/DefaultEventDispatcher.swift Invalidates session at the end of the upload task callback.
Sources/ODP/OdpEventApiManager.swift Invalidates session in the data task callback defer after completion is invoked.
Sources/ODP/OdpSegmentApiManager.swift Invalidates session in the data task callback defer after completion is invoked.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/Customization/DefaultDatafileHandler.swift
Comment thread Tests/TestUtils/MockUrlSession.swift Outdated
@coveralls

coveralls commented Jul 1, 2026

Copy link
Copy Markdown

Coverage Status

Coverage is 93.859%fix/urlsession-deallocation into master. No base build found for master.

- DefaultDatafileHandler: call completionHandler with failure result
  when getRequest returns nil, preventing caller deadlock on
  DispatchGroup.wait()
- MockUrlSession: make lock static so all instances share a single
  serial queue for the static validSessions counter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants