Skip to content

Test Failure: System.Text.RegularExpressions.Tests #128330

Description

@kg

Summary:
CLR "SanityCheck()" assertion at methodtable.cpp:8697 fires during garbage collection stack walk (Object::Validate -> WKS::GCHeap::Relocate -> ScanStackRoots), causing FailFast and process termination on windows-x86 under jitstress2_jitstressregs3. The test (System.Text.RegularExpressions.Tests) appears incidental — the failing assertion is in the GC scan, not in the regex code.

Failed in (1):

Console Log: Console Log
Source: runtime-coreclr libraries-jitstress2-jitstressregs / net11.0-windows-Release-x86-jitstress2_jitstressregs3-Windows.10.Amd64.Open / System.Text.RegularExpressions.Tests

Failed tests:

- net11.0-windows-Release-x86-jitstress2_jitstressregs3-Windows.10.Amd64.Open
  - System.Text.RegularExpressions.Tests

Error Message:

Assert failure(PID 55140 [0x0000d764], Thread: 21008 [0x5210]): SanityCheck()



CORECLR! Object::ValidateInner + 0x137 (0x7323f7b7)

CORECLR! Object::Validate + 0xA9 (0x7323f629)

CORECLR! WKS::GCHeap::Relocate + 0x51 (0x736abdf1)

CORECLR! GcEnumObject + 0x161 (0x733e33d1)

CORECLR! EnumGcRefsX86 + 0x4CB (0x73100f1b)

CORECLR! EECodeManager::EnumGcRefs + 0x1D2 (0x73100a02)

CORECLR! GcStackCrawlCallBack + 0x2B9 (0x733e3879)

CORECLR! Thread::StackWalkFramesEx + 0x223 (0x7329a6d3)

CORECLR! Thread::StackWalkFrames + 0x13B (0x7329a41b)

CORECLR! ScanStackRoots + 0x307 (0x733e0a37)

    File: D:\a\_work\1\s\src\coreclr\vm\methodtable.cpp:8697

    Image: C:\h\w\C6B20A5C\p\dotnet.exe

Stack Trace:

coreclr!FailFastOnAssert+0x22:
72ed5802 c3              ret
0:024> cdb: Reading initial command '$<C:\h\w\C6B20A5C\t\tmpdfqm0q.tmp'
0:024> 
0:024> .load C:\Users\runner\.dotnet\sos\sos.dll
0:024> ~*k

Analysis:
No matching open or closed GitHub issue found via search passes for: "RegularExpressions.Tests jitstress", "jitstress2_jitstressregs3 SanityCheck", "Object Validate FailFastOnAssert jitstress2 x86", "Regression SanityCheck methodtable.cpp 8697". This appears to be a NEW failure. Worth filing — likely a JIT codegen bug on x86 under jitstress2_jitstressregs3 that produces an object reference the GC stack walker cannot validate.

Generated by ci-pipeline-monitor/scripts/update_github.py

Activity

  1. added this to the 11.0.0 milestone on May 18, 2026
  2. added
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on May 18, 2026
  3. dotnet-policy-service commented on May 18, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
    See info in area-owners.md if you want to be subscribed.

  4. JulieLeeMSFT commented on May 21, 2026

    @JulieLeeMSFT
    Member

    @kotlarmilos, forwarding to you to check why this issue can be missed by the ci-scan.

  5. JulieLeeMSFT commented on May 21, 2026

    @JulieLeeMSFT
    Member

    cc @janvorli. Is this GC area issue?

  6. mangod9 commented on May 25, 2026

    @mangod9
    Member

    Dump Analysis

    Dump: dotnet.exe.55140.dmp (Windows x86, 32-bit) from Helix job 90cffbd1-cae8-4b84-bab1-cd2e6df06092, work item System.Text.RegularExpressions.Tests, build 1423181.

    Note: SOS cannot load for x86 target from x64 debugger, so analysis was done with native CDB commands only.

    Crash Details

    Assert: MethodTable::SanityCheck() at methodtable.cpp:8697

    Full call chain (from assert message):

    Object::ValidateInner → Object::Validate → WKS::GCHeap::Relocate → 
    GcEnumObject → EnumGcRefsX86 → EECodeManager::EnumGcRefs → 
    GcStackCrawlCallBack → Thread::StackWalkFramesEx → ScanStackRoots
    

    What Happened

    GC thread 24 (0x5210) triggered a GC and is scanning thread 16 (0x4a40)'s stack. The x86 GC enumerator (EnumGcRefsX86) is walking the frame of an async state machine MoveNext method (Roslyn's ConvertFieldToGeneratedRegexProperty.MoveNext()) at method start 0x28c36138, curOffs 0x23f.

    At curOffs 0x23f, the code is:

    28c36371: call [25927960h]          ; SemanticModel.GetOperation (virtual dispatch)
    28c36377: lea edx, [edi+1Ch]        ; ← curOffs 0x23f = IP after call return
    28c3637a: call RhpCheckedAssignRefEAXAVLocation  ; write barrier store of eax into [edi+1Ch]

    The GC info at this offset reports stack slot [0x5449ef64] as containing a GC object reference. But the actual value is:

    Address Value What it is
    0x5449ef64 0x5449f184 Stack address — points within the same thread's stack
    0x5449f184 0x298f7630 A heap object (valid, but this is the pointee, not what GC sees)

    The slot contains a byref/interior pointer to a stack location, not an object reference. When the GC's Relocate phase tries to validate 0x5449f184 as an object (calling MethodTable::SanityCheck), it fails because it's a stack address, not a heap object.

    Root Cause

    x86 JIT GC encoding bug under jitstressregs3: The JIT32 GC encoder (EnumGcRefsX86) incorrectly reports a byref (interior pointer) stack slot as a regular ref (object reference) slot.

    Under jitstressregs3, the extreme register pressure on x86's 6 general-purpose registers forces the JIT to spill locals that would normally be enregistered. The async state machine MoveNext method has both ref (object reference) and byref (interior/stack pointer) locals. When the stress mode forces these into different stack slots than normal, the GC encoder's tracking of ref vs byref type for spilled locals gets confused, encoding a byref slot as ref.

    Evidence:

    • Stack at 0x5449ef48 contains 0x12345678 — the JIT's poison fill pattern for uninitialized debug locals, confirming this is a Checked/debug JIT build with stress
    • The method is an async MoveNext with many locals (frame size 0x1C4 = 452 bytes)
    • The bad slot is deep in the frame, likely a compiler-generated temp for a ref struct or Span<T> byref

    Why x86-Specific

    1. x86 uses JIT32_GCENCODER — a completely different GC encoding format from x64's GcInfoEncoder. The x86 encoder uses regPtrDsc linked lists and different slot tracking.
    2. x86 has only 6 GP registers (eax, ecx, edx, ebx, esi, edi) — jitstressregs3 on x86 creates much more extreme spill pressure than on x64 with 14 GP registers.
    3. The x86 GC encoder's handling of byref vs ref for spilled locals under stress may have a bug that doesn't exist in the newer x64 GcInfoEncoder.

    Recommended Next Steps

    1. Reproduce: Run Roslyn's ConvertFieldToGeneratedRegexProperty under DOTNET_JitStress=2 DOTNET_JitStressRegs=3 on x86 Checked build
    2. JIT investigation: Check gcencode.cpp's gcMakeRegPtrTable for the JIT32 encoder path — specifically how byref vs ref type is tracked for spilled temporaries in async MoveNext methods
    3. JitDump: Get JIT dump for the MoveNext method with DOTNET_JitDump=*ConvertFieldToGeneratedRegexProperty* under the same stress to see the GC encoding decisions
  7. JulieLeeMSFT commented on Jun 2, 2026

    @JulieLeeMSFT
    Member

    Possibly caused by #123500 or #124472.

    Image
  8. jakobbotsch commented on Jun 12, 2026

    @jakobbotsch
    Member

    Not sure that reasoning makes much sense (MoveNext is not a runtime async method), but I'll take a look regardless.

  9. jakobbotsch commented on Jul 9, 2026

    @jakobbotsch
    Member

    Logs are gone and I don't see similar failures in recent runs. Don't think this is actionable now. I will look at some of the more recent failures.

  10. locked and limited conversation to collaborators on Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions