fix(pointer): crash (0xc0000005) when the pointer-capturing component has been unmounted#16334
Open
FaithfulAudio wants to merge 2 commits into
Open
Conversation
…g OnPointerCaptureLost CapturePointer and releasePointerCapture look the capturing component up by its cached m_pointerCapturingComponentTag and dereference the result unguarded. That tag can outlive the component it names: when list/ScrollView virtualization recycles the capturing row mid-pan, componentViewDescriptorWithTag returns a descriptor whose .view is null, so winrt::get_self(...)->OnPointerCaptureLost() dereferences null and terminates the process with 0xc0000005. Null-check targetComponentView at both sites. Skipping the notify loses no state transition: CapturePointer overwrites the stale tag immediately below, and releasePointerCapture clears it via the existing m_capturedPointers.size() == 0 branch.
Contributor
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
This was referenced Jul 26, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(pointer): crash (0xc0000005) when the pointer-capturing component has been unmounted
Problem
A Fabric/Composition app crashes with an access violation (
0xc0000005) during ordinarylist interaction on a touch device. Reproduced on a production RNW 0.83.2 new-arch app
(Facilitron FIT) by panning a virtualized inspection list: scroll the list, let
virtualization recycle the row that currently holds pointer capture, then press or release
again. The process dies rather than throwing.
Root cause
Both capture paths in
CompositionEventHandlerlook the capturing component up by itscached tag and then dereference the result without checking it:
m_pointerCapturingComponentTagcan outlive the component it names. If the capturingcomponent is unmounted without releasing capture — exactly what list/ScrollView
virtualization does when it recycles a row mid-pan —
componentViewDescriptorWithTagreturns a descriptor whose
.viewis null.winrt::get_selfon a null projectedreference yields a null pointer, and the
->OnPointerCaptureLost()call dereferences it.The same unguarded pattern appears twice:
CompositionEventHandler::CapturePointer— when a new capture displaces a previous oneCompositionEventHandler::releasePointerCapture— when capture is releasedThis is independent of any pointer-routing behavior; it is purely a missing lifetime check
on a cached tag.
Fix
Null-check
targetComponentViewbefore notifying, at both sites. When the view is gonethere is nothing to notify — in
CapturePointerthe stale tag is overwritten immediatelybelow, and in
releasePointerCapturethe tag is cleared by the existingm_capturedPointers.size() == 0branch. So skipping the notify loses no state transition.Confined to
vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp.No header, signature, or public-API change.
Validation
Built from source (
UseExperimentalNuget=false) into the production RNW 0.83.2new-arch app, so the patched
Microsoft.ReactNative.dllis what was exercised rather thana prebuilt NuGet package.
the process with
0xc0000005. Reproducible.PT_TOUCHcontacts(
InitializeTouchInjection/InjectTouchInput— synthetic mouse input does not exerciseRNW's touch path). Across 8 cycles on the live Inspection List: 10 press-a-row-then-pan-
far-while-still-held events (the row is recycled underneath the held contact, which is the
exact sequence that produced the null view), 3 expand/collapse mount-unmount churns
under the pointer, and 8 hard flicks during mount settle. No crash; the process stayed
responsive throughout. A separate 20-tap soak including capture-loss and scroll-steal
cases also passed with no dropped taps.
Honest limit on that evidence: the binary under test already contains this guard, so the
absence of a crash is consistent with the fix but does not prove causation — demonstrating
that would need an otherwise-identical build without the guard, which I have not produced
(it is a ~4.5 hour from-source framework build). What the soak does establish is that the
guarded code paths are reached repeatedly and behave correctly. The root cause itself is
plain from the code:
m_pointerCapturingComponentTagcan outlive the component it names,and
componentViewDescriptorWithTag(...).viewis documented to return a null.viewfor astale tag.
Caveats for reviewers:
unmounted while holding capture never observes
OnPointerCaptureLost. If you wouldrather RNW proactively released capture at unmount time (in the component teardown path)
so the tag can never go stale, that is a larger but arguably more correct fix and I am
happy to take that direction instead.
which I could not express in the existing unit-test harness. Pointers to the right
fixture welcome.
0.83-stableto match the app in production. Glad to forward-port tomain.Microsoft Reviewers: Open in CodeFlow