microsoft / microsoft/microsoft-ui-xaml

Use-after-free in UIAffinityReleaseQueue::DoCleanup - OnParentUpdated AddRefs freed peer (WASDK 2.2.0)

Open
#11,228 1 comment 0 reactions 0 assignees View on GitHub
area-Lifetime bug team-Core
Dominant language
C++
Stars
8.4k
Forks
942
Avg merge
2d 7h
Merged PRs (30d)
105

Description

### Describe the bug

During `UIAffinityReleaseQueue::DoCleanup` on the render tick, teardown of a `CButton` subtree
reaches `CMultiParentShareableDependencyObject::RemoveParent -> CDependencyObject::OnParentChange -> FxCallbacks::FrameworkCallbacks_OnParentUpdated -> DirectUI::DependencyObject::OnParentUpdated -> DXamlCore::TryGetPeer/GetPeerPrivate -> ctl::addref_interface`, where the framework peer being
AddRef'd has **already been freed** — apparently by an earlier release in the same cleanup batch.
The indirect call goes through a dangling vtable, so the process dies in one of several ways
depending on where the wild call lands.

The failure is entirely within native WinUI teardown machinery — no managed or MAUI frames appear in the stack. The 2.2.0 fix for re-entrant dispatch during cleanup (`UIAffinityReleaseQueue_PauseDispatchDuringCleanup`) is present and active in this build but does **not** prevent this variant.

| # | Failure | WER bucket | Failure hash |
| --- | --- | --- | --- |
| 1 | `c0000409` / FAST_FAIL_GUARD_ICALL_CHECK_FAILURE | `FAIL_FAST_GUARD_ICALL_CHECK_FAILURE_c0000409_Microsoft.UI.Xaml.dll!DirectUI::DependencyObject::OnParentUpdated` | `{78134b6d-ebd8-ea31-78d2-e7b09c4158f4}` |
| 2 | `c0000005` read AV in `OnParentUpdated+0xf4`, freed object was `ctl::ComObject` | `INVALID_POINTER_READ_c0000005_...EasingColorKeyFrame_::Release` | `{5723fca6-977c-3977-b9a4-64fdfa2f13fb}` |
| 3 | `c0000005` **write** AV — the wild vtable call landed in `Microsoft.Graphics.Canvas` / `dcompi!CSurfaceFactory::CreateGradientSurface` and wrote through a bad pointer | `INVALID_POINTER_WRITE_c0000005_dcompi.dll!...CreateGradientSurface` | `{8d520244-b787-01ab-955e-aa7c571361d0}` |
| 4 | `c0000374` heap corruption detected later by LFH delayed-free validation during background heap compaction | `HEAP_CORRUPTION_ACTIONABLE_Internal_c0000374_ntdll.dll!RtlReportFatalFailure` | `{099f1bff-accd-a8c4-c18d-0881a82d9f4a}` |

All four share the identical mid-stack; six crash dumps collected within days. Bucket #3 shows the
defect is **memory-corruption class** (wild call into live code performing writes), not just a crash.

Representative stack (bucket 1; symbols from the public server, WASDK 2.1.3/2.2.0 — identical shape on both):

```
Microsoft_UI_Xaml!ctl::addref_interface (inline) <- freed peer
Microsoft_UI_Xaml!DirectUI::DXamlCore::GetPeerPrivate+0x101 [DXamlCore.cpp @ 1676]
Microsoft_UI_Xaml!DirectUI::DXamlCore::TryGetPeer (inline)
Microsoft_UI_Xaml!DirectUI::DependencyObject::OnParentUpdated+0x8f [DependencyObject.cpp @ 3896]
Microsoft_UI_Xaml!CMultiParentShareableDependencyObject::RemoveParent+0x215
Microsoft_UI_Xaml!CDependencyObject::ResetReferenceFromChild+0xba
Microsoft_UI_Xaml!CDependencyObject::ResetReferencesFromChildren+0xe5
Microsoft_UI_Xaml!CShape::~CShape+0x44 <- Rectangle inside the Button template
... (destructor cascade: Rectangle -> Image -> ContentPresenter -> Border -> Viewbox -> Grid -> Grid) ...
Microsoft_UI_Xaml!CButton::`scalar deleting destructor'
Microsoft_UI_Xaml!DirectUI::DependencyObject::DisconnectFrameworkPeerCore+0xd4
Microsoft_UI_Xaml!DirectUI::DependencyObject::OnFinalRelease+0x323
Microsoft_UI_Xaml!ctl::ComBase::ReleaseImpl+0x84
Microsoft_UI_Xaml!DirectUI::UIAffinityReleaseQueue::DoCleanup+0x1aa [UIAffinityReleaseQueue.cpp @ 218]
Microsoft_UI_Xaml!DirectUI::UIAffinityReleaseQueue::BuildTree+0x1e
Microsoft_UI_Xaml!DirectUI::BuildTreeService::BuildTrees+0x119
Microsoft_UI_Xaml!CCoreServices::NWDrawTree+0x54e
Microsoft_UI_Xaml!CXcpBrowserHost::OnTick+0x58
```

The dying subtree is always a Button template hosting a Viewbox-wrapped icon Image, and in bucket 2
the freed object is a color-animation keyframe — consistent with buttons being destroyed while
their pointer-over/pressed state animations are still live.

### Why is this important?

This is a hard crash (process killed by OS, no managed exception) triggered by normal user interaction - navigating between pages and scrolling lists. It is not an edge case; users hit it within minutes of active use. The failure is memory-corruption class (bucket #3 shows writes through a dangling pointer), meaning even non-crashing runs may silently corrupt state. There is no managed-side workaround since the defect is entirely within native WinUI teardown.

### Steps to reproduce the bug

App context: packaged .NET 10 MAUI desktop app (WinUI 3), virtualized lists (~950 rows) whose rows contain Buttons with icon Images; list refreshes replace the ObservableCollection contents; pages carry ToolbarItems (AppBarButtons).

1. Navigate rapidly between pages containing such lists (sub-second intervals).
2. Simultaneously scroll and click list-row buttons as fast as possible while refreshes replace rows.

### Actual behavior

Within minutes, the app hard-crashes with one of the four buckets above (no managed exception — the failure is native).

It is a timing race (destroyed-while-animating subtrees + deferred off-UI-thread final releases draining on a later tick), so reproduction is probabilistic; we reproduce within minutes of "frenzied" interaction. We can provide crash dumps privately on request, and can attempt a minimal non-MAUI repro if that would help.

### Expected behavior

Deferred cleanup of released framework peers must not AddRef peers already destroyed within the same (or an earlier) cleanup batch.

### Screenshots

N/A (native crash; WinDbg outputs available for all buckets).

### NuGet package version

Microsoft.WindowsAppSDK 2.2.0 / Microsoft.UI.Xaml 3.2.2.0 Also observed identically when the app declared WASDK 2.1.3 — the `Microsoft.WindowsAppRuntime.2` framework package resolved to the installed 2.2.0 runtime.

### Windows version

Windows 11 (21H2): Build 22000

### Additional context

Possibly related family: #[10194 ](https://github.com/microsoft/microsoft-ui-xaml/issues/10194) (_free_base AV during Image cleanup at window close, WASDK 1.6.3, closed needs-author-feedback) — same OnParentUpdated-during-teardown neighborhood, different bucket.

Contributor guide

Open the contributing guide

Research direction

Start by reading UAffinityReleaseQueue.cpp around DoCleanup and DependencyObject.cpp around OnParentUpdated, then trace the listed RemoveParent and GetPeerPrivate calls. Reproduce the timing-sensitive teardown with the described WinUI list scenario and use the supplied crash buckets and dumps to verify that deferred cleanup no longer AddRefs a freed peer.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
desktop
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.