Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 22, 2026

Description

TypeMapping.GetOrCreateExternalTypeMapping threw non-deterministic ArgumentException when type names hashed to the same value. For example, "Windows.Media.Devices.Core.FrameFlashControl" and "Windows.Security.Credentials.PasswordCredential" produce identical GetHashCode() results, causing collision on insertion.

Root cause: LazyExternalTypeDictionary used Dictionary<int, DelayedType> keyed by string.GetHashCode() instead of the string itself.

Changes

  • TypeMapLazyDictionary.cs: Changed LazyExternalTypeDictionary to use Dictionary<string, DelayedType>
    • Removed ComputeHashCode method
    • Direct string key usage in TryGetOrLoadType and Add
// Before: Hash collision possible
Dictionary<int, DelayedType> _lazyData;  // int = key.GetHashCode()

// After: No collision possible  
Dictionary<string, DelayedType> _lazyData;  // string key directly

This matches the collision-safe approach already used in LazyProxyTypeDictionary but simplified since external type mappings don't require multi-value support.

The fix addresses a design issue where hash collisions could occur. The existing test coverage is sufficient to validate the behavior.

Original prompt

This section details on the original issue you should resolve

<issue_title>TypeMapping.GetOrCreateExternalTypeMapping throws ArgumentException for duplicate key on CoreCLR occasionally</issue_title>
<issue_description>### Description

In the CsWinRT 3.0 CI, we have been occasionally seeing TypeMapping.GetOrCreateExternalTypeMapping throw ArgumentException. This seems to happen for different types and reruns typically cause it to pass. I have also been able to repro it locally a couple times but took multiple runs of the same exe.

It does seem to happen only on CoreCLR and we haven't been able to repro it with AOT. Inspecting the assemblies doesn't show any duplicate attributes for the TypeMap. The specific typemap entry from the exception stack in this instance is in WinRT.Interop.dll.

Reproduction Steps

Repro and traces

Run ClassActivation.exe until repro

Expected behavior

No exception when there is no duplicate typemap entry

Actual behavior

Exception type:   System.ArgumentException
Message:          An item with the same key has already been added. Key: Windows.Foundation.IAsyncOperation`1<System.Collections.Generic.IReadOnlyList`1<Windows.ApplicationModel.Contacts.ContactAnnotationList>>
InnerException:   <none>
StackTrace (generated):
    SP       IP       Function
    027BE478 0509B6D4 System_Private_CoreLib!System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException[[System.__Canon, System.Private.CoreLib]](System.__Canon)+0x1c
    027BE488 6E88BAD4 System_Private_CoreLib!System.Runtime.InteropServices.TypeMapLazyDictionary+LazyExternalTypeDictionary.Add(System.String, TypeNameUtf8, System.Reflection.RuntimeAssembly)+0x84
    027BE4A8 6E88B5C4 System_Private_CoreLib!System.Runtime.InteropServices.TypeMapLazyDictionary.NewExternalTypeEntry(CallbackContext*, ProcessAttributesCallbackArg*)+0x84
    027BE628 0509BA93 System_Private_CoreLib!System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()+0x2b
    027BE634 6E88B845 System_Private_CoreLib!System.Runtime.InteropServices.TypeMapLazyDictionary.CreateMaps(System.RuntimeType, BOOL (CallbackContext*, ProcessAttributesCallbackArg*), BOOL (CallbackContext*, ProcessAttributesCallbackArg*))+0xf5
    027BE6A4 6E88B879 System_Private_CoreLib!System.Runtime.InteropServices.TypeMapLazyDictionary.CreateExternalTypeMap(System.RuntimeType)+0x29
    027BE6C4 6EBFE03D System_Private_CoreLib!System.Runtime.InteropServices.TypeMapping.GetOrCreateExternalTypeMapping[[System.__Canon, System.Private.CoreLib]]()+0x1d
    027BE6D0 05097A3F WinRT_Runtime2!WindowsRuntime.InteropServices.WindowsRuntimeMarshallingInfo..cctor()+0x1f

Regression?

No response

Known Workarounds

No response

Configuration

.NET 10
x86/x64 CoreCLR

Other information

No response</issue_description>

<agent_instructions>Please address the issue based on details in the comment #123337 (comment)</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@manodasanW FYI @AaronRobinsonMSFT @jkoritzinsky @AaronRobinsonMSFT I can repro this with the attached `ClassActivation.exe` case. I'll take a look today. @AaronRobinsonMSFT @manodasanW I think the TypeMap failure is a red-herring. There is an exception prior to that that seems to cause an issue. I suppose it is possible it poisoned the cache and then succeeds later; that is just a guess. It would be helpful if you could verify the first exception is expected because it only seems to happen when the TypeMap fails, so I assume it is the underlying issue.

I'm using the createdump collection tool to help find the original exception.

Set the following to use the createdump tool.

SET DOTNET_DbgEnableMiniDump=1
SET DOTNET_EnableCrashReport=1
SET DOTNET_DbgMiniDumpType=4
SET DOTNET_DbgMiniDumpName=%CD%\crash.%%d.dmp
0:000> k
 # ChildEBP RetAddr      
00 02b7c9cc 75086468     ntdll!NtWaitForSingleObject+0xc
01 02b7c9cc 750863d2     KERNELBASE!WaitForSingleObjectEx+0x88
02 02b7c9e0 675c21e2     KERNELBASE!WaitForSingleObject+0x12
03 02b7ca94 675bfae2     coreclr!LaunchCreateDump+0x92 [...\runtime\src\coreclr\vm\excep.cpp @ 3830] 
04 02b7cac8 675c244d     coreclr!CreateCrashDumpIfEnabled+0x82 [...\runtime\src\coreclr\vm\excep.cpp @ 3865] 
05 02b7cb14 675c1bcc     coreclr!NotifyAppDomainsOfUnhandledException+0xb7 [...\runtime\src\coreclr\vm\excep.cpp @ 5151] 
06 02b7cb90 674a8eee     coreclr!InternalUnhandledExceptionFilter_Worker+0x16f [...\runtime\src\coreclr\vm\excep.cpp @ 4468] 
07 02b7cd88 673e9b92     coreclr!SfiNextWorker+0x17e [...\runtime\src\coreclr\vm\exceptionhandling.cpp @ 4001...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes dotnet/runtime#123337

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

…nstead of hash codes

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
@AaronRobinsonMSFT
Copy link
Member

@copilot Please remove the added test. It doesn't really provide any protection here.

@AaronRobinsonMSFT AaronRobinsonMSFT added this to the 11.0.0 milestone Jan 22, 2026
Copilot AI and others added 2 commits January 22, 2026 18:04
Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
@AaronRobinsonMSFT
Copy link
Member

@copilot Please revert all changes under tests/Interop/TypeMap. There is little value to the added tests outside of what is already present. The failure case this PR represents doesn't represent an obvious test failure but rather a poor design decision.

Copilot AI changed the title [WIP] Fix ArgumentException in TypeMapping for CoreCLR Fix hash collision in TypeMapLazyDictionary causing non-deterministic ArgumentException Jan 22, 2026
Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 22, 2026

@copilot Please revert all changes under tests/Interop/TypeMap. There is little value to the added tests outside of what is already present. The failure case this PR represents doesn't represent an ob...

Reverted all test changes in commit 0e5dc16. Only the core fix in TypeMapLazyDictionary.cs remains.

@AaronRobinsonMSFT
Copy link
Member

/backport to release/10.0

@github-actions
Copy link
Contributor

Started backporting to release/10.0 (link to workflow run)

Copy link
Contributor

Copilot AI left a comment

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 fixes a non-deterministic ArgumentException in TypeMapping.GetOrCreateExternalTypeMapping caused by hash collisions when external type names shared the same string.GetHashCode() value. The change updates the lazy external type map to use the full string key instead of its hash code, eliminating the possibility of collisions in this path.

Changes:

  • Updated LazyExternalTypeDictionary to use Dictionary<string, DelayedType> instead of Dictionary<int, DelayedType>.
  • Removed the now-unnecessary ComputeHashCode helper and all call sites.
  • Adjusted TryGetOrLoadType and Add to use the string key directly for lookups and duplicate checks.

@AaronRobinsonMSFT AaronRobinsonMSFT merged commit 52bc52c into main Jan 23, 2026
152 of 155 checks passed
@github-project-automation github-project-automation bot moved this to Done in AppModel Jan 23, 2026
@AaronRobinsonMSFT AaronRobinsonMSFT deleted the copilot/fix-argumentexception-duplicate-key branch January 23, 2026 16:57
steveisok pushed a commit that referenced this pull request Jan 26, 2026
…n-deterministic ArgumentException (#123517)

Backport of #123502 to release/10.0

/cc @AaronRobinsonMSFT @Copilot

## Customer Impact

- [x] Customer reported
- [ ] Found internally

When large numbers of `TypeMapAssociationAttribute` exist, it can occur
that collisions of type names (`string`s) occur. The current approach
ignored the possibility of collision and simply acquired the result of
`GetHashCode()` and inserted that as a key into a `Dictionary<,>`. This
meant there was no opportunity to handle the case when two non-equal
`string` instances generate the same hash code. Using the hash code
directly was done to avoid the non-trivial increase in memory usage
imposed by using the `string` instances as the key.

The solution is to use the `string` as the key instead.

## Regression

- [ ] Yes
- [x] No

## Testing

Verified using a CsWinRT repro case. Prior to this fix a collision would
occur 1 / 100 times run. After this change the repro ran for more than
1000 iterations with no collisions.

## Risk

Low. This new approach does increase the baseline amount of memory when
the `TypeMap` is used on coreclr. This was deemed acceptable since the
coreclr isn't considered the P1 scenario for `TypeMap`. The native AOT,
the P1 scenario for `TypeMap`, is not impacted by this change.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants