Skip to content

Commit 7d2d3a2

Browse files
Abbondanzometa-codesync[bot]
authored andcommitted
Fix SIGSEGV in ShadowNode::getTag() caused by use-after-free in findShadowNodeByTag_DEPRECATED (#55751)
Summary: Pull Request resolved: #55751 A SIGSEGV crash is occurring in production when `ShadowNode::getTag()` is called on a destroyed ShadowNode during focus navigation (`FabricUIManagerBinding::findNextFocusableElement`). **Root cause**: `UIManager::findShadowNodeByTag_DEPRECATED` captures a **raw pointer** to the root shadow node inside a `tryCommit` callback, then dereferences it **after the lock is released**. Another thread can commit a new tree in between, destroying the old root and leaving a dangling pointer: ``` tryCommit([&](const RootShadowNode& old) { rootShadowNode = &old; // capture raw address return nullptr; // cancel commit, release lock }, {}); // !!! LOCK RELEASED — another thread can replace + destroy the old root rootShadowNode->getChildren(); // use-after-free → SIGSEGV ``` **Fix**: Replace the `tryCommit` + raw pointer pattern with `ShadowTree::getCurrentRevision()`, which returns a `ShadowTreeRevision` by value containing a `shared_ptr<const RootShadowNode>`. The `shared_ptr` copy keeps the root node alive for the entire traversal. **Why the old root gets destroyed**: After a commit, the old root's `shared_ptr` in `currentRevision_` is replaced. The `MountingCoordinator::push()` also overwrites `lastRevision_`. If no other holder remains (e.g. `baseRevision_` holds an earlier root), the old root's refcount drops to 0 and it is freed — while the finder thread may still hold a raw pointer to it. Changelog: [Internal] Reviewed By: mdvacca, javache Differential Revision: D94376636 fbshipit-source-id: 07882beed246d61c1476f7f7790586c60a1debdb
1 parent ec29c29 commit 7d2d3a2

22 files changed

+476
-62
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<e3509361f95f3681f0bb583da5540d31>>
7+
* @generated SignedSource<<477777b9a795b57f3bb3eaeb030738a9>>
88
*/
99

1010
/**
@@ -354,6 +354,12 @@ public object ReactNativeFeatureFlags {
354354
@JvmStatic
355355
public fun enableVirtualViewDebugFeatures(): Boolean = accessor.enableVirtualViewDebugFeatures()
356356

357+
/**
358+
* Fix a use-after-free race condition in findShadowNodeByTag_DEPRECATED by using getCurrentRevision() instead of tryCommit() with a raw pointer.
359+
*/
360+
@JvmStatic
361+
public fun fixFindShadowNodeByTagRaceCondition(): Boolean = accessor.fixFindShadowNodeByTagRaceCondition()
362+
357363
/**
358364
* Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React.
359365
*/

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<ae86a1485b81df483dad1108b39fae31>>
7+
* @generated SignedSource<<c7b6ea7f1672df7bb3396375378784c5>>
88
*/
99

1010
/**
@@ -74,6 +74,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
7474
private var enableViewRecyclingForViewCache: Boolean? = null
7575
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
7676
private var enableVirtualViewDebugFeaturesCache: Boolean? = null
77+
private var fixFindShadowNodeByTagRaceConditionCache: Boolean? = null
7778
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
7879
private var fixTextClippingAndroid15useBoundsForWidthCache: Boolean? = null
7980
private var fuseboxAssertSingleHostStateCache: Boolean? = null
@@ -590,6 +591,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
590591
return cached
591592
}
592593

594+
override fun fixFindShadowNodeByTagRaceCondition(): Boolean {
595+
var cached = fixFindShadowNodeByTagRaceConditionCache
596+
if (cached == null) {
597+
cached = ReactNativeFeatureFlagsCxxInterop.fixFindShadowNodeByTagRaceCondition()
598+
fixFindShadowNodeByTagRaceConditionCache = cached
599+
}
600+
return cached
601+
}
602+
593603
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean {
594604
var cached = fixMappingOfEventPrioritiesBetweenFabricAndReactCache
595605
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<b57dc37b228c3d622be039686ea11471>>
7+
* @generated SignedSource<<ec036cff49622b8c2b52d2f9eaa59e6c>>
88
*/
99

1010
/**
@@ -136,6 +136,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
136136

137137
@DoNotStrip @JvmStatic public external fun enableVirtualViewDebugFeatures(): Boolean
138138

139+
@DoNotStrip @JvmStatic public external fun fixFindShadowNodeByTagRaceCondition(): Boolean
140+
139141
@DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
140142

141143
@DoNotStrip @JvmStatic public external fun fixTextClippingAndroid15useBoundsForWidth(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<65cdfdcbe22ff163b75e0b067bd72693>>
7+
* @generated SignedSource<<523e3c35d4bd1fc85f2a3bb26b8aad3f>>
88
*/
99

1010
/**
@@ -131,6 +131,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
131131

132132
override fun enableVirtualViewDebugFeatures(): Boolean = false
133133

134+
override fun fixFindShadowNodeByTagRaceCondition(): Boolean = false
135+
134136
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false
135137

136138
override fun fixTextClippingAndroid15useBoundsForWidth(): Boolean = false

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<7d8e2872030e38ccb038d9d7aab214a2>>
7+
* @generated SignedSource<<cfd6a4514be320519a57566182b73f69>>
88
*/
99

1010
/**
@@ -78,6 +78,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
7878
private var enableViewRecyclingForViewCache: Boolean? = null
7979
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
8080
private var enableVirtualViewDebugFeaturesCache: Boolean? = null
81+
private var fixFindShadowNodeByTagRaceConditionCache: Boolean? = null
8182
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
8283
private var fixTextClippingAndroid15useBoundsForWidthCache: Boolean? = null
8384
private var fuseboxAssertSingleHostStateCache: Boolean? = null
@@ -648,6 +649,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
648649
return cached
649650
}
650651

652+
override fun fixFindShadowNodeByTagRaceCondition(): Boolean {
653+
var cached = fixFindShadowNodeByTagRaceConditionCache
654+
if (cached == null) {
655+
cached = currentProvider.fixFindShadowNodeByTagRaceCondition()
656+
accessedFeatureFlags.add("fixFindShadowNodeByTagRaceCondition")
657+
fixFindShadowNodeByTagRaceConditionCache = cached
658+
}
659+
return cached
660+
}
661+
651662
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean {
652663
var cached = fixMappingOfEventPrioritiesBetweenFabricAndReactCache
653664
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<460d442da5dc25a441b671e7b30e7e56>>
7+
* @generated SignedSource<<bbaa4d1498f606886341d34a62acb508>>
88
*/
99

1010
/**
@@ -131,6 +131,8 @@ public interface ReactNativeFeatureFlagsProvider {
131131

132132
@DoNotStrip public fun enableVirtualViewDebugFeatures(): Boolean
133133

134+
@DoNotStrip public fun fixFindShadowNodeByTagRaceCondition(): Boolean
135+
134136
@DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
135137

136138
@DoNotStrip public fun fixTextClippingAndroid15useBoundsForWidth(): Boolean

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<8d00a6310e0fae475007f11fa56a5a6f>>
7+
* @generated SignedSource<<45063df01d7ce8726b4a7d901f1b3341>>
88
*/
99

1010
/**
@@ -363,6 +363,12 @@ class ReactNativeFeatureFlagsJavaProvider
363363
return method(javaProvider_);
364364
}
365365

366+
bool fixFindShadowNodeByTagRaceCondition() override {
367+
static const auto method =
368+
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixFindShadowNodeByTagRaceCondition");
369+
return method(javaProvider_);
370+
}
371+
366372
bool fixMappingOfEventPrioritiesBetweenFabricAndReact() override {
367373
static const auto method =
368374
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixMappingOfEventPrioritiesBetweenFabricAndReact");
@@ -811,6 +817,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableVirtualViewDebugFeatures(
811817
return ReactNativeFeatureFlags::enableVirtualViewDebugFeatures();
812818
}
813819

820+
bool JReactNativeFeatureFlagsCxxInterop::fixFindShadowNodeByTagRaceCondition(
821+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
822+
return ReactNativeFeatureFlags::fixFindShadowNodeByTagRaceCondition();
823+
}
824+
814825
bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact(
815826
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
816827
return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact();
@@ -1149,6 +1160,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
11491160
makeNativeMethod(
11501161
"enableVirtualViewDebugFeatures",
11511162
JReactNativeFeatureFlagsCxxInterop::enableVirtualViewDebugFeatures),
1163+
makeNativeMethod(
1164+
"fixFindShadowNodeByTagRaceCondition",
1165+
JReactNativeFeatureFlagsCxxInterop::fixFindShadowNodeByTagRaceCondition),
11521166
makeNativeMethod(
11531167
"fixMappingOfEventPrioritiesBetweenFabricAndReact",
11541168
JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact),

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<c884eaa1b04699c8deb66eb62bb5e3f8>>
7+
* @generated SignedSource<<5ac93ed057017f8d1a388b8029614f18>>
88
*/
99

1010
/**
@@ -192,6 +192,9 @@ class JReactNativeFeatureFlagsCxxInterop
192192
static bool enableVirtualViewDebugFeatures(
193193
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
194194

195+
static bool fixFindShadowNodeByTagRaceCondition(
196+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
197+
195198
static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(
196199
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
197200

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<20f3c045c134a0dae21f8428c6f8714a>>
7+
* @generated SignedSource<<9d81f74c5926706ee353813e594575e8>>
88
*/
99

1010
/**
@@ -242,6 +242,10 @@ bool ReactNativeFeatureFlags::enableVirtualViewDebugFeatures() {
242242
return getAccessor().enableVirtualViewDebugFeatures();
243243
}
244244

245+
bool ReactNativeFeatureFlags::fixFindShadowNodeByTagRaceCondition() {
246+
return getAccessor().fixFindShadowNodeByTagRaceCondition();
247+
}
248+
245249
bool ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact() {
246250
return getAccessor().fixMappingOfEventPrioritiesBetweenFabricAndReact();
247251
}

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<20d4471389baccef0854624bb31550a5>>
7+
* @generated SignedSource<<84e2800073ffab2313a4e27897c0c246>>
88
*/
99

1010
/**
@@ -309,6 +309,11 @@ class ReactNativeFeatureFlags {
309309
*/
310310
RN_EXPORT static bool enableVirtualViewDebugFeatures();
311311

312+
/**
313+
* Fix a use-after-free race condition in findShadowNodeByTag_DEPRECATED by using getCurrentRevision() instead of tryCommit() with a raw pointer.
314+
*/
315+
RN_EXPORT static bool fixFindShadowNodeByTagRaceCondition();
316+
312317
/**
313318
* Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React.
314319
*/

0 commit comments

Comments
 (0)