SIGN IN SIGN UP
facebook / react-native UNCLAIMED

A framework for building native applications using React

0 0 0 C++

Fix use-after-free during hot reload on macOS (#55277)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/55277

Fixes a crash occurring during Metro hot reload on macOS after prolonged sessions (15+ minutes) - P2129051751:

```
:horizon::(anonymous namespace)::handleSignal(int, __siginfo*, void*)
:_sigtramp
:std::__1::__hash_const_iterator<std::__1::__hash_node<std::__1::__hash_value_type<int, std::__1::unique_ptr<facebook::react::AnimatedNode, std::__1::default_delete<facebook::react::AnimatedNode>>>, void*>*> std::__1::__hash_table<std::__1::__hash_value_type<int, std::__1::unique_ptr<facebook::react::AnimatedNode, std::__1::default_delete<facebook::react::AnimatedNode>>>, std::__1::__unordered_map_hasher<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<facebook::react::AnimatedNode, std::__1::default_delete<facebook::react::AnimatedNode>>>, std::__1::hash<int>, std::__1::equal_to<int>, true>, std::__1::__unordered_map_equal<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<facebook::react::AnimatedNode, std::__1::default_delete<facebook::react::AnimatedNode>>>, std::__1::equal_to<int>, std::__1::hash<int>, true>, std::__1::allocator<std::__1::__hash_value_type<int, std::__1::unique_ptr<facebook::react::AnimatedNode, std::__1::default_delete<facebook::react::AnimatedNode>>>>>::find<int>(int const&) const
:std::__1::unordered_map<int, std::__1::unique_ptr<facebook::react::AnimatedNode, std::__1::default_delete<facebook::react::AnimatedNode>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<facebook::react::AnimatedNode, std::__1::default_delete<facebook::react::AnimatedNode>>>>>::find[abi:ne200100](int const&) const
:facebook::react::AnimatedNode* facebook::react::NativeAnimatedNodesManager::getAnimatedNode<facebook::react::AnimatedNode, void>(int) const requires std::is_base_of_v<facebook::react::AnimatedNode, facebook::react::AnimatedNode>
:facebook::react::NativeAnimatedNodesManager::updateNodes(std::__1::set<int, std::__1::less<int>, std::__1::allocator<int>> const&)
:facebook::react::NativeAnimatedNodesManager::onAnimationFrame(double)
:facebook::react::NativeAnimatedNodesManager::onRender()
:facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0::operator()() const
:decltype(std::declval<facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0&>()()) std::__1::__invoke[abi:ne200100]<facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0&>(facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0&)
:void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ne200100]<facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0&>(facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0&)
:void std::__1::__invoke_r[abi:ne200100]<void, facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0&>(facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0&)
:std::__1::__function::__alloc_func<facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0, std::__1::allocator<facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0>, void ()>::operator()[abi:ne200100]()
:std::__1::__function::__func<facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0, std::__1::allocator<facebook::react::NativeAnimatedNodesManager::startRenderCallbackIfNeeded(bool)::$_0>, void ()>::operator()()
:std::__1::__function::__value_func<void ()>::operator()[abi:ne200100]() const
:std::__1::function<void ()>::operator()() const
:-[RCTAnimatedModuleProvider _onDisplayLinkTick]
:-[RCTPlatformDisplayLink tick]
:__RCTPlatformDisplayLinkCallBack_block_invoke
:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
:__CFRunLoopDoBlocks
:__CFRunLoopRun
:_CFRunLoopRunSpecificWithOptions
:RunCurrentEventLoopInMode
:ReceiveNextEventCommon
:_BlockUntilNextEventMatchingListInMode
:_DPSBlockUntilNextEventMatchingListInMode
:_DPSNextEvent
:-[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]
:-[NSApplication(NSEventRouting) nextEventMatchingMask:untilDate:inMode:dequeue:]
:-[NSApplication(RCTTouchHandlerOverride) override_nextEventMatchingMask:untilDate:inMode:dequeue:]
:-[NSApplication(RCTSurfaceTouchHandlerOverride) override_surface_nextEventMatchingMask:untilDate:inMode:dequeue:]
:-[NSApplication run]
:main
:Stack End
```

The display link callback was checking if the animation provider was alive but not holding a strong reference during callback execution. This allowed the NativeAnimatedNodesManager to be destroyed mid-callback, causing a use-after-free.

By storing the result of weak_ptr::lock() in a local variable, we ensure the provider (and its managed NativeAnimatedNodesManager) remains alive for the entire duration of the _onRender() callback.

Changelog: [Internal]

Differential Revision: D91236980

fbshipit-source-id: dff6035176c1d2eb4d61c668b5a6311e5e4521d4
Y
Yannick Loriot committed
05921817a9be4e2cf114102ed2cef6c38b5b1cf1
Parent: afb6847
Committed by meta-codesync[bot] <215208954+meta-codesync[bot]@users.noreply.github.com> on 1/22/2026, 11:02:04 PM