Skip to content

Harden BindingGroup against re-entrant binding-expression changes#11764

Open
wnvko-msft wants to merge 5 commits into
mainfrom
mvenkov/harden-bindinggroup-updatevalues-reentrancy
Open

Harden BindingGroup against re-entrant binding-expression changes#11764
wnvko-msft wants to merge 5 commits into
mainfrom
mvenkov/harden-bindinggroup-updatevalues-reentrancy

Conversation

@wnvko-msft

@wnvko-msft wnvko-msft commented Jul 8, 2026

Copy link
Copy Markdown

Fixes #1690

Description

BindingGroup iterates its internal _bindingExpressions collection with downward index loops (for (int i = _bindingExpressions.Count - 1; i >= 0; --i)) in ObtainConvertedProposedValues, UpdateValues, CheckValidationRules, and the UpdateTarget loop in CancelEdit. During these loops WPF calls out to user code (value converters, property setters, validation rules). If that user code mutates the binding-expression collection - e.g. a binding leaves the group and the collection shrinks - the cached index becomes stale and _bindingExpressions[i] throws ArgumentOutOfRangeException.

This change hardens those loops: each now iterates over a snapshot taken before the loop (via a new CopyBindingExpressions helper) and skips any expression that has since left the group (bindingExpression.BindingGroup != this). Expressions that join mid-loop always append to the end and were never processed by the original downward loop, so that behavior is unchanged. Side-effecting calls (e.g. UpdateSource) are still invoked for every surviving member, preserving semantics.

Customer Impact

Applications using BindingGroup (commonly via DataGrid row editing/commit) can crash with an unhandled ArgumentOutOfRangeException when a commit or validation modifies the set of bindings in the group. Without this fix the crash takes down the app.

Regression

No. This is a long-standing latent defect in the iteration pattern, not a regression from a recent release.

Testing

  • Built PresentationFramework for x86 and x64 (0 warnings/errors).
  • Reproduced the original crash with a minimal BindingGroup + commit repro app on the locally-built runtime, then confirmed the same app no longer throws after overlaying the fixed PresentationFramework.dll.
  • Manually exercised the edit/commit/cancel paths.

Risk

Low. The change is confined to BindingGroup.cs and only alters how four existing loops enumerate the binding-expression collection (snapshot + membership check) without changing the per-expression work or its ordering. Expressions added mid-loop retain the pre-existing behavior of being deferred to the next pass, and all side-effecting calls continue to run for every surviving member.

Microsoft Reviewers: Open in CodeFlow

@wnvko-msft
wnvko-msft requested review from a team and Copilot July 8, 2026 13:28
@wnvko-msft
wnvko-msft requested a review from a team as a code owner July 8, 2026 13:28
@dotnet-policy-service dotnet-policy-service Bot added the PR metadata: Label to tag PRs, to facilitate with triage label Jul 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens BindingGroup against re-entrant mutation of its internal binding-expression collection during edit/commit/validation flows, preventing ArgumentOutOfRangeException crashes when user code (converters/setters/validation rules) changes group membership mid-iteration.

Changes:

  • Replaces index-based downward loops over _bindingExpressions with snapshot-based iteration to prevent stale indices under re-entrancy.
  • Adds CopyBindingExpressions() helper and uses it in CancelEdit, ObtainConvertedProposedValues, UpdateValues, and CheckValidationRules.
  • Skips snapshot entries that have since left the group (bindingExpression.BindingGroup != this) to preserve per-expression semantics for surviving members.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@h3xds1nz

h3xds1nz commented Jul 8, 2026

Copy link
Copy Markdown
Member

You cannot possibly allocate an array everytime during evaluation.

Sure, the re-entrancy case should be handled gracefully, but this only decreases perf for everyone else based on a degenerate edge case. And it still keeps the undefined behaviour of adding new bindings.

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

Labels

Included in test pass PR Proposed PR metadata: Label to tag PRs, to facilitate with triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ArgumentOutOfRange exception thrown using WPF DataGrid control combo box with .NET Framework 4.8

3 participants