Skip to content

[wasm/R2R] TypeIntrinsics_r/_ro: GC reports uninitialized frame slots during throw-helper calls (pMT->Validate assert) #135966

Description

@lewing

Build Information

Build: https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1637640
Build error leg or test failing: R2R-CG2 browser wasm Checked - JIT_r.WorkItemExecution (JIT/Intrinsics/TypeIntrinsics_r)
Pull request: #135899

Error Details

Running test: JIT/Intrinsics/TypeIntrinsics_r/TypeIntrinsics_r.dll
ASSERT FAILED
	Expression: !CREATE_CHECK_STRING(pMT && pMT->Validate())
	Location:   /__w/1/s/src/coreclr/vm/object.cpp:509
	Function:   ValidateInner
Frame (InlinedCallFrame): 0x4fe6b4
Frame (SoftwareExceptionFrame): 0x4feb08
Frame (PrestubMethodFrame): 0x4fed48
Frame (PrestubMethodFrame): 0x4ff238
ASSERT FAILED
	Expression: IsExecutingOnAltStack() || (curSP <= m_pFrame && m_pFrame < m_CacheStackBase) || m_pFrame == (Frame*) -1
	Location:   /__w/1/s/src/coreclr/vm/threads.h:1179
	Function:   GetFrame
(repeated 3x)
Aborted(native code called abort())
RuntimeError: unreachable

Both runs of this lane for #135899 (Helix jobs c34772cf-9b4e-4ed1-bf2a-3762ec6112ec and 49de35bc-09c7-4794-ad81-140e84941c6e) failed this way.

Investigation

This is a pre-existing wasm JIT bug, not caused by #135899. Fix: #135970. It reproduces on code without that PR under DOTNET_GCStress=1.

Repro (Linux x64, Node 23.11.1, CI's Helix correlation and work-item payloads, Checked browser-wasm runtime):

node --experimental-wasm-exnref --stack-size=8192 $CORE_ROOT/corerun.js -c $CORE_ROOT \
  -p System.Reflection.Metadata.MetadataUpdater.IsSupported=false \
  -p System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization=true <workitem>/JIT/JIT_r/JIT_r.dll
Configuration Result
#135899 payload, no stress crashes 20/20 with fixed paths/env (1/3 through JIT_r.sh); passes on macOS
Same, CoreLib recompiled without #135899 0/10
Same, CoreLib recompiled without #135899, DOTNET_GCStress=1 same pMT->Validate() assert
Unrelated PR #135962's payload (no #135899 code), DOTNET_GCStress=1 same assert

Without stress the crash depends on GC timing. Paths, environment size and logging all change whether it fires. #135899 removes box allocations from CoreLib, which moves the first GC onto the bad window.

Root cause (see #135970): on wasm, optRemoveRedundantZeroInits skipped prolog zeroing for GC locals stored after a throwing tree, because IsPotentialGCSafePoint didn't treat GTF_EXCEPT trees as safe points. Wasm has no hardware exceptions, so a null or bounds check is a call to a throw helper, during which a GC can run. Wasm frames report GC locals as untracked slots, so that GC reports whatever an earlier call left in them. Here, Object.GetType's null check on this calls ThrowNullReferenceException before its temps (sp+0x10..0x18) are stored, and the leftover value fails pMT->Validate().

An earlier version of this issue blamed a stale DynamicHelperFrame. That frame is live: it belongs to the delay-load call made from ThrowNullReferenceException.

Related, but different signatures:

Error Message

{
  "ErrorMessage": "",
  "ErrorPattern": [
    "Running test: JIT/Intrinsics/TypeIntrinsics_ro?/TypeIntrinsics_ro?\\.dll",
    "Expression: !CREATE_CHECK_STRING\\(pMT && pMT->Validate\\(\\)\\)"
  ],
  "BuildRetry": false,
  "ExcludeConsoleLog": false
}

Verified against the Helix console logs: each element matches once (the GetFrame assert 3 times) in both failing JIT_r logs. A passing JIT_r log from the same lane (PR #135962) contains only the first line.

Note

This issue was drafted with GitHub Copilot.

Known issue validation

Build: 🔎 https://dev.azure.com/dnceng-public/public/_build/results?buildId=1637640
Error message validated: [Running test: JIT/Intrinsics/TypeIntrinsics_r/TypeIntrinsics_r.dll Expression: !CREATE_CHECK_STRING(pMT && pMT->Validate()) Expression: IsExecutingOnAltStack() || (curSP <= m_pFrame && m_pFrame < m_CacheStackBase) || m_pFrame == (Frame*) -1]
Result validation: ✅ Known issue matched with the provided build.
Validation performed at: 10/16/2026 6:07:11 PM UTC

Activity

  1. added
    arch-wasmWebAssembly architecture
    blocking-clean-ciBlocking PR or rolling runs of 'runtime' or 'runtime-extra-platforms'
    Known Build ErrorUse this to report build issues in the .NET Helix tab
    on Oct 16, 2026
  2. dotnet-policy-service commented on Oct 16, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
    See info in area-owners.md if you want to be subscribed.

  3. dotnet-policy-service commented on Oct 16, 2026

    @dotnet-policy-service
    Contributor

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

  4. changed the title [-][wasm/R2R] TypeIntrinsics_r: stale DynamicHelperFrame after caught NullReferenceException (pMT->Validate assert)[/-] [+][wasm/R2R] TypeIntrinsics_r: GC reports uninitialized frame slots during throw-helper calls (pMT->Validate assert)[/+] on Oct 16, 2026
  5. lewing commented on Oct 16, 2026

    @lewing
    MemberAuthor

    It turns our this is an xplat problem #135970

  6. removed
    untriagedNew issue has not been triaged by the area owner
    on Oct 19, 2026
  7. added this to the 12.0.0 milestone on Oct 19, 2026
  8. changed the title [-][wasm/R2R] TypeIntrinsics_r: GC reports uninitialized frame slots during throw-helper calls (pMT->Validate assert)[/-] [+][wasm/R2R] TypeIntrinsics_r/_ro: GC reports uninitialized frame slots during throw-helper calls (pMT->Validate assert)[/+] on Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Known Build ErrorUse this to report build issues in the .NET Helix tabarch-wasmWebAssembly architecturearea-VM-coreclrblocking-clean-ciBlocking PR or rolling runs of 'runtime' or 'runtime-extra-platforms'

Type

No type

Projects

  • Status
    No status

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions