Skip to content

Fix SIGSEGV in ShadowNode::getTag() caused by use-after-free in findShadowNodeByTag_DEPRECATED#55751

Open
Abbondanzo wants to merge 1 commit intofacebook:mainfrom
Abbondanzo:export-D94376636
Open

Fix SIGSEGV in ShadowNode::getTag() caused by use-after-free in findShadowNodeByTag_DEPRECATED#55751
Abbondanzo wants to merge 1 commit intofacebook:mainfrom
Abbondanzo:export-D94376636

Conversation

@Abbondanzo
Copy link
Contributor

Summary:
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]

Differential Revision: D94376636

…hadowNodeByTag_DEPRECATED

Summary:
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]

Differential Revision: D94376636
@meta-cla meta-cla bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Feb 25, 2026
@meta-codesync
Copy link

meta-codesync bot commented Feb 25, 2026

@Abbondanzo has exported this pull request. If you are a Meta employee, you can view the originating Diff in D94376636.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. fb-exported meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants