From b3501484a6564b715071cf1fc08e8f100c5ba0c7 Mon Sep 17 00:00:00 2001 From: OrfeasZ Date: Wed, 16 Jul 2025 20:19:05 +0300 Subject: [PATCH] Add guards around nativeProps usage to prevent race conditions Some third-party libraries, like react-native-reanimated, can clone nodes in a different thread while react-native is calling setNativeProps_DEPRECATED. This results in a race condition, where a stale pointer to nativeProps_DEPRECATED can be accessed, resulting in a crash. --- .../ReactCommon/react/renderer/core/ShadowNode.cpp | 1 + .../ReactCommon/react/renderer/core/ShadowNodeFamily.h | 2 ++ .../ReactCommon/react/renderer/uimanager/UIManager.cpp | 3 +++ 3 files changed, 6 insertions(+) diff --git a/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp index e50a06cbf35b..6b9c96adb05c 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp @@ -139,6 +139,7 @@ ShadowNode::ShadowNode( std::shared_ptr ShadowNode::clone( const ShadowNodeFragment& fragment) const { const auto& family = *family_; + std::lock_guard npGuard(family.nativePropsMutex); const auto& componentDescriptor = family.componentDescriptor_; if (family.nativeProps_DEPRECATED != nullptr) { auto propsParserContext = PropsParserContext{family_->getSurfaceId(), {}}; diff --git a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h index 2699cad51ba5..ecf1ee63d7bb 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h +++ b/packages/react-native/ReactCommon/react/renderer/core/ShadowNodeFamily.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -118,6 +119,7 @@ class ShadowNodeFamily final : public jsi::NativeState { * architecture and will be removed in the future. */ mutable std::unique_ptr nativeProps_DEPRECATED; + mutable std::recursive_mutex nativePropsMutex; /** * @return tag for the ShadowNodeFamily. diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp index 4855071d9abd..fb393c4bb4c2 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -118,6 +118,7 @@ std::shared_ptr UIManager::cloneNode( auto props = ShadowNodeFragment::propsPlaceholder(); if (!rawProps.isEmpty()) { + std::lock_guard npGuard(family.nativePropsMutex); if (family.nativeProps_DEPRECATED != nullptr) { // 1. update the nativeProps_DEPRECATED props. // @@ -426,6 +427,7 @@ void UIManager::setNativeProps_DEPRECATED( const std::shared_ptr& shadowNode, RawProps rawProps) const { auto& family = shadowNode->getFamily(); + family.nativePropsMutex.lock(); if (family.nativeProps_DEPRECATED) { // Values in `rawProps` patch (take precedence over) // `nativeProps_DEPRECATED`. For example, if both `nativeProps_DEPRECATED` @@ -440,6 +442,7 @@ void UIManager::setNativeProps_DEPRECATED( family.nativeProps_DEPRECATED = std::make_unique((folly::dynamic)rawProps); } + family.nativePropsMutex.unlock(); shadowTreeRegistry_.visit( family.getSurfaceId(), [&](const ShadowTree& shadowTree) {