Skip to content

Commit 384dceb

Browse files
Generic attributes handling in CustomAttributeDecoder (#73705)
* 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]>
1 parent 7cd6a16 commit 384dceb

File tree

3 files changed

+503
-19
lines changed

3 files changed

+503
-19
lines changed

src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/CustomAttributeDecoder.cs

Lines changed: 150 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, Meta
2222
public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHandle value)
2323
{
2424
BlobHandle signature;
25+
BlobHandle attributeOwningTypeSpec = default;
2526
switch (constructor.Kind)
2627
{
2728
case HandleKind.MethodDefinition:
@@ -32,6 +33,13 @@ public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHan
3233
case HandleKind.MemberReference:
3334
MemberReference reference = _reader.GetMemberReference((MemberReferenceHandle)constructor);
3435
signature = reference.Signature;
36+
37+
// If this is a generic attribute, we'll need its instantiation to decode the signatures
38+
if (reference.Parent.Kind == HandleKind.TypeSpecification)
39+
{
40+
TypeSpecification genericOwner = _reader.GetTypeSpecification((TypeSpecificationHandle)reference.Parent);
41+
attributeOwningTypeSpec = genericOwner.Signature;
42+
}
3543
break;
3644

3745
default:
@@ -60,12 +68,38 @@ public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHan
6068
throw new BadImageFormatException();
6169
}
6270

63-
ImmutableArray<CustomAttributeTypedArgument<TType>> fixedArguments = DecodeFixedArguments(ref signatureReader, ref valueReader, parameterCount);
71+
BlobReader genericContextReader = default;
72+
if (!attributeOwningTypeSpec.IsNil)
73+
{
74+
// If this is a generic attribute, grab the instantiation arguments so that we can
75+
// interpret the constructor signature, should it refer to the generic context.
76+
genericContextReader = _reader.GetBlobReader(attributeOwningTypeSpec);
77+
if (genericContextReader.ReadSignatureTypeCode() == SignatureTypeCode.GenericTypeInstance)
78+
{
79+
int kind = genericContextReader.ReadCompressedInteger();
80+
if (kind != (int)SignatureTypeKind.Class && kind != (int)SignatureTypeKind.ValueType)
81+
{
82+
throw new BadImageFormatException();
83+
}
84+
85+
genericContextReader.ReadTypeHandle();
86+
87+
// At this point, the reader points to the "GenArgCount Type Type*" part of the signature.
88+
}
89+
else
90+
{
91+
// Some other invalid TypeSpec. Don't accidentally allow resolving generic parameters
92+
// from the constructor signature into a broken blob.
93+
genericContextReader = default;
94+
}
95+
}
96+
97+
ImmutableArray<CustomAttributeTypedArgument<TType>> fixedArguments = DecodeFixedArguments(ref signatureReader, ref valueReader, parameterCount, genericContextReader);
6498
ImmutableArray<CustomAttributeNamedArgument<TType>> namedArguments = DecodeNamedArguments(ref valueReader);
6599
return new CustomAttributeValue<TType>(fixedArguments, namedArguments);
66100
}
67101

68-
private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments(ref BlobReader signatureReader, ref BlobReader valueReader, int count)
102+
private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments(ref BlobReader signatureReader, ref BlobReader valueReader, int count, BlobReader genericContextReader)
69103
{
70104
if (count == 0)
71105
{
@@ -76,7 +110,7 @@ private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments
76110

77111
for (int i = 0; i < count; i++)
78112
{
79-
ArgumentTypeInfo info = DecodeFixedArgumentType(ref signatureReader);
113+
ArgumentTypeInfo info = DecodeFixedArgumentType(ref signatureReader, genericContextReader);
80114
arguments.Add(DecodeArgument(ref valueReader, info));
81115
}
82116

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

@@ -170,12 +204,33 @@ private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader,
170204
throw new BadImageFormatException();
171205
}
172206

173-
var elementInfo = DecodeFixedArgumentType(ref signatureReader, isElementType: true);
207+
var elementInfo = DecodeFixedArgumentType(ref signatureReader, genericContextReader, isElementType: true);
174208
info.ElementType = elementInfo.Type;
175209
info.ElementTypeCode = elementInfo.TypeCode;
176210
info.Type = _provider.GetSZArrayType(info.ElementType);
177211
break;
178212

213+
case SignatureTypeCode.GenericTypeParameter:
214+
if (genericContextReader.Length == 0)
215+
{
216+
throw new BadImageFormatException();
217+
}
218+
219+
int parameterIndex = signatureReader.ReadCompressedInteger();
220+
int numGenericParameters = genericContextReader.ReadCompressedInteger();
221+
if (parameterIndex >= numGenericParameters)
222+
{
223+
throw new BadImageFormatException();
224+
}
225+
226+
while (parameterIndex > 0)
227+
{
228+
SkipType(ref genericContextReader);
229+
parameterIndex--;
230+
}
231+
232+
return DecodeFixedArgumentType(ref genericContextReader, default, isElementType);
233+
179234
default:
180235
throw new BadImageFormatException();
181236
}
@@ -363,5 +418,95 @@ private TType GetTypeFromHandle(EntityHandle handle) =>
363418
HandleKind.TypeReference => _provider.GetTypeFromReference(_reader, (TypeReferenceHandle)handle, 0),
364419
_ => throw new BadImageFormatException(SR.NotTypeDefOrRefHandle),
365420
};
421+
422+
private static void SkipType(ref BlobReader blobReader)
423+
{
424+
int typeCode = blobReader.ReadCompressedInteger();
425+
426+
switch (typeCode)
427+
{
428+
case (int)SignatureTypeCode.Boolean:
429+
case (int)SignatureTypeCode.Char:
430+
case (int)SignatureTypeCode.SByte:
431+
case (int)SignatureTypeCode.Byte:
432+
case (int)SignatureTypeCode.Int16:
433+
case (int)SignatureTypeCode.UInt16:
434+
case (int)SignatureTypeCode.Int32:
435+
case (int)SignatureTypeCode.UInt32:
436+
case (int)SignatureTypeCode.Int64:
437+
case (int)SignatureTypeCode.UInt64:
438+
case (int)SignatureTypeCode.Single:
439+
case (int)SignatureTypeCode.Double:
440+
case (int)SignatureTypeCode.IntPtr:
441+
case (int)SignatureTypeCode.UIntPtr:
442+
case (int)SignatureTypeCode.Object:
443+
case (int)SignatureTypeCode.String:
444+
case (int)SignatureTypeCode.Void:
445+
case (int)SignatureTypeCode.TypedReference:
446+
return;
447+
448+
case (int)SignatureTypeCode.Pointer:
449+
case (int)SignatureTypeCode.ByReference:
450+
case (int)SignatureTypeCode.Pinned:
451+
case (int)SignatureTypeCode.SZArray:
452+
SkipType(ref blobReader);
453+
return;
454+
455+
case (int)SignatureTypeCode.FunctionPointer:
456+
SignatureHeader header = blobReader.ReadSignatureHeader();
457+
if (header.IsGeneric)
458+
{
459+
blobReader.ReadCompressedInteger(); // arity
460+
}
461+
462+
int paramCount = blobReader.ReadCompressedInteger();
463+
SkipType(ref blobReader);
464+
for (int i = 0; i < paramCount; i++)
465+
SkipType(ref blobReader);
466+
return;
467+
468+
case (int)SignatureTypeCode.Array:
469+
SkipType(ref blobReader);
470+
blobReader.ReadCompressedInteger(); // rank
471+
int boundsCount = blobReader.ReadCompressedInteger();
472+
for (int i = 0; i < boundsCount; i++)
473+
{
474+
blobReader.ReadCompressedInteger();
475+
}
476+
int lowerBoundsCount = blobReader.ReadCompressedInteger();
477+
for (int i = 0; i < lowerBoundsCount; i++)
478+
{
479+
blobReader.ReadCompressedSignedInteger();
480+
}
481+
return;
482+
483+
case (int)SignatureTypeCode.RequiredModifier:
484+
case (int)SignatureTypeCode.OptionalModifier:
485+
blobReader.ReadTypeHandle();
486+
SkipType(ref blobReader);
487+
return;
488+
489+
case (int)SignatureTypeCode.GenericTypeInstance:
490+
SkipType(ref blobReader);
491+
int count = blobReader.ReadCompressedInteger();
492+
for (int i = 0; i < count; i++)
493+
{
494+
SkipType(ref blobReader);
495+
}
496+
return;
497+
498+
case (int)SignatureTypeCode.GenericTypeParameter:
499+
blobReader.ReadCompressedInteger();
500+
return;
501+
502+
case (int)SignatureTypeKind.Class:
503+
case (int)SignatureTypeKind.ValueType:
504+
SkipType(ref blobReader);
505+
break;
506+
507+
default:
508+
throw new BadImageFormatException();
509+
}
510+
}
366511
}
367512
}

0 commit comments

Comments
 (0)