Skip to content

Repository files navigation

RNCWebViewDecisionManager Race Condition Repro

Deterministic JS-only repro for a thread-safety bug in react-native-webview.

This repo is designed for an irrefutable race report using Thread Sanitizer (TSan). Hard EXC_BAD_ACCESS is possible in production but non-deterministic locally.

Stack

  • React Native: 0.81.5
  • react-native-webview: 13.15.0
  • iOS: Legacy Architecture (RCT_NEW_ARCH_ENABLED=0)

Why this repro is minimal

  • No native source modifications are required to reproduce the race.
  • The app repeatedly remounts hidden WebViews from JS while running.
  • No network requests, no custom native logging, and no release-only setup.
  • Deterministic evidence is TSan output, not a lucky crash.

Setup

npm install
cd ios && pod install && cd ..

Run deterministic race repro (TSan)

  1. Open ios/RNCWebViewRepro.xcworkspace in Xcode.
  2. Select an iOS Simulator target.
  3. Edit Scheme -> Run:
    • Build Configuration: Debug
    • Diagnostics: enable Thread Sanitizer
  4. Run app.
  5. Tap Start Repro.
  6. Capture the first TSan report that includes both:
    • -[RNCWebViewDecisionManager setDecisionHandler:]
    • -[RNCWebViewDecisionManager setResult:forLockIdentifier:]
  7. Ignore reports that do not include RNCWebViewDecisionManager (startup noise).
  8. Tap Stop Repro after capturing the report.

Optional startup-noise suppression

React Native startup can emit unrelated TSan warnings before tapping Start. To reduce that noise, set this Run env var in Scheme:

  • TSAN_OPTIONS=halt_on_error=0 suppressions=/Users/angelazcarraga/Workspace/RNCWebViewRepro/ios/tsan-suppressions.txt

Expected TSan signal

The relevant report shows unsynchronized access to the same NSMutableDictionary from two threads:

  • Main thread:
    • RNCWebViewImpl webView:decidePolicyForNavigationAction:decisionHandler:
    • RNCWebViewDecisionManager setDecisionHandler:
  • com.facebook.react.RNCWebViewModuleQueue:
    • RNCWebViewModule shouldStartLoadWithLockIdentifier:lockIdentifier:
    • RNCWebViewDecisionManager setResult:forLockIdentifier:

Optional crash repro

A local EXC_BAD_ACCESS crash can occur but is timing-dependent and not required for this repro package.

Proposed fix

Synchronize access to decisionHandlers in apple/RNCWebViewDecisionManager.m:

- (int)setDecisionHandler:(DecisionBlock)decisionHandler {
    @synchronized(self) {
        int lockIdentifier = self.nextLockIdentifier++;
        [self.decisionHandlers setObject:decisionHandler forKey:@(lockIdentifier)];
        return lockIdentifier;
    }
}

- (void)setResult:(BOOL)shouldStart forLockIdentifier:(int)lockIdentifier {
    DecisionBlock handler = nil;
    @synchronized(self) {
        handler = [self.decisionHandlers objectForKey:@(lockIdentifier)];
        if (handler) {
            [self.decisionHandlers removeObjectForKey:@(lockIdentifier)];
        }
    }
    if (handler) {
        handler(shouldStart);
    } else {
        RCTLogWarn(@"Lock not found");
    }
}

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages