Skip to content

Commit 333c4e7

Browse files
Fix handling of static virtual method implementation checking (#54710)
- It turns out that GetMethodDescFromMemberDefOrRefOrSpec and FindOrCreateAssociatedMethodDesc are not safe to use on a MemberRef whent the associated MethodTable is not fully loaded. - Instead only use that feature when working with a MethodDef or a fully loaded type, and when working with a not fully loaded type, use MemberLoader::FindMethod instead. - When running the resolution algorithm for doing constraint validation, it also is not necessary to fully resolve to the exact correct MethodDesc, which as that process uses FindOrCreateAssociatedMethodDesc needs to be avoided. - The above was not evident as in many cases, the validation algorithm did not run as it was misplaced and located directly before the call to SetIsFullyLoaded. That code path is only followed if the type is able to fully load without circular dependencies. (Test case CuriouslyRecurringGenericWithUnimplementedMethod added to cover that scenario) - In addition, while investigating these issues, I realized we were lacking checks that the constraints on the impl and decl method were not checked at during type load, but that work was instead deferred to dispatch time. Along with the constraint check there was also a set of accessibility checks that had been missed that are common to all MethodImpl handling. Fix by adding tweaking the logic to share most of that code.
1 parent 24adc91 commit 333c4e7

10 files changed

+290
-49
lines changed

src/coreclr/vm/methodtable.cpp

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5658,6 +5658,22 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const
56585658

56595659
}
56605660

5661+
// Validate implementation of virtual static methods on all implemented interfaces unless:
5662+
// 1) The type resides in a module where sanity checks are disabled (such as System.Private.CoreLib, or an
5663+
// R2R module with type checks disabled)
5664+
// 2) There are no virtual static methods defined on any of the interfaces implemented by this type;
5665+
// 3) The type is abstract in which case it's allowed to leave some virtual static methods unimplemented
5666+
// akin to equivalent behavior of virtual instance method overriding in abstract classes;
5667+
// 4) The type is a not the typical type definition. (The typical type is always checked)
5668+
5669+
if (fNeedsSanityChecks &&
5670+
IsTypicalTypeDefinition() &&
5671+
!IsAbstract())
5672+
{
5673+
if (HasVirtualStaticMethods())
5674+
VerifyThatAllVirtualStaticMethodsAreImplemented();
5675+
}
5676+
56615677
if (locals.fBailed)
56625678
{
56635679
// We couldn't complete security checks on some dependency because it is already being processed by one of our callers.
@@ -5671,22 +5687,6 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const
56715687
}
56725688
else
56735689
{
5674-
// Validate implementation of virtual static methods on all implemented interfaces unless:
5675-
// 1) The type resides in the system module (System.Private.CoreLib); we own this module and ensure
5676-
// its consistency by other means not requiring runtime checks;
5677-
// 2) There are no virtual static methods defined on any of the interfaces implemented by this type;
5678-
// 3) The method is abstract in which case it's allowed to leave some virtual static methods unimplemented
5679-
// akin to equivalent behavior of virtual instance method overriding in abstract classes;
5680-
// 4) The type is a shared generic in which case we generally don't have enough information to perform
5681-
// the validation.
5682-
if (!GetModule()->IsSystem() &&
5683-
HasVirtualStaticMethods() &&
5684-
!IsAbstract() &&
5685-
!IsSharedByGenericInstantiations())
5686-
{
5687-
VerifyThatAllVirtualStaticMethodsAreImplemented();
5688-
}
5689-
56905690
// Finally, mark this method table as fully loaded
56915691
SetIsFullyLoaded();
56925692
}
@@ -9201,7 +9201,7 @@ MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FA
92019201
//==========================================================================================
92029202
// Finds the (non-unboxing) MethodDesc that implements the interface virtual static method pInterfaceMD.
92039203
MethodDesc *
9204-
MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL checkDuplicates, BOOL allowVariantMatches)
9204+
MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented, BOOL allowVariantMatches)
92059205
{
92069206
if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations())
92079207
{
@@ -9231,7 +9231,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc*
92319231
// Search for match on a per-level in the type hierarchy
92329232
for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable())
92339233
{
9234-
MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, checkDuplicates);
9234+
MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented);
92359235
if (pMD != nullptr)
92369236
{
92379237
return pMD;
@@ -9273,7 +9273,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc*
92739273
{
92749274
// Variant or equivalent matching interface found
92759275
// Attempt to resolve on variance matched interface
9276-
pMD = pMT->TryResolveVirtualStaticMethodOnThisType(it.GetInterface(), pInterfaceMD, checkDuplicates);
9276+
pMD = pMT->TryResolveVirtualStaticMethodOnThisType(it.GetInterface(), pInterfaceMD, verifyImplemented);
92779277
if (pMD != nullptr)
92789278
{
92799279
return pMD;
@@ -9295,7 +9295,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc*
92959295
// Try to locate the appropriate MethodImpl matching a given interface static virtual method.
92969296
// Returns nullptr on failure.
92979297
MethodDesc*
9298-
MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL checkDuplicates)
9298+
MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented)
92999299
{
93009300
HRESULT hr = S_OK;
93019301
IMDInternalImport* pMDInternalImport = GetMDImport();
@@ -9347,13 +9347,39 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
93479347
{
93489348
continue;
93499349
}
9350-
MethodDesc *pMethodDecl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(
9350+
MethodDesc *pMethodDecl;
9351+
9352+
if ((TypeFromToken(methodDecl) == mdtMethodDef) || pInterfaceMT->IsFullyLoaded())
9353+
{
9354+
pMethodDecl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(
93519355
GetModule(),
93529356
methodDecl,
93539357
&sigTypeContext,
93549358
/* strictMetadataChecks */ FALSE,
93559359
/* allowInstParam */ FALSE,
93569360
/* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS);
9361+
}
9362+
else if (TypeFromToken(methodDecl) == mdtMemberRef)
9363+
{
9364+
LPCUTF8 szMember;
9365+
PCCOR_SIGNATURE pSig;
9366+
DWORD cSig;
9367+
9368+
IfFailThrow(pMDInternalImport->GetNameAndSigOfMemberRef(methodDecl, &pSig, &cSig, &szMember));
9369+
9370+
// Do a quick name check to avoid excess use of FindMethod
9371+
if (strcmp(szMember, pInterfaceMD->GetName()) != 0)
9372+
{
9373+
continue;
9374+
}
9375+
9376+
pMethodDecl = MemberLoader::FindMethod(pInterfaceMT, szMember, pSig, cSig, GetModule());
9377+
}
9378+
else
9379+
{
9380+
COMPlusThrow(kTypeLoadException, E_FAIL);
9381+
}
9382+
93579383
if (pMethodDecl == nullptr)
93589384
{
93599385
COMPlusThrow(kTypeLoadException, E_FAIL);
@@ -9369,13 +9395,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
93699395
COMPlusThrow(kTypeLoadException, E_FAIL);
93709396
}
93719397

9372-
MethodDesc *pMethodImpl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(
9398+
MethodDesc *pMethodImpl = MemberLoader::GetMethodDescFromMethodDef(
93739399
GetModule(),
93749400
methodBody,
9375-
&sigTypeContext,
9376-
/* strictMetadataChecks */ FALSE,
9377-
/* allowInstParam */ FALSE,
9378-
/* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS);
9401+
FALSE,
9402+
CLASS_LOAD_EXACTPARENTS);
93799403
if (pMethodImpl == nullptr)
93809404
{
93819405
COMPlusThrow(kTypeLoadException, E_FAIL);
@@ -9388,7 +9412,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
93889412
COMPlusThrow(kTypeLoadException, E_FAIL);
93899413
}
93909414

9391-
if (pInterfaceMD->HasMethodInstantiation() || pMethodImpl->HasMethodInstantiation() || HasInstantiation())
9415+
if (!verifyImplemented)
93929416
{
93939417
pMethodImpl = pMethodImpl->FindOrCreateAssociatedMethodDesc(
93949418
pMethodImpl,
@@ -9398,11 +9422,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType
93989422
/* allowInstParam */ FALSE,
93999423
/* forceRemotableMethod */ FALSE,
94009424
/* allowCreate */ TRUE,
9401-
/* level */ CLASS_LOAD_EXACTPARENTS);
9425+
/* level */ CLASS_LOADED);
94029426
}
94039427
if (pMethodImpl != nullptr)
94049428
{
9405-
if (!checkDuplicates)
9429+
if (!verifyImplemented)
94069430
{
94079431
return pMethodImpl;
94089432
}
@@ -9432,7 +9456,7 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented()
94329456
MethodDesc *pMD = it.GetMethodDesc();
94339457
if (pMD->IsVirtual() &&
94349458
pMD->IsStatic() &&
9435-
!ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* checkDuplicates */ TRUE, /* allowVariantMatches */ FALSE))
9459+
!ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE))
94369460
{
94379461
IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
94389462
GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL);

src/coreclr/vm/methodtable.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,7 +2285,11 @@ class MethodTable
22852285

22862286

22872287
// Resolve virtual static interface method pInterfaceMD on this type.
2288-
MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL checkDuplicates = FALSE, BOOL allowVariantMatches = TRUE);
2288+
//
2289+
// Specify allowNullResult to return NULL instead of throwing if the there is no implementation
2290+
// Specify verifyImplemented to verify that there is a match, but do not actually return a final useable MethodDesc
2291+
// Specify allowVariantMatches to permit generic interface variance
2292+
MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented = FALSE, BOOL allowVariantMatches = TRUE);
22892293

22902294
// Try a partial resolve of the constraint call, up to generic code sharing.
22912295
//
@@ -2402,7 +2406,7 @@ class MethodTable
24022406

24032407
// Try to resolve a given static virtual method override on this type. Return nullptr
24042408
// when not found.
2405-
MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL checkDuplicates);
2409+
MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented);
24062410

24072411
public:
24082412
static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl);

src/coreclr/vm/methodtablebuilder.cpp

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,21 +1186,45 @@ MethodTableBuilder::bmtInterfaceEntry::CreateSlotTable(
11861186
CONSISTENCY_CHECK(m_pImplTable == NULL);
11871187

11881188
SLOT_INDEX cSlots = (SLOT_INDEX)GetInterfaceType()->GetMethodTable()->GetNumVirtuals();
1189-
bmtInterfaceSlotImpl * pST = new (pStackingAllocator) bmtInterfaceSlotImpl[cSlots];
1189+
SLOT_INDEX cSlotsTotal = cSlots;
1190+
1191+
if (GetInterfaceType()->GetMethodTable()->HasVirtualStaticMethods())
1192+
{
1193+
MethodTable::MethodIterator it(GetInterfaceType()->GetMethodTable());
1194+
for (; it.IsValid(); it.Next())
1195+
{
1196+
MethodDesc *pDeclMD = it.GetDeclMethodDesc();
1197+
if (pDeclMD->IsStatic() && pDeclMD->IsVirtual())
1198+
{
1199+
cSlotsTotal++;
1200+
}
1201+
}
1202+
}
1203+
1204+
bmtInterfaceSlotImpl * pST = new (pStackingAllocator) bmtInterfaceSlotImpl[cSlotsTotal];
1205+
11901206

11911207
MethodTable::MethodIterator it(GetInterfaceType()->GetMethodTable());
11921208
for (; it.IsValid(); it.Next())
11931209
{
1194-
if (!it.IsVirtual())
1210+
MethodDesc *pDeclMD = it.GetDeclMethodDesc();
1211+
if (!pDeclMD->IsVirtual())
11951212
{
11961213
break;
11971214
}
11981215

11991216
bmtRTMethod * pCurMethod = new (pStackingAllocator)
12001217
bmtRTMethod(GetInterfaceType(), it.GetDeclMethodDesc());
12011218

1202-
CONSISTENCY_CHECK(m_cImplTable == it.GetSlotNumber());
1203-
pST[m_cImplTable++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX);
1219+
if (pDeclMD->IsStatic())
1220+
{
1221+
pST[cSlots + m_cImplTableStatics++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX);
1222+
}
1223+
else
1224+
{
1225+
CONSISTENCY_CHECK(m_cImplTable == it.GetSlotNumber());
1226+
pST[m_cImplTable++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX);
1227+
}
12041228
}
12051229

12061230
m_pImplTable = pST;
@@ -4808,16 +4832,16 @@ VOID MethodTableBuilder::TestMethodImpl(
48084832
{
48094833
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NONVIRTUAL_DECL);
48104834
}
4811-
if (!IsMdVirtual(dwImplAttrs))
4835+
if ((IsMdVirtual(dwImplAttrs) && IsMdStatic(dwImplAttrs)) || (!IsMdVirtual(dwImplAttrs) && !IsMdStatic(dwImplAttrs)))
48124836
{
48134837
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL);
48144838
}
4815-
// Virtual methods cannot be static
4816-
if (IsMdStatic(dwDeclAttrs))
4839+
// Virtual methods on classes/valuetypes cannot be static
4840+
if (IsMdStatic(dwDeclAttrs) && !hDeclMethod.GetOwningType().IsInterface())
48174841
{
48184842
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL);
48194843
}
4820-
if (IsMdStatic(dwImplAttrs))
4844+
if ((!!IsMdStatic(dwImplAttrs)) != (!!IsMdStatic(dwDeclAttrs)))
48214845
{
48224846
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL);
48234847
}
@@ -5421,14 +5445,14 @@ MethodTableBuilder::PlaceVirtualMethods()
54215445
// that the name+signature corresponds to. Used by ProcessMethodImpls and ProcessInexactMethodImpls
54225446
// Always returns the first match that it finds. Affects the ambiguities in code:#ProcessInexactMethodImpls_Ambiguities
54235447
MethodTableBuilder::bmtMethodHandle
5424-
MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig)
5448+
MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, bool searchForStaticMethods)
54255449
{
54265450
STANDARD_VM_CONTRACT;
54275451

54285452
bmtMethodHandle declMethod;
54295453

54305454
bmtInterfaceEntry::InterfaceSlotIterator slotIt =
5431-
pItfEntry->IterateInterfaceSlots(GetStackingAllocator());
5455+
pItfEntry->IterateInterfaceSlots(GetStackingAllocator(), searchForStaticMethods);
54325456
// Check for exact match
54335457
for (; !slotIt.AtEnd(); slotIt.Next())
54345458
{
@@ -5656,7 +5680,7 @@ MethodTableBuilder::ProcessMethodImpls()
56565680
DeclaredMethodIterator it(*this);
56575681
while (it.Next())
56585682
{
5659-
if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl())
5683+
if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && bmtProp->fNoSanityChecks)
56605684
{
56615685
// Non-virtual methods can only be classified as methodImpl when implementing
56625686
// static virtual methods.
@@ -5839,7 +5863,7 @@ MethodTableBuilder::ProcessMethodImpls()
58395863
}
58405864

58415865
// 3. Find the matching method.
5842-
declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig);
5866+
declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, !IsMdVirtual(it.Attrs())); // Search for statics when the impl is non-virtual
58435867
}
58445868
else
58455869
{
@@ -5874,6 +5898,14 @@ MethodTableBuilder::ProcessMethodImpls()
58745898
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token());
58755899
}
58765900

5901+
if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && IsMdStatic(it.Attrs()))
5902+
{
5903+
// Non-virtual methods can only be classified as methodImpl when implementing
5904+
// static virtual methods.
5905+
ValidateStaticMethodImpl(declMethod, *it);//bmtMethodHandle(pCurImplMethod));
5906+
continue;
5907+
}
5908+
58775909
if (bmtMetaData->rgMethodImplTokens[m].fRequiresCovariantReturnTypeChecking)
58785910
{
58795911
it->GetMethodDesc()->SetRequiresCovariantReturnTypeChecking();
@@ -6744,6 +6776,30 @@ MethodTableBuilder::PlaceParentDeclarationOnClass(
67446776
(*pSlotIndex)++;
67456777
} // MethodTableBuilder::PlaceParentDeclarationOnClass
67466778

6779+
VOID MethodTableBuilder::ValidateStaticMethodImpl(
6780+
bmtMethodHandle hDecl,
6781+
bmtMethodHandle hImpl)
6782+
{
6783+
// While we don't want to place the static method impl declarations on the class/interface, we do
6784+
// need to validate the method constraints and signature are compatible
6785+
if (!bmtProp->fNoSanityChecks)
6786+
{
6787+
///////////////////////////////
6788+
// Verify the signatures match
6789+
6790+
MethodImplCompareSignatures(
6791+
hDecl,
6792+
hImpl,
6793+
FALSE /* allowCovariantReturn */,
6794+
IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_INTERFACE_METHOD_IMPL);
6795+
6796+
///////////////////////////////
6797+
// Validate the method impl.
6798+
6799+
TestMethodImpl(hDecl, hImpl);
6800+
}
6801+
}
6802+
67476803
//*******************************************************************************
67486804
// This will validate that all interface methods that were matched during
67496805
// layout also validate against type constraints.

0 commit comments

Comments
 (0)