Skip to content

Commit

Permalink
Generic attributes handling in CustomAttributeDecoder (#73705)
Browse files Browse the repository at this point in the history
* Add handling of generic attributes to CustomAttributeDecoder

If the attribute constructor refers to the generic T in its signature, we would throw `BadImageFormatException`. This adds handling by capturing the generic context and interpreting the signature variables when needed.

* Add tests

Co-authored-by: Michal Strehovský <[email protected]>
  • Loading branch information
buyaa-n and MichalStrehovsky authored Aug 11, 2022
1 parent 7cd6a16 commit 384dceb
Show file tree
Hide file tree
Showing 3 changed files with 503 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, Meta
public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHandle value)
{
BlobHandle signature;
BlobHandle attributeOwningTypeSpec = default;
switch (constructor.Kind)
{
case HandleKind.MethodDefinition:
Expand All @@ -32,6 +33,13 @@ public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHan
case HandleKind.MemberReference:
MemberReference reference = _reader.GetMemberReference((MemberReferenceHandle)constructor);
signature = reference.Signature;

// If this is a generic attribute, we'll need its instantiation to decode the signatures
if (reference.Parent.Kind == HandleKind.TypeSpecification)
{
TypeSpecification genericOwner = _reader.GetTypeSpecification((TypeSpecificationHandle)reference.Parent);
attributeOwningTypeSpec = genericOwner.Signature;
}
break;

default:
Expand Down Expand Up @@ -60,12 +68,38 @@ public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHan
throw new BadImageFormatException();
}

ImmutableArray<CustomAttributeTypedArgument<TType>> fixedArguments = DecodeFixedArguments(ref signatureReader, ref valueReader, parameterCount);
BlobReader genericContextReader = default;
if (!attributeOwningTypeSpec.IsNil)
{
// If this is a generic attribute, grab the instantiation arguments so that we can
// interpret the constructor signature, should it refer to the generic context.
genericContextReader = _reader.GetBlobReader(attributeOwningTypeSpec);
if (genericContextReader.ReadSignatureTypeCode() == SignatureTypeCode.GenericTypeInstance)
{
int kind = genericContextReader.ReadCompressedInteger();
if (kind != (int)SignatureTypeKind.Class && kind != (int)SignatureTypeKind.ValueType)
{
throw new BadImageFormatException();
}

genericContextReader.ReadTypeHandle();

// At this point, the reader points to the "GenArgCount Type Type*" part of the signature.
}
else
{
// Some other invalid TypeSpec. Don't accidentally allow resolving generic parameters
// from the constructor signature into a broken blob.
genericContextReader = default;
}
}

ImmutableArray<CustomAttributeTypedArgument<TType>> fixedArguments = DecodeFixedArguments(ref signatureReader, ref valueReader, parameterCount, genericContextReader);
ImmutableArray<CustomAttributeNamedArgument<TType>> namedArguments = DecodeNamedArguments(ref valueReader);
return new CustomAttributeValue<TType>(fixedArguments, namedArguments);
}

private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments(ref BlobReader signatureReader, ref BlobReader valueReader, int count)
private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments(ref BlobReader signatureReader, ref BlobReader valueReader, int count, BlobReader genericContextReader)
{
if (count == 0)
{
Expand All @@ -76,7 +110,7 @@ private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments

for (int i = 0; i < count; i++)
{
ArgumentTypeInfo info = DecodeFixedArgumentType(ref signatureReader);
ArgumentTypeInfo info = DecodeFixedArgumentType(ref signatureReader, genericContextReader);
arguments.Add(DecodeArgument(ref valueReader, info));
}

Expand Down Expand Up @@ -124,7 +158,7 @@ private struct ArgumentTypeInfo
// better perf-wise, but even more important is that we can't actually reason about
// a method signature with opaque TType values without adding some unnecessary chatter
// with the provider.
private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader, bool isElementType = false)
private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader, BlobReader genericContextReader, bool isElementType = false)
{
SignatureTypeCode signatureTypeCode = signatureReader.ReadSignatureTypeCode();

Expand Down Expand Up @@ -170,12 +204,33 @@ private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader,
throw new BadImageFormatException();
}

var elementInfo = DecodeFixedArgumentType(ref signatureReader, isElementType: true);
var elementInfo = DecodeFixedArgumentType(ref signatureReader, genericContextReader, isElementType: true);
info.ElementType = elementInfo.Type;
info.ElementTypeCode = elementInfo.TypeCode;
info.Type = _provider.GetSZArrayType(info.ElementType);
break;

case SignatureTypeCode.GenericTypeParameter:
if (genericContextReader.Length == 0)
{
throw new BadImageFormatException();
}

int parameterIndex = signatureReader.ReadCompressedInteger();
int numGenericParameters = genericContextReader.ReadCompressedInteger();
if (parameterIndex >= numGenericParameters)
{
throw new BadImageFormatException();
}

while (parameterIndex > 0)
{
SkipType(ref genericContextReader);
parameterIndex--;
}

return DecodeFixedArgumentType(ref genericContextReader, default, isElementType);

default:
throw new BadImageFormatException();
}
Expand Down Expand Up @@ -363,5 +418,95 @@ private TType GetTypeFromHandle(EntityHandle handle) =>
HandleKind.TypeReference => _provider.GetTypeFromReference(_reader, (TypeReferenceHandle)handle, 0),
_ => throw new BadImageFormatException(SR.NotTypeDefOrRefHandle),
};

private static void SkipType(ref BlobReader blobReader)
{
int typeCode = blobReader.ReadCompressedInteger();

switch (typeCode)
{
case (int)SignatureTypeCode.Boolean:
case (int)SignatureTypeCode.Char:
case (int)SignatureTypeCode.SByte:
case (int)SignatureTypeCode.Byte:
case (int)SignatureTypeCode.Int16:
case (int)SignatureTypeCode.UInt16:
case (int)SignatureTypeCode.Int32:
case (int)SignatureTypeCode.UInt32:
case (int)SignatureTypeCode.Int64:
case (int)SignatureTypeCode.UInt64:
case (int)SignatureTypeCode.Single:
case (int)SignatureTypeCode.Double:
case (int)SignatureTypeCode.IntPtr:
case (int)SignatureTypeCode.UIntPtr:
case (int)SignatureTypeCode.Object:
case (int)SignatureTypeCode.String:
case (int)SignatureTypeCode.Void:
case (int)SignatureTypeCode.TypedReference:
return;

case (int)SignatureTypeCode.Pointer:
case (int)SignatureTypeCode.ByReference:
case (int)SignatureTypeCode.Pinned:
case (int)SignatureTypeCode.SZArray:
SkipType(ref blobReader);
return;

case (int)SignatureTypeCode.FunctionPointer:
SignatureHeader header = blobReader.ReadSignatureHeader();
if (header.IsGeneric)
{
blobReader.ReadCompressedInteger(); // arity
}

int paramCount = blobReader.ReadCompressedInteger();
SkipType(ref blobReader);
for (int i = 0; i < paramCount; i++)
SkipType(ref blobReader);
return;

case (int)SignatureTypeCode.Array:
SkipType(ref blobReader);
blobReader.ReadCompressedInteger(); // rank
int boundsCount = blobReader.ReadCompressedInteger();
for (int i = 0; i < boundsCount; i++)
{
blobReader.ReadCompressedInteger();
}
int lowerBoundsCount = blobReader.ReadCompressedInteger();
for (int i = 0; i < lowerBoundsCount; i++)
{
blobReader.ReadCompressedSignedInteger();
}
return;

case (int)SignatureTypeCode.RequiredModifier:
case (int)SignatureTypeCode.OptionalModifier:
blobReader.ReadTypeHandle();
SkipType(ref blobReader);
return;

case (int)SignatureTypeCode.GenericTypeInstance:
SkipType(ref blobReader);
int count = blobReader.ReadCompressedInteger();
for (int i = 0; i < count; i++)
{
SkipType(ref blobReader);
}
return;

case (int)SignatureTypeCode.GenericTypeParameter:
blobReader.ReadCompressedInteger();
return;

case (int)SignatureTypeKind.Class:
case (int)SignatureTypeKind.ValueType:
SkipType(ref blobReader);
break;

default:
throw new BadImageFormatException();
}
}
}
}
Loading

0 comments on commit 384dceb

Please sign in to comment.