Skip to content

Commit e028946

Browse files
fkgozalimeta-codesync[bot]
authored andcommitted
Add C++ test for Scheduler delegate UAF after JS-throw teardown (#56800)
Summary: Pull Request resolved: #56800 Reproduces, in a standalone gtest, the use-after-free race between Scheduler teardown and pending rendering-update lambdas previously enqueued via runtimeScheduler->scheduleRenderingUpdate inside Scheduler::uiManagerDidFinishTransaction. The lambda captures the SchedulerDelegate by raw pointer; when the delegate is destroyed (as part of an instance teardown triggered by an uncaught fatal error) before the lambda runs, the dereference is a use-after-free unless the invalidation-token guard in Scheduler::setDelegate (enableSchedulerDelegateInvalidation) is enabled at queue time. The test: - Drives the *real* Scheduler::uiManagerDidFinishTransaction so the lambda is enqueued via the regular code path into a real RuntimeScheduler's pending-rendering-updates queue. - Initiates teardown via an uncaught JSI host-function throw routed through RuntimeScheduler's onTaskError callback (the test's analog of a host-side fatal handler), which drops the delegate. - Triggers the next event loop tick to drain the queue. Three test cases: 1. Sanity_LambdaRunsOnNextTickWhenDelegateAlive -- baseline: lambda runs and reaches the delegate when no teardown happens. 2. GuardEnabled_JSThrowInitiatedTeardownIsSafe -- with the guard ON, the pending lambda observes the invalidation token after teardown and returns without touching the freed delegate. Safe. 3. GuardDisabled_JSThrowInitiatedTeardownIsUAF -- with the guard OFF, the lambda dereferences the destroyed delegate. Caught by EXPECT_DEATH via a magic-sentinel ASSERT_EQ in the recording delegate, or by AddressSanitizer on the vptr load. Fantom is intentionally not used here: it shares the global runtime VM across tests, which would interfere with this test's contract that no further JS executes after a fatal-driven instance teardown. Changelog: [Internal] Reviewed By: javache Differential Revision: D104777850 fbshipit-source-id: 7ecacaf21a4a9b121575fcc66c2fcbb6bfd4adc7
1 parent 8823a80 commit e028946

3 files changed

Lines changed: 623 additions & 0 deletions

File tree

packages/react-native/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ let reactFabric = RNTarget(
473473
"components/unimplementedview",
474474
"components/virtualview",
475475
"components/root/tests",
476+
"scheduler/tests",
476477
],
477478
dependencies: [.reactNativeDependencies, .reactJsiExecutor, .rctTypesafety, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .reactRendererDebug, .reactGraphics, .yoga, .reactJsInspectorTracing],
478479
sources: ["animated", "animationbackend", "animations", "attributedstring", "core", "componentregistry", "componentregistry/native", "components/root", "components/view", "components/view/platform/cxx", "components/scrollview", "components/scrollview/platform/cxx", "components/scrollview/platform/ios", "components/legacyviewmanagerinterop", "components/legacyviewmanagerinterop/platform/ios", "dom", "scheduler", "mounting", "observers/events", "observers/intersection", "observers/mutation", "telemetry", "consistency", "leakchecker", "uimanager", "uimanager/consistency", "viewtransition"]

packages/react-native/ReactCommon/React-Fabric.podspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Pod::Spec.new do |s|
159159

160160
s.subspec "scheduler" do |ss|
161161
ss.source_files = podspec_sources("react/renderer/scheduler/**/*.{m,mm,cpp,h}", "react/renderer/scheduler/**/*.h")
162+
ss.exclude_files = "react/renderer/scheduler/tests"
162163
ss.header_dir = "react/renderer/scheduler"
163164

164165
ss.dependency "React-Fabric/animationbackend"

0 commit comments

Comments
 (0)