[release/11.0] Fix generic virtual dispatch hang after multiple AssemblyLoadContext unloads - #133183
Open
github-actions[bot] wants to merge 1 commit into
Open
[release/11.0] Fix generic virtual dispatch hang after multiple AssemblyLoadContext unloads#133183github-actions[bot] wants to merge 1 commit into
github-actions[bot] wants to merge 1 commit into
Conversation
…unloads (#132859) - [x] Root cause confirmed: `GenericCache.TrySet` detected the flush sentinel with `table.Length == 2`, but the sentinel array is length 3 (2 entries + element 0 used for aux data). The sentinel was therefore never recognized, so after a flush entries were inserted into the *shared* sentinel table. Since every flush re-installs that same sentinel, stale entries survived flushes and generic-virtual-dispatch targets belonging to unloaded collectible `AssemblyLoadContext`s could be returned once native addresses were reused (hang/crash). - [x] Fix in `src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericCache.cs`: added `SENTINEL_TABLE_SIZE` constant and an `IsSentinel(table)` helper based on `CacheElementCount(table)`, used by both sentinel checks in `TrySet`; tightened the constructor assert. - [x] Regression test added: `src/tests/Loader/CollectibleAssemblies/GenericVirtualMethod` (payload assembly with the generic virtual method/override from the issue + runner that loads/invokes/unloads 10 times and asserts the virtual-function-pointer cache is empty after each unload). - [x] Validation: baseline `./build.sh clr+libs -lc release -rc checked` (exit 0); test fails against unfixed CoreLib (cache entry version 2 after flush, exit 101) and passes with the fix (exit 100); other `Loader/CollectibleAssemblies` tests still pass. <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #132562 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> Co-authored-by: Vladimir Sadov <vsadov@microsoft.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
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.
Backport of #132859 to release/11.0
/cc @VSadov @copilot
Customer Impact
#132562
the bug may cause permanent retention of unloadable types thus preventing unloading and causing hangs.
Regression
Root cause is the #89331 (net 8.0) and
it was exposed by #106843 (net 10.0)
Testing
There is a new targeted testcase that deterministically reproduces the issue.
Risk
Low. An off-by-one error allowed a type key be inserted into a sentinel cache (supposed to be always treated as full and replaced with larger real cache on an insertion).
Insertion into sentinel cache is by itself benign. Once that happens the sentinel is truly full and will resize on next insertion.
That is - except when the type first inserted was unloadable, then it cannot ever be unloaded.
The change fixes the part where we recognize sentinel cache correctly and always treat it as full.