Skip to content
Open
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
20 changes: 20 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,9 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle)
TypeSpecification typeSpecification = _metadataReader.GetTypeSpecification(handle);

BlobReader signatureReader = _metadataReader.GetBlobReader(typeSpecification.Signature);

ValidateTypeSpecificationSignature(signatureReader);

EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure);

TypeDesc parsedType = parser.ParseType();
Expand All @@ -579,6 +582,23 @@ private object ResolveTypeSpecification(TypeSpecificationHandle handle)
return parsedType;
}

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();
}

private object ResolveMemberReference(MemberReferenceHandle handle)
{
MemberReference memberReference = _metadataReader.GetMemberReference(handle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@

.method public hidebysig instance void Call.GenericMethodWithPrivateNestedClass_Invalid_MethodAccess() cil managed
{
call void class SimpleClass::GenericMethod<class SimpleClass/PrivateNestedClass>()
call void SimpleClass::GenericMethod<class SimpleClass/PrivateNestedClass>()
ret
}

Expand Down
72 changes: 72 additions & 0 deletions src/coreclr/tools/ILVerification.Tests/ILTests/BaseTypeTests.il
Original file line number Diff line number Diff line change
@@ -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<T>
extends [System.Runtime]System.Object
{
}

.class public auto ansi beforefieldinit GenericClassTypeSpecBase_ValidType_Valid
extends class GenericBase`1<int32>
{
}

.class public auto ansi beforefieldinit GenericOpenClassTypeSpecBase_ValidType_Valid`1<T>
extends class GenericBase`1<!0>
{
}

.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<T>
extends [System.Runtime]System.ValueType
{
}

.class public auto ansi beforefieldinit GenericValueTypeSpecBase_InvalidType_InvalidBaseType
extends valuetype GenericValueTypeBase`1<int32>
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(MSBuildProjectName).il" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@

ldloca.s V_0
ldstr "Hello"
stobj string
stobj [System.Runtime]System.String
ret
}

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/tools/ILVerification/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -453,4 +453,7 @@
<data name="LocallocStackNotEmpty" xml:space="preserve">
<value>Stack must be empty before localloc, except for the size item.</value>
</data>
<data name="InvalidBaseType" xml:space="preserve">
<value>Type '{0}' has invalid base type '{1}'.</value>
</data>
</root>
102 changes: 96 additions & 6 deletions src/coreclr/tools/ILVerification/TypeVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,79 @@ 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 if (baseType.Kind == HandleKind.TypeSpecification)
{
if (!IsValidBaseTypeSpecification((TypeSpecificationHandle)baseType))
{
VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType));
return false;
}

}
else if (_module.GetType(baseType).IsValueType)
{
VerificationError(VerifierError.InvalidBaseType, Format(type), Format(baseType));
return false;
}

return true;
}

private bool IsValidBaseTypeSpecification(TypeSpecificationHandle typeSpecificationHandle)
{
try
{
TypeDesc baseType = _module.GetType(typeSpecificationHandle);

// 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 (baseType is not InstantiatedType)
{
return false;
}

if (baseType.IsValueType)
{
return false;
}

return true;
}
catch (BadImageFormatException)
{
return false;
}
catch (TypeSystemException)
{
return false;
}
}

public void VerifyInterfaces()
{
TypeDefinition typeDefinition = _module.MetadataReader.GetTypeDefinition(_typeDefinitionHandle);
Expand Down Expand Up @@ -115,17 +185,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 Format(_module.GetType(handle));
}
catch (BadImageFormatException)
{
return type.ToString();
}
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)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/tools/ILVerification/VerifierError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
}
Loading