Skip to content

CMiniMdRW::PutString stuck in infinite loop #119958

Closed

Description

@kevingosse

Description

I just ran into a situation where our application (ReSharper backend) got stuck in an infinite loop.

The managed code called System.Reflection.Emit.TypeBuilder.DefineField, which then got stuck with the following callstack:

coreclr!CChainedHash<STRINGHASH>::Find+0x86 [D:\a\_work\1\s\src\coreclr\inc\utilcode.h @ 2683] 
coreclr!StgStringPool::AddString+0x7c [D:\a\_work\1\s\src\coreclr\utilcode\stgpool.cpp @ 922] 
coreclr!MetaData::StringHeapRW::AddString+0xc [D:\a\_work\1\s\src\coreclr\md\heaps\stringheap.h @ 225] 
coreclr!CMiniMdRW::PutString+0x49 [D:\a\_work\1\s\src\coreclr\md\enc\metamodelrw.cpp @ 4197] 
coreclr!RegMeta::DefineField+0x1ce [D:\a\_work\1\s\src\coreclr\md\compiler\emit.cpp @ 2609] 
coreclr!TypeBuilder_DefineField+0x94 [D:\a\_work\1\s\src\coreclr\vm\comdynamic.cpp @ 240] 
System_Private_CoreLib!System.Reflection.Emit.RuntimeFieldBuilder..ctor+0x1a4
System_Private_CoreLib!System.Reflection.Emit.RuntimeTypeBuilder.DefineFieldCore+0xbb
System_Private_CoreLib!System.Reflection.Emit.TypeBuilder.DefineField+0x19
JetBrains_RdFramework_Reflection!JetBrains.Rd.Reflection.ProxyGenerator.ImplementProperty+0xe6

More specifically, it got stuck in this loop: https://github.com/dotnet/runtime/blob/v9.0.8/src/coreclr/inc/utilcode.h#L2681-L2686

I captured a memory dump and was able to inspect the m_rgData table

index => iNext

0 => 28,
1 => 43,
2 => 38,
3 => 42,
4 => 36,
5 => 45,
6 => 40,
7 => 31,
8 => 39,
9 => 44,
10 => 4294967295,
11 => 4294967295,
12 => 4294967295,
13 => 11,
14 => 12,
15 => 10,
16 => 14,
17 => 4294967295,
18 => 4294967295,
19 => 15,
20 => 4294967295,
21 => 16,
22 => 4294967295,
23 => 18,
24 => 20,
25 => 23,
26 => 29,
27 => 13,
28 => 21,
29 => 19,
30 => 17,
31 => 31,
32 => 4294967295,
33 => 26,
34 => 27,
35 => 30,
36 => 22,
37 => 4294967295,
38 => 25,
39 => 4294967295,
40 => 35,
41 => 34,
42 => 24,
43 => 33,
44 => 37,
45 => 41,
46 => 47,
47 => 48,
48 => 4294967295,

For index 31, iNext is 31, which explains the infinite loop.

Unfortunately I don't know how the process got in that state, and I can't reproduce the issue. The dotTrace profiler was active, it was targeting another process but it's possible it activated some ETW sources that could somehow impact the behavior.

I can share the memory dump if it helps.

Reproduction Steps

Unfortunately I don't have a repro.

Expected behavior

System.Reflection.Emit.TypeBuilder.DefineField should complete normally.

Actual behavior

The call got stuck in an infinite loop in CChainedHash<STRINGHASH>::Find.

Regression?

No response

Known Workarounds

No response

Configuration

Windows 11 (x64)
.NET 9.0.8

Other information

No response

Activity

  1. added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Sep 22, 2025
  2. janvorli commented on Sep 23, 2025

    @janvorli
    Member
  3. dotnet-policy-service commented on Sep 23, 2025

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-system-reflection-emit
    See info in area-owners.md if you want to be subscribed.

  4. added this to the Future milestone on Sep 24, 2025
  5. added
    needs-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration
    and removed
    untriagedNew issue has not been triaged by the area owner
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Sep 24, 2025
  6. kevingosse commented on Sep 25, 2025

    @kevingosse
    ContributorAuthor

    We just had a crash reported by a user, involving the same hashtable.

    The crash is an access violation in CChainedHash<STRINGHASH>::Find, more specifically:

                    pItem = &m_rgData[m_iFree];
                    m_iFree = pItem->iNext;
    

    At the time of the crash, m_iFree is garbage (0x4EE01367).

    One interesting thing is that windbg reports some heap corruption:

    Heap address:  00000235bf810000
    Error address: 00000235835bfbc0
    Last known valid blocks: before - 00000235835bb870, after - 00000235835d7fc0
    Error type:    HEAP_FAILURE_MULTIPLE_ENTRIES_CORRUPTION
    Details:       The heap manager detected multiple corrupt heap entries.
    Follow-up:     Enable pageheap.
    
    [...]
    
    0:038> !heap -p -a 00000235835bfbc0
        address 00000235835bfbc0 found in
        _HEAP @ 235bf810000
                  HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
            00000235835bfbc0 0011 0000  [00]   00000235835bfbd0    00100 - (busy)
    

    To try to see if the corruption was caused by a buffer overrun, I inspected the previous heap block:

    0x235835bfbc0 - 0x110 = 0x235835bfab0

    0:038> !heap -p -a 0x235835bfab0
        address 00000235835bfab0 found in
        _HEAP @ 235bf810000
                  HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
            00000235835bfab0 0011 0000  [00]   00000235835bfac0    00108 - (busy)
    

    The UserPtr of this block (00000235835bfac0) is the same address as m_rgData.

    Digging further, m_iSize is 33. Storing 33 entries would require 33*8=264=0x108 bytes of memory. But the heap block is only 0x100 bytes, so the last entry overwrote the header of the next heap block, causing the corrupted heap error.

    This does not explain the garbage value in m_iFree, but clearly there's something wrong going on.

  7. kevingosse commented on Sep 25, 2025

    @kevingosse
    ContributorAuthor

    I have the feeling that it's a race condition somewhere, so I've been reviewing the locking patterns in the calls to CChainedHash<STRINGHASH>::ReHash. In most cases I see a call to the macro LOCKWRITE, however I don't see it in this call chain:

    JetBrains.RdFramework.Reflection.dll!JetBrains.Rd.Reflection.ProxyGenerator.ImplementMethod
    System.Private.CoreLib.dll!System.Reflection.Emit.RuntimeTypeBuilder::DefineMethodOverrideCore
    System.Private.CoreLib.dll!System.Int32 System.Reflection.Emit.RuntimeModuleBuilder::GetMethodMetadataToken
    System.Private.CoreLib.dll!System.Int32 System.Reflection.Emit.RuntimeModuleBuilder::GetMethodTokenNoLock
    System.Private.CoreLib.dll!System.Int32 System.Reflection.Emit.RuntimeModuleBuilder::GetMemberRefOfMethodInfo
    coreclr.dll!ModuleBuilder_GetMemberRefOfMethodInfo
    coreclr.dll!MDInternalRW::TranslateSigWithScope
    coreclr.dll!TranslateSigHelper
    coreclr.dll!ImportHelper::MergeUpdateTokenInSig
    coreclr.dll!ImportHelper::MergeUpdateTokenInFieldSig
    coreclr.dll!ImportHelper::ImportTypeRef
    coreclr.dll!ImportHelper::CreateAssemblyRefFromAssemblyRef
    coreclr.dll!CMiniMdRW::PutString
    coreclr.dll!StgStringPool::AddString
    coreclr.dll!CChainedHash<STRINGHASH>::Find
    coreclr.dll!CChainedHash<STRINGHASH>::ReHash
    

    Is that expected? Is there another locking mechanism that I missed?

  8. jkotas commented on Sep 25, 2025

    @jkotas
    Member

    Yes, that looks like a problem. Would you be interested in submitting a PR with the fix?

  9. kevingosse commented on Sep 26, 2025

    @kevingosse
    ContributorAuthor

    I managed to build a repro, so I confirm that TranslateSigHelper was indeed the problem.

    Repro: https://gist.github.com/kevingosse/eff57610ed6f807b148c3f4b748a1d65 (it requires the JetBrains.RdFramework.Reflection nuget package).

    I've implemented a fix and verified that it doesn't crash anymore: #120143

  10. rizi commented on Sep 26, 2025

    @rizi
    Contributor

    @kevingosse thx for fixing this issue, @jkotas in which version of net will this fix be available?

    Br

  11. jkotas commented on Sep 27, 2025

    @jkotas
    Member

    I have proposed backport to .NET 10

  12. locked and limited conversation to collaborators on Oct 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions