Skip to content

Commit 2d85297

Browse files
protikbiswas100Protik Biswasjonthysell
authored
adding nullptr check before destroyShadow calls (#15127)
* adding nullptr check before destroyShadow calls * fixing nit issue * Change files * adding chnage type * adding correct format * Apply suggestion from @jonthysell Adding type as prerelease Co-authored-by: Jon Thysell <thysell@gmail.com> * Apply suggestion from @jonthysell apply dependantChangeTyoe to patch Co-authored-by: Jon Thysell <thysell@gmail.com> --------- Co-authored-by: Protik Biswas <protikbiswas100@microsoft.com> Co-authored-by: Jon Thysell <thysell@gmail.com>
1 parent 054949a commit 2d85297

5 files changed

Lines changed: 77 additions & 13 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "adding nullptr check before destroyShadow calls",
4+
"packageName": "react-native-windows",
5+
"email": "protikbiswas100@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Modules/PaperUIManagerModule.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,22 +501,37 @@ class UIManagerModule : public std::enable_shared_from_this<UIManagerModule>, pu
501501
}
502502

503503
void DropView(int64_t tag, bool removeChildren = true, bool zombieView = false) {
504+
// Prevent re-entry and double deletion which can cause memory corruption
505+
if (m_nodesBeingDropped.find(tag) != m_nodesBeingDropped.end()) {
506+
return;
507+
}
508+
504509
if (auto node = m_nodeRegistry.findNode(tag)) {
510+
// Mark this node as being dropped
511+
m_nodesBeingDropped.insert(tag);
512+
505513
node->onDropViewInstance();
506514

507515
m_nativeUIManager->RemoveView(*node, removeChildren);
508516

509517
if (zombieView)
510518
node->m_zombie = true;
511519

512-
for (auto childTag : node->m_children)
520+
// Make a copy of children to avoid iterator invalidation
521+
auto children = node->m_children;
522+
for (auto childTag : children) {
513523
DropView(childTag, removeChildren, zombieView);
524+
}
514525

515526
if (removeChildren)
516527
node->removeAllChildren();
517528

518-
if (!zombieView)
529+
if (!zombieView) {
519530
m_nodeRegistry.removeNode(tag);
531+
}
532+
533+
// Remove from tracking set
534+
m_nodesBeingDropped.erase(tag);
520535
}
521536
}
522537

@@ -525,6 +540,8 @@ class UIManagerModule : public std::enable_shared_from_this<UIManagerModule>, pu
525540
ShadowNodeRegistry m_nodeRegistry;
526541
std::shared_ptr<NativeUIManager> m_nativeUIManager;
527542
const React::JSValueObject accessibilityEventTypes = React::JSValueObject{{"typeViewFocused", 8}};
543+
// Track nodes being dropped to prevent double-deletion and corruption
544+
std::unordered_set<int64_t> m_nodesBeingDropped;
528545
};
529546

530547
UIManager::UIManager() : m_module(std::make_shared<UIManagerModule>()) {}

vnext/Microsoft.ReactNative/Views/ShadowNodeBase.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,19 @@ ViewManagerBase *ShadowNodeBase::GetViewManager() const {
2828
}
2929

3030
void ShadowNodeBase::updateProperties(winrt::Microsoft::ReactNative::JSValueObject &props) {
31-
GetViewManager()->UpdateProperties(this, props);
31+
auto viewManager = GetViewManager();
32+
if (viewManager) {
33+
viewManager->UpdateProperties(this, props);
34+
}
3235
}
3336

3437
void ShadowNodeBase::createView(const winrt::Microsoft::ReactNative::JSValueObject &props) {
35-
m_view = GetViewManager()->CreateView(this->m_tag, props);
38+
auto viewManager = GetViewManager();
39+
if (!viewManager) {
40+
return;
41+
}
42+
43+
m_view = viewManager->CreateView(this->m_tag, props);
3644

3745
if (g_HasActualSizeProperty == TriBit::Undefined) {
3846
if (auto uielement = m_view.try_as<xaml::UIElement>()) {
@@ -49,23 +57,38 @@ bool ShadowNodeBase::NeedsForceLayout() {
4957
void ShadowNodeBase::dispatchCommand(
5058
const std::string &commandId,
5159
winrt::Microsoft::ReactNative::JSValueArray &&commandArgs) {
52-
GetViewManager()->DispatchCommand(GetView(), commandId, std::move(commandArgs));
60+
auto viewManager = GetViewManager();
61+
if (viewManager) {
62+
viewManager->DispatchCommand(GetView(), commandId, std::move(commandArgs));
63+
}
5364
}
5465

5566
void ShadowNodeBase::removeAllChildren() {
56-
GetViewManager()->RemoveAllChildren(GetView());
67+
auto viewManager = GetViewManager();
68+
if (viewManager) {
69+
viewManager->RemoveAllChildren(GetView());
70+
}
5771
}
5872

5973
void ShadowNodeBase::AddView(ShadowNode &child, int64_t index) {
60-
this->GetViewManager()->AddView(GetView(), static_cast<ShadowNodeBase &>(child).GetView(), index);
74+
auto viewManager = GetViewManager();
75+
if (viewManager) {
76+
viewManager->AddView(GetView(), static_cast<ShadowNodeBase &>(child).GetView(), index);
77+
}
6178
}
6279

6380
void ShadowNodeBase::RemoveChildAt(int64_t indexToRemove) {
64-
GetViewManager()->RemoveChildAt(GetView(), indexToRemove);
81+
auto viewManager = GetViewManager();
82+
if (viewManager) {
83+
viewManager->RemoveChildAt(GetView(), indexToRemove);
84+
}
6585
}
6686

6787
void ShadowNodeBase::onDropViewInstance() {
68-
GetViewManager()->OnDropViewInstance(m_view);
88+
auto viewManager = GetViewManager();
89+
if (viewManager) {
90+
viewManager->OnDropViewInstance(m_view);
91+
}
6992
m_handledKeyboardEventHandler = nullptr;
7093
}
7194

@@ -81,7 +104,12 @@ void ShadowNodeBase::ReplaceView(XamlView view) {
81104
}
82105

83106
void ShadowNodeBase::ReplaceChild(const XamlView &oldChildView, const XamlView &newChildView) {
84-
GetViewManager()->ReplaceChild(m_view, oldChildView, newChildView);
107+
// Add safety checks to prevent crashes during cleanup
108+
auto viewManager = GetViewManager();
109+
if (!viewManager || !m_view || !oldChildView || !newChildView) {
110+
return;
111+
}
112+
viewManager->ReplaceChild(m_view, oldChildView, newChildView);
85113
}
86114

87115
void ShadowNodeBase::ReparentView(XamlView view) {

vnext/Microsoft.ReactNative/Views/ShadowNodeRegistry.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
namespace Microsoft::ReactNative {
1111

1212
void ShadowNodeDeleter::operator()(ShadowNode *node) {
13-
if (node->m_viewManager)
14-
node->m_viewManager->destroyShadow(node);
13+
if (node && node->m_viewManager) {
14+
// Clear the view manager pointer to prevent double deletion
15+
auto viewManager = node->m_viewManager;
16+
node->m_viewManager = nullptr;
17+
viewManager->destroyShadow(node);
18+
}
1519
}
1620

1721
void ShadowNodeRegistry::addRootView(std::unique_ptr<ShadowNode, ShadowNodeDeleter> &&root, int64_t rootViewTag) {

vnext/Microsoft.ReactNative/Views/ViewManagerBase.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,11 @@ ShadowNode *ViewManagerBase::createShadow() const {
187187
}
188188

189189
void ViewManagerBase::destroyShadow(ShadowNode *node) const {
190-
delete node;
190+
if (node) {
191+
// Ensure we don't double-delete by clearing the view manager pointer
192+
node->m_viewManager = nullptr;
193+
delete node;
194+
}
191195
}
192196

193197
void ViewManagerBase::GetExportedCustomBubblingEventTypeConstants(
@@ -293,6 +297,10 @@ void ViewManagerBase::RemoveAllChildren(const XamlView &parent) {}
293297

294298
void ViewManagerBase::ReplaceChild(const XamlView &parent, const XamlView &oldChild, const XamlView &newChild) {
295299
// ASSERT: Child must either implement or not allow children.
300+
// Add null checks to prevent crashes during cleanup
301+
if (!parent || !oldChild || !newChild) {
302+
return;
303+
}
296304
assert(false);
297305
}
298306

0 commit comments

Comments
 (0)