Skip to content

Fix use-after-free race in MSIDFlightManager - #1900

Open
Dharshan BJ (DharshanBJ) wants to merge 7 commits into
AzureAD:devfrom
DharshanBJ:dharshanb/fixCrash
Open

Fix use-after-free race in MSIDFlightManager#1900
Dharshan BJ (DharshanBJ) wants to merge 7 commits into
AzureAD:devfrom
DharshanBJ:dharshanb/fixCrash

Conversation

@DharshanBJ

@DharshanBJ Dharshan BJ (DharshanBJ) commented Jul 14, 2026

Copy link
Copy Markdown

PR Checklist (must be completed before review)

  • All tests pass locally
  • PR size is <= 500 LOC per PR Size Check policy
  • PR is independently mergeable (no hidden dependencies)
  • Appropriate reviewers are assigned
  • PR reviewed by code owner (required if Copilot-generated)
  • SME or Senior IC assigned where required

Proposed changes

Fixes a race condition in MSIDFlightManager that could cause a use-after-free crash (SIGSEGV) when flights are read concurrently with a provider swap.

Root cause: A data race / use-after-free on  _flightProvider . Readers accessed it through the unsynchronized nonatomic getter while  setFlightProvider:  used  dispatch_barrier_async , releasing the previous provider asynchronously. A concurrent reader could retain a pointer mid-release →  objc_retain  on freed memory → crash.

The fix ( IdentityCore/src/MSIDFlightManager.m ):

•  setFlightProvider:  →  dispatch_barrier_sync , so the assignment and release of the old provider complete before returning and are exclusive against reads.
•  boolForKey:  /  stringForKey:  (and the synchronized public  flightProvider  getter) read the ivar inside  dispatch_sync  into a strong local, then invoke the provider outside the block — keeping it alive during use and avoiding a re-entrancy deadlock.

Result: All reads/writes of  _flightProvider  are serialized on the queue with a strong reference held during use, eliminating the use-after-free.

Scope: IdentityCore/src/MSIDFlightManager.m (three methods).

Note: this PR also contains an earlier commit adding a stray // TEST comment in MSIDBrokerConstants.m. That appears unintentional and can be dropped if not needed.

Type of change

  • Feature work
  • Bug fix
  • Documentation
  • Engineering change
  • Test
  • Logging/Telemetry

Risk

  • High – Errors could cause MAJOR regression of many scenarios. (Example: new large features or high level infrastructure changes)
  • Medium – Errors could cause regression of 1 or more scenarios. (Example: somewhat complex bug fixes, small new features)
  • Small – No issues are expected. (Example: Very small bug fixes, string changes, or configuration settings changes)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request makes a minimal change in IdentityCore by adding an extra comment line to MSIDBrokerConstants.m.

Changes:

  • Added a // TEST comment line in the file header.

Comment on lines 22 to 25
// THE SOFTWARE.
// TEST

#import "MSIDBrokerConstants.h"
Comment thread IdentityCore/src/MSIDBrokerConstants.m Outdated
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// TEST
Serialize all access to _flightProvider on the synchronization queue and
hold a strong local reference while messaging the provider, so a concurrent
setFlightProvider: cannot deallocate it between the nil-check and the message
send (use-after-free -> SIGSEGV). Change the setter to dispatch_barrier_sync
so the assignment/release completes before it returns.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3dcf24a-1b1d-4835-b745-826edac60905
@DharshanBJ Dharshan BJ (DharshanBJ) changed the title update [patch] [bugfix]: Fix use-after-free race in MSIDFlightManager Jul 14, 2026
@DharshanBJ Dharshan BJ (DharshanBJ) changed the title [patch] [bugfix]: Fix use-after-free race in MSIDFlightManager Fix use-after-free race in MSIDFlightManager Jul 14, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c3dcf24a-1b1d-4835-b745-826edac60905
// installed provider) completes before the setter returns. An async barrier let the
// previous provider be released on the queue after the setter returned, racing with
// concurrent readers and allowing a stale/nil provider to be observed.
dispatch_barrier_sync(self.synchronizationQueue, ^{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can deadlock if a provider calls setFlightProvider: from boolForKey: or stringForKey:, since those callbacks run on the same queue and this uses dispatch_barrier_sync. Could we capture the provider while on the queue, then invoke it after leaving the queue?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

updated,  boolForKey: / stringForKey:  now capture the provider under the queue and invoke it after leaving, so a re-entrant  setFlightProvider:  no longer deadlocks.

Dharshan BJ (DharshanBJ) and others added 4 commits July 14, 2026 16:34
… deadlock

Capture a strong reference to the provider on the queue, then invoke boolForKey:/
stringForKey: after leaving the queue. This preserves the use-after-free fix while
preventing a deadlock if a provider re-enters setFlightProvider: from its own callback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe48d6bf-0351-4eb6-b0ed-93de3427ee1c
});

return result;
return provider ? [provider boolForKey:flightKey] : NO;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The use-after-free path is already covered by testConcurrentReads_whileProviderSwappedAndCleared_doNotCrash, but the re-entrancy case this change fixes isn't covered anywhere. Can we add a regression test with a provider whose boolForKey: calls setFlightProvider: back on the same manager? It's deterministic now and would have hung before this change.

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