diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs index 312d33f2fd5eb3..ea770a28a8c61b 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs @@ -570,6 +570,11 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle) TypeSpecification typeSpecification = _metadataReader.GetTypeSpecification(handle); BlobReader signatureReader = _metadataReader.GetBlobReader(typeSpecification.Signature); + +#if ILVERIFICATION + ValidateTypeSpecificationSignature(signatureReader); +#endif + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); TypeDesc parsedType = parser.ParseType(); @@ -579,6 +584,25 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle) return parsedType; } +#if ILVERIFICATION + private static void ValidateTypeSpecificationSignature(BlobReader signatureReader) + { + switch (signatureReader.ReadSignatureTypeCode()) + { + case SignatureTypeCode.Pointer: + case SignatureTypeCode.FunctionPointer: + case SignatureTypeCode.Array: + case SignatureTypeCode.SZArray: + case SignatureTypeCode.GenericTypeInstance: + case SignatureTypeCode.GenericTypeParameter: + case SignatureTypeCode.GenericMethodParameter: + return; + } + + ThrowHelper.ThrowBadImageFormatException(); + } +#endif + private object ResolveMemberReference(MemberReferenceHandle handle) { MemberReference memberReference = _metadataReader.GetMemberReference(handle); diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il index 1139e289e03719..4132dd2c34a9d7 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/AccessTests.il @@ -264,7 +264,7 @@ .method public hidebysig instance void Call.GenericMethodWithPrivateNestedClass_Invalid_MethodAccess() cil managed { - call void class SimpleClass::GenericMethod() + call void SimpleClass::GenericMethod() ret } diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il new file mode 100644 index 00000000000000..de23cc5e6bcee2 --- /dev/null +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Runtime +{ +} + +.assembly BaseTypeTests +{ +} + +.class public auto ansi beforefieldinit ObjectTypeSpecBase_InvalidType_InvalidBaseType + extends object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } +} + +.class public auto ansi beforefieldinit ArrayTypeSpecBase_InvalidType_InvalidBaseType + extends int32[] +{ +} + +.class public auto ansi beforefieldinit PointerTypeSpecBase_InvalidType_InvalidBaseType + extends int32* +{ +} + +.class interface public auto ansi abstract NilBaseInterface_ValidType_Valid +{ +} + +.class public auto ansi beforefieldinit GenericBase`1 + extends [System.Runtime]System.Object +{ +} + +.class public auto ansi beforefieldinit GenericClassTypeSpecBase_ValidType_Valid + extends class GenericBase`1 +{ +} + +.class public auto ansi beforefieldinit GenericOpenClassTypeSpecBase_ValidType_Valid`1 + extends class GenericBase`1 +{ +} + +.class public sequential ansi sealed beforefieldinit ValueTypeBase + extends [System.Runtime]System.ValueType +{ +} + +.class public auto ansi beforefieldinit ValueTypeBase_InvalidType_InvalidBaseType + extends valuetype ValueTypeBase +{ +} + +.class public sequential ansi sealed beforefieldinit GenericValueTypeBase`1 + extends [System.Runtime]System.ValueType +{ +} + +.class public auto ansi beforefieldinit GenericValueTypeSpecBase_InvalidType_InvalidBaseType + extends valuetype GenericValueTypeBase`1 +{ +} diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.ilproj b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.ilproj new file mode 100644 index 00000000000000..356b4dcc778989 --- /dev/null +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.ilproj @@ -0,0 +1,9 @@ + + + $(MSBuildProjectName) + + + + + + diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il index 65de357f4be137..0ba5d66814c063 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/CastingTests.il @@ -343,10 +343,10 @@ ret } - .method public static void Casting.BoxByRefInt_Invalid_BoxByRef.ExpectedValClassObjRefVariable(int32& i) cil managed + .method public static void Casting.BoxIntPointer_Invalid_UnmanagedPointer.ExpectedValClassObjRefVariable(int32* i) cil managed { ldarg.0 - box int32& + box int32* pop ret } diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il index d8ca59cd5f1c76..67f99c03a97fcf 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/LoadStoreIndirectTests.il @@ -140,7 +140,7 @@ ldloca.s V_0 ldstr "Hello" - stobj string + stobj [System.Runtime]System.String ret } diff --git a/src/coreclr/tools/ILVerification/ILVerification.projitems b/src/coreclr/tools/ILVerification/ILVerification.projitems index c301f2da1d298e..05c12be9f40b4d 100644 --- a/src/coreclr/tools/ILVerification/ILVerification.projitems +++ b/src/coreclr/tools/ILVerification/ILVerification.projitems @@ -13,6 +13,7 @@ + ILVERIFICATION;$(DefineConstants) $(MSBuildThisFileDirectory)..\Common\ diff --git a/src/coreclr/tools/ILVerification/Strings.resx b/src/coreclr/tools/ILVerification/Strings.resx index 02be5d1e00ff0c..fd31723fb735cb 100644 --- a/src/coreclr/tools/ILVerification/Strings.resx +++ b/src/coreclr/tools/ILVerification/Strings.resx @@ -453,4 +453,7 @@ Stack must be empty before localloc, except for the size item. + + Type '{0}' has invalid base type '{1}'. + diff --git a/src/coreclr/tools/ILVerification/TypeVerifier.cs b/src/coreclr/tools/ILVerification/TypeVerifier.cs index 4caec0c170c7eb..b6a5cd0f8a6a9b 100644 --- a/src/coreclr/tools/ILVerification/TypeVerifier.cs +++ b/src/coreclr/tools/ILVerification/TypeVerifier.cs @@ -39,9 +39,62 @@ public TypeVerifier(EcmaModule module, TypeDefinitionHandle typeDefinitionHandle public void Verify() { + // Once the base type metadata is invalid, later checks can force the type system to + // resolve the same bad base token again and produce a duplicate token resolution error. + if (!VerifyBaseType()) + { + return; + } + VerifyInterfaces(); } + private bool VerifyBaseType() + { + TypeDefinition typeDefinition = _module.MetadataReader.GetTypeDefinition(_typeDefinitionHandle); + EcmaType type = _module.GetType(_typeDefinitionHandle); + EntityHandle baseType = typeDefinition.BaseType; + if (baseType.IsNil) + { + if (!type.IsObject && !type.IsModuleType && !type.IsInterface) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; + } + } + else + { + TypeDesc resolvedBaseType; + try + { + resolvedBaseType = _module.GetType(baseType); + } + catch (BadImageFormatException) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; + } + catch (TypeSystemException) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; + } + + // Arrays, pointers, and generic variables are valid TypeSpec forms in other + // metadata and IL token contexts, so GetType must continue to resolve them. A + // BaseType TypeSpec is narrower: it has to name a constructed generic class, + // which resolves to InstantiatedType. + if (resolvedBaseType.IsValueType || + (baseType.Kind == HandleKind.TypeSpecification && resolvedBaseType is not InstantiatedType)) + { + VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType)); + return false; + } + } + + return true; + } + public void VerifyInterfaces() { TypeDefinition typeDefinition = _module.MetadataReader.GetTypeDefinition(_typeDefinitionHandle); @@ -115,17 +168,37 @@ public void VerifyInterfaces() private string Format(TypeDesc type) { - if (_verifierOptions.IncludeMetadataTokensInErrorMessages) + TypeDesc typeDefinition = type.GetTypeDefinition(); + if (_verifierOptions.IncludeMetadataTokensInErrorMessages && typeDefinition is EcmaType ecmaType) { - TypeDesc typeDesc = type.GetTypeDefinition(); - EcmaModule module = (EcmaModule)((MetadataType)typeDesc).Module; + EcmaModule module = (EcmaModule)ecmaType.Module; + return string.Format("{0}([{1}]0x{2:X8})", type, module, module.MetadataReader.GetToken(ecmaType.Handle)); + } + + return type.ToString(); + } - return string.Format("{0}([{1}]0x{2:X8})", type, module, module.MetadataReader.GetToken(((EcmaType)type).Handle)); + private string Format(EntityHandle handle) + { + if (handle.IsNil) + { + return "nil"; } - else + + try { - return type.ToString(); + return Format(_module.GetType(handle)); } + catch (BadImageFormatException) + { + } + catch (TypeSystemException) + { + } + + return _verifierOptions.IncludeMetadataTokensInErrorMessages ? + string.Format("{0}([{1}]0x{2:X8})", handle.Kind, _module, _module.MetadataReader.GetToken(handle)) : + handle.Kind.ToString(); } private string Format(TypeDesc interfaceTypeDesc, EcmaModule module, InterfaceImplementation interfaceImplementation) diff --git a/src/coreclr/tools/ILVerification/VerifierError.cs b/src/coreclr/tools/ILVerification/VerifierError.cs index bc4821fbedb7d4..c9d4e8e18c8d55 100644 --- a/src/coreclr/tools/ILVerification/VerifierError.cs +++ b/src/coreclr/tools/ILVerification/VerifierError.cs @@ -190,5 +190,6 @@ public enum VerifierError InterfaceImplHasDuplicate, // InterfaceImpl has a duplicate InterfaceMethodNotImplemented, // Class implements interface but not method LocallocStackNotEmpty, // localloc requires that stack must be empty, except for 'size' argument + InvalidBaseType, // Type has an invalid base type. } } diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il index 54bc08c49083b3..418bbc0c048b2f 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/ILTestAssembly/PreserveBaseOverridesAttibuteTesting.il @@ -1272,4 +1272,4 @@ ret } } -*/ \ No newline at end of file +*/