Skip to content

Fix AV in ICorProfilerInfo2::GetClassLayout for Runtime Async continuation types#131133

Open
UtkarshBandekar wants to merge 2 commits into
dotnet:mainfrom
UtkarshBandekar:av-fix
Open

Fix AV in ICorProfilerInfo2::GetClassLayout for Runtime Async continuation types#131133
UtkarshBandekar wants to merge 2 commits into
dotnet:mainfrom
UtkarshBandekar:av-fix

Conversation

@UtkarshBandekar

Copy link
Copy Markdown

Description

ICorProfilerInfo2::GetClassLayout() crashes with an access violation when
called on a ClassID that refers to a dynamically-generated Runtime Async
Continuation MethodTable (introduced in #120411). These types have no
backing metadata (TypeDef/FieldDef tokens), but GetClassLayout assumes
every field it encounters can be resolved via FieldDesc::GetMemberDef().

For continuation types, ApproxFieldDescIterator::Next() can return a null
FieldDesc*, and the existing code calls GetMemberDef() on it
unconditionally, causing a null-pointer dereference at field.h:134
(return TokenFromRid(m_mb, mdtFieldDef)).

Fixes/relates to #120800.

Root cause

Confirmed via WinDbg on a local repro (.NET 11.0.0-preview.6.26359.118):

  • !dumpmt /d <ClassID> resolves to Continuation_24_0_1_16_1, with
    mdToken: 0000000002000000 — RID 0, i.e. no real metadata token.
  • The runtime already exposes MethodTable::IsContinuationWithoutMetadata()
    / TypeHandle::IsContinuationWithoutMetadata(), but GetClassLayout never
    calls it.
  • Fault: mov ecx, dword ptr [rax+8] with rax == 0, inside
    FieldDesc::GetMemberDef inlined into
    ProfToEEInterfaceImpl::GetClassLayout+0x15e.
  • Full native stack confirms this triggers from an entirely ordinary
    profiler heap-walk (ForceGCGetClassLayout called per class), so any
    profiler doing a heap snapshot while a Runtime Async continuation is live
    will hit this.

Fix

Add an early check for typeHandle.IsContinuationWithoutMetadata() in
ProfToEEInterfaceImpl::GetClassLayout, returning CORPROF_E_DATAINCOMPLETE
— consistent with how this file already signals "no data available" in
other entrypoints (GetModuleInfo2, GetRVAStaticAddress,
EnumModuleFrozenObjects, etc.).

Repro

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net11.0</TargetFramework>
    <Features>runtime-async=on</Features>
  </PropertyGroup>
</Project>

Minimal repro: suspend an async method mid-await (object live across the
suspension point), then have a profiler call GetClassLayout on the
resulting continuation's ClassID — e.g. via a full-heap GC snapshot
(ForceGC + heap walk), which is what triggers it in practice.

Testing

  • Add profiler test coverage under src/tests/profiler/ exercising
    GetClassLayout against a live Runtime Async continuation object.
  • Verify CORPROF_E_DATAINCOMPLETE is returned rather than crashing.

@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Jul 21, 2026
@azure-pipelines

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

@UtkarshBandekar

Copy link
Copy Markdown
Author

@dotnet-policy-service agree

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

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

Labels

area-Diagnostics-coreclr community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants