Skip to content

[WIP] Fix assert failure for null check on thread - #136949

Open
VSadov with Copilot wants to merge 4 commits into
mainfrom
copilot/fix-assert-failure-th-isnull
Open

VSadov with Copilot wants to merge 4 commits into
mainfrom
copilot/fix-assert-failure-th-isnull

Conversation

Copilot AI commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.


This section details on the original issue you should resolve

<issue_title>Assert failure: !th.IsNull()</issue_title>
<issue_description>## Build Information
Build: https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1654712
Build error leg or test failing: OutOfProcessTest.JIT/Regression_3/DevDiv_461649/DevDiv_461649/DevDiv_461649.cmd

Error Message

Fill the error message using step by step known issues guidance.

{
  "ErrorMessage": "Failed test: OutOfProcessTest.JIT/Regression_3/DevDiv_461649/DevDiv_461649/DevDiv_461649.cmd",
  "BuildRetry": false,
  "ExcludeConsoleLog": false
}

Known issue validation

Build: 🔎 https://dev.azure.com/dnceng-public/public/_build/results?buildId=1654712
Error message validated: [Failed test: OutOfProcessTest.JIT/Regression_3/DevDiv_461649/DevDiv_461649/DevDiv_461649.cmd]
Result validation: ❌ Known issue did not match with the provided build.
Validation performed at: 10/30/2026 1:07:04 AM UTC

Report

Summary

24-Hour Hit Count 7-Day Hit Count 1-Month Count
0 0 0

<agent_instructions>Re: also fails in #135794

What fails

On x86 (non-Unix), ResolveWorker builds a resolve stub for a virtual or interface call. The stub needs the number of stack bytes the callee pops, so it calls pMD->SizeOfArgStack() on the method from the dispatch token. That call walks the method signature. For a value-type parameter or return it needs the type layout already loaded, because it runs under ENABLE_FORBID_GC_LOADER_USE. Otherwise !th.IsNull() asserts.

Why it started

Before #135805, the R2R caller's fixups loaded those types. Now ExternalMethodFixupWorker calls PrepareForUseAsAVirtualCallTargetOfANativeImage(pGCRefMap). For a plain instance method with an import map, it only stores the map via SetGCRefMap and returns. So the method has a map but its value types may be unloaded.

The fix

The x86 GCRefMap already encodes the stack-pop size. The runtime already trusts it for the same purpose in UpdateRegDisplay_Impl (via CbStackPopUsingGCRefMap). For non-vararg methods, CbStackPop() equals SizeOfArgStack().

PTR_BYTE pGCRefMap = pMD->FindGCRefMap(objectType, true /* isDispatch */);
if (pGCRefMap != NULL)
{
    GCRefMapDecoder decoder(pGCRefMap);
    stackArgumentsSize = decoder.ReadStackPop() * sizeof(TADDR);
}
else
{
    ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
    stackArgumentsSize = pMD->SizeOfArgStack();   // unchanged fallback
}
  • It uses the same lookup StubDispatchFrame uses for GC scanning.
  • Nothing is loaded and the scope isn't relaxed. It also adds #include "gcrefmap.h", and it stays inside the existing x86 #if.
  • The fallback should be safe. As far as I can tell from reading, preparation skips the load only when it stores a shareable map. Generic methods and methods that need a hidden generic argument are fully prepared. I haven't confirmed that.

</agent_instructions>

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

@jkotas Regression likely caused by https://github.com//pull/135806 cc @davidwrighton

@azure-pipelines

azure-pipelines Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI linked an issue Nov 4, 2026 that may be closed by this pull request
@VSadov VSadov closed this Nov 4, 2026
Copilot stopped work on behalf of VSadov due to an error November 4, 2026 00:09
Copilot AI requested a review from VSadov November 4, 2026 00:09
@VSadov VSadov reopened this Nov 4, 2026
Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
@jkotas
jkotas marked this pull request as ready for review November 4, 2026 00:18
@jkotas
jkotas requested a balanced review from Copilot November 4, 2026 00:19
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

On Windows x86, the map-hit path can create a dispatch stub with signature layouts still unloaded, allowing a later null-receiver call to assert instead of throwing NullReferenceException.

1 open issue
What changed in this PR

This PR addresses #136674 by changing CoreCLR’s non-Unix x86 virtual-call stub creation to obtain stack-pop sizes without requiring unloaded value-type layouts.

Changes:

  • Adds gcrefmap.h and reads the stack-pop size from a dispatch-compatible GCRefMap when available.
  • Preserves the existing signature-based fallback and loader-use restriction when no map is found.
File Description
src/​coreclr/​vm/​virtualcallstub.cpp Uses GCRefMap stack-pop metadata during x86 resolve-stub creation.

Review effort: Balanced

if (pGCRefMap != NULL)
{
GCRefMapDecoder decoder(pGCRefMap);
stackArgumentsSize = decoder.ReadStackPop() * sizeof(TADDR);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in 337d00e: the dispatch-stub branch of AdjustContextForVirtualStub now reads stackArgumentsSize from the resolve stub (via failTarget() and ResolveHolder::FromFailEntry). It no longer walks the signature or needs the loader-forbid scope. I dropped the unused pMgr assignment. I haven't compiled or run it, and I didn't add the null-receiver test.

Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>

This comment was marked as resolved.

Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>

This comment was marked as resolved.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The Windows x86-only changes adjust ESP during exception recovery, but only Linux x64 validation is reported, leaving ReadyToRun dispatch and null-receiver delegate stack restoration unverified on the target platform.

1 open issue

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assert failure: !th.IsNull()

4 participants