Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for issue 70385 (stack overflow in the CoreCLR runtime during SVM validation) #71135

Merged
merged 5 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5475,7 +5475,8 @@ namespace
MethodDesc *interfaceMD,
MethodTable *interfaceMT,
BOOL allowVariance,
MethodDesc **candidateMD)
MethodDesc **candidateMD,
ClassLoadLevel level)
{
*candidateMD = NULL;

Expand Down Expand Up @@ -5568,7 +5569,8 @@ namespace
candidateMaybe = pMT->TryResolveVirtualStaticMethodOnThisType(
interfaceMT,
interfaceMD,
/* verifyImplemented */ FALSE);
/* verifyImplemented */ FALSE,
/* level */ level);
}
}
}
Expand All @@ -5588,9 +5590,11 @@ namespace
FALSE, // forceBoxedEntryPoint
candidateMaybe->HasMethodInstantiation() ?
candidateMaybe->AsInstantiatedMethodDesc()->IMD_GetMethodInstantiation() :
Instantiation(), // for method themselves that are generic
Instantiation(), // for method themselves that are generic
FALSE, // allowInstParam
TRUE // forceRemoteableMethod
TRUE, // forceRemoteableMethod
TRUE, // allowCreate
level // level
);
}

Expand All @@ -5607,7 +5611,8 @@ BOOL MethodTable::FindDefaultInterfaceImplementation(
MethodTable *pInterfaceMT,
MethodDesc **ppDefaultMethod,
BOOL allowVariance,
BOOL throwOnConflict
BOOL throwOnConflict,
ClassLoadLevel level
)
{
CONTRACT(BOOL) {
Expand All @@ -5627,7 +5632,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation(

// Check the current method table itself
MethodDesc *candidateMaybe = NULL;
if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe))
if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe, level))
{
_ASSERTE(candidateMaybe != NULL);

Expand Down Expand Up @@ -5667,7 +5672,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation(
MethodTable *pCurMT = it.GetInterface(pMT);

MethodDesc *pCurMD = NULL;
if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD))
if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD, level))
{
//
// Found a match. But is it a more specific match (we want most specific interfaces)
Expand Down Expand Up @@ -7991,7 +7996,8 @@ MethodTable::ResolveVirtualStaticMethod(
BOOL allowNullResult,
BOOL verifyImplemented,
BOOL allowVariantMatches,
BOOL* uniqueResolution)
BOOL* uniqueResolution,
ClassLoadLevel level)
{
if (uniqueResolution != nullptr)
{
Expand Down Expand Up @@ -8024,7 +8030,7 @@ MethodTable::ResolveVirtualStaticMethod(
// Search for match on a per-level in the type hierarchy
for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable())
{
MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented);
MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented, level);
if (pMD != nullptr)
{
return pMD;
Expand Down Expand Up @@ -8068,7 +8074,7 @@ MethodTable::ResolveVirtualStaticMethod(
{
// Variant or equivalent matching interface found
// Attempt to resolve on variance matched interface
pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented);
pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented, level);
if (pMD != nullptr)
{
return pMD;
Expand All @@ -8082,7 +8088,8 @@ MethodTable::ResolveVirtualStaticMethod(
pInterfaceType,
&pMD,
/* allowVariance */ allowVariantMatches,
/* throwOnConflict */ uniqueResolution == nullptr);
/* throwOnConflict */ uniqueResolution == nullptr,
level);
if (haveUniqueDefaultImplementation || (pMD != nullptr && (verifyImplemented || uniqueResolution != nullptr)))
{
// We tolerate conflicts upon verification of implemented SVMs so that they only blow up when actually called at execution time.
Expand Down Expand Up @@ -8112,7 +8119,7 @@ MethodTable::ResolveVirtualStaticMethod(
// Try to locate the appropriate MethodImpl matching a given interface static virtual method.
// Returns nullptr on failure.
MethodDesc*
MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented)
MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, ClassLoadLevel level)
{
HRESULT hr = S_OK;
IMDInternalImport* pMDInternalImport = GetMDImport();
Expand Down Expand Up @@ -8239,7 +8246,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
/* allowInstParam */ FALSE,
/* forceRemotableMethod */ FALSE,
/* allowCreate */ TRUE,
/* level */ CLASS_LOADED);
/* level */ level);
}
if (pMethodImpl != nullptr)
{
Expand Down Expand Up @@ -8285,7 +8292,8 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented()
/* allowNullResult */ TRUE,
/* verifyImplemented */ TRUE,
/* allowVariantMatches */ FALSE,
/* uniqueResolution */ &uniqueResolution)))
/* uniqueResolution */ &uniqueResolution,
/* level */ CLASS_LOAD_EXACTPARENTS)))
{
IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL);
Expand Down
9 changes: 6 additions & 3 deletions src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -2094,13 +2094,15 @@ class MethodTable
// Specify allowVariantMatches to permit generic interface variance
// Specify uniqueResolution to store the flag saying whether the resolution was unambiguous;
// when NULL, throw an AmbiguousResolutionException upon hitting ambiguous SVM resolution.
// The 'level' parameter specifies the load level for the class containing the resolved MethodDesc.
MethodDesc *ResolveVirtualStaticMethod(
MethodTable* pInterfaceType,
MethodDesc* pInterfaceMD,
BOOL allowNullResult,
BOOL verifyImplemented = FALSE,
BOOL allowVariantMatches = TRUE,
BOOL *uniqueResolution = NULL);
BOOL *uniqueResolution = NULL,
ClassLoadLevel level = CLASS_LOADED);

// Try a partial resolve of the constraint call, up to generic code sharing.
//
Expand Down Expand Up @@ -2179,7 +2181,8 @@ class MethodTable
MethodTable *pObjectMT,
MethodDesc **ppDefaultMethod,
BOOL allowVariance,
BOOL throwOnConflict);
BOOL throwOnConflict,
ClassLoadLevel level = CLASS_LOADED);
#endif // DACCESS_COMPILE

DispatchSlot FindDispatchSlot(UINT32 typeID, UINT32 slotNumber, BOOL throwOnConflict);
Expand Down Expand Up @@ -2218,7 +2221,7 @@ class MethodTable

// Try to resolve a given static virtual method override on this type. Return nullptr
// when not found.
MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented);
MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, ClassLoadLevel level);

public:
static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

interface IBase<T>
{
static abstract void Method();
}

interface IDerived<T> : IBase<T>
{
static void IBase<T>.Method()
{
Console.WriteLine("IDerived.Method");
}
}

class Final : IDerived<Final>
{
}

class Program
{
private static void CallSVM<T, U>()
where T : IBase<U>
{
T.Method();
}

static int Main()
{
CallSVM<Final, Final>();
return 100;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@

// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18408
// Copyright (c) Microsoft Corporation. All rights reserved.



// Metadata version: v4.0.30319
.assembly extern System.Runtime
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 7:0:0:0
}
.assembly extern System.Console
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 7:0:0:0
}
.assembly RecursiveGeneric
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.

//
// .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )

.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module RecursiveGeneric.dll
// MVID: {10541B0F-16D6-4F9A-B0EB-E793F524F163}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x03000000


// =============== CLASS MEMBERS DECLARATION ===================

.class interface private abstract auto ansi IBase`1<T>
{
.method public hidebysig static abstract virtual
void Method() cil managed
{
} // end of method IBase`1::Method

} // end of class IBase`1

.class interface private abstract auto ansi IDerived`1<T>
implements class IBase`1<!T>
{
.method public hidebysig static void Method() cil managed
{
.override method void class IBase`1<!T>::Method()
//
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Final.Method"
IL_0006: call void [System.Console]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Final::Method

} // end of class IDerived`1

.class private auto ansi beforefieldinit Final
extends [System.Runtime]System.Object
implements class IDerived`1<class Final>,
class IBase`1<class Final>
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
//
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Final::.ctor

} // end of class Final

.class private auto ansi beforefieldinit Program
extends [System.Runtime]System.Object
{
.method private hidebysig static void CallSVM<(class IBase`1<!!U>) T,U>() cil managed
{
//
.maxstack 8
IL_0000: nop
IL_0001: constrained. !!T
IL_0007: call void class IBase`1<!!U>::Method()
IL_000c: nop
IL_000d: ret
} // end of method Program::CallSVM

.method private hidebysig static int32
Main() cil managed
{
.entrypoint
//
.maxstack 1
.locals init ([0] int32 V_0)
IL_0000: nop
IL_0001: call void Program::CallSVM<class Final,class Final>()
IL_0006: nop
IL_0007: ldc.i4.s 100
IL_0009: stloc.0
IL_000a: br.s IL_000c

IL_000c: ldloc.0
IL_000d: ret
} // end of method Program::Main

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
//
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Runtime]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor

} // end of class Program


// =============================================================

//
//
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>Full</DebugType>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileName).il" />
</ItemGroup>
</Project>