Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 035305b

Browse files
authored
Make AsnSerializer resiliant to ILC reflection rules
AsnSerializer had two different paths which checked for ordered fields, one which used the MetadataToken and one which didn't. The MetadataToken method is more reliable, but is not available on ILC. With this change * MetadataToken is used when available, and we hope for the best otherwise. * The two paths now use the same resolution, so future algorithms apply evenly * The results are cached, so we only sort once per type
1 parent 6fb1371 commit 035305b

File tree

1 file changed

+54
-15
lines changed

1 file changed

+54
-15
lines changed

src/Common/src/System/Security/Cryptography/Asn1V2.Serializer.cs

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Buffers;
6+
using System.Collections.Concurrent;
67
using System.Collections.Generic;
78
using System.Diagnostics;
89
using System.Linq;
910
using System.Numerics;
1011
using System.Reflection;
12+
using System.Runtime.CompilerServices;
1113

1214
namespace System.Security.Cryptography.Asn1
1315
{
@@ -59,6 +61,9 @@ internal static class AsnSerializer
5961
private delegate object Deserializer(AsnReader reader);
6062
private delegate bool TryDeserializer<T>(AsnReader reader, out T value);
6163

64+
private static readonly ConcurrentDictionary<Type, FieldInfo[]> s_orderedFields =
65+
new ConcurrentDictionary<Type, FieldInfo[]>();
66+
6267
private static Deserializer TryOrFail<T>(TryDeserializer<T> tryDeserializer)
6368
{
6469
return reader =>
@@ -70,6 +75,53 @@ private static Deserializer TryOrFail<T>(TryDeserializer<T> tryDeserializer)
7075
};
7176
}
7277

78+
private static FieldInfo[] GetOrderedFields(Type typeT)
79+
{
80+
return s_orderedFields.GetOrAdd(
81+
typeT,
82+
t =>
83+
{
84+
// https://github.com/dotnet/corefx/issues/14606 asserts that ordering by the metadata
85+
// token on a SequentialLayout will produce the fields in their layout order.
86+
//
87+
// Some other alternatives:
88+
// * Add an attribute for controlling the field read order.
89+
// fieldInfos.Select(fi => (fi, fi.GetCustomAttribute<AsnFieldOrderAttribute>(false)).
90+
// Where(val => val.Item2 != null).OrderBy(val => val.Item2.OrderWeight).Select(val => val.Item1);
91+
//
92+
// * Use Marshal.OffsetOf as a sort key
93+
//
94+
// * Some sort of interface to return the fields in a declared order, using either
95+
// an existing object, or Activator.CreateInstance. It would need to check that
96+
// any returned fields actually were declared on the type that was queried.
97+
//
98+
// * Invent more alternatives
99+
FieldInfo[] fieldInfos = t.GetFields(FieldFlags);
100+
101+
if (fieldInfos.Length == 0)
102+
{
103+
return Array.Empty<FieldInfo>();
104+
}
105+
106+
try
107+
{
108+
int token = fieldInfos[0].MetadataToken;
109+
}
110+
catch (InvalidOperationException)
111+
{
112+
// If MetadataToken isn't available (like in ILC) then just hope that
113+
// the fields are returned in declared order. For the most part that
114+
// will result in data misaligning to fields and deserialization failing,
115+
// thus a CryptographicException.
116+
return fieldInfos;
117+
}
118+
119+
Array.Sort(fieldInfos, (x, y) => x.MetadataToken.CompareTo(y.MetadataToken));
120+
return fieldInfos;
121+
});
122+
}
123+
124+
73125
private static ChoiceAttribute GetChoiceAttribute(Type typeT)
74126
{
75127
ChoiceAttribute attr = typeT.GetCustomAttribute<ChoiceAttribute>(inherit: false);
@@ -102,20 +154,7 @@ private static void PopulateChoiceLookup(
102154
Type typeT,
103155
LinkedList<FieldInfo> currentSet)
104156
{
105-
FieldInfo[] fieldInfos = typeT.GetFields(FieldFlags);
106-
107-
// https://github.com/dotnet/corefx/issues/14606 asserts that ordering by the metadata
108-
// token on a SequentialLayout will produce the fields in their layout order.
109-
//
110-
// Some other alternatives:
111-
// * Add an attribute for controlling the field read order.
112-
// fieldInfos.Select(fi => (fi, fi.GetCustomAttribute<AsnFieldOrderAttribute>(false)).
113-
// Where(val => val.Item2 != null).OrderBy(val => val.Item2.OrderWeight).Select(val => val.Item1);
114-
//
115-
// * Use Marshal.OffsetOf as a sort key
116-
//
117-
// * Invent more alternatives
118-
foreach (FieldInfo fieldInfo in fieldInfos.OrderBy(fi => fi.MetadataToken))
157+
foreach (FieldInfo fieldInfo in GetOrderedFields(typeT))
119158
{
120159
Type fieldType = fieldInfo.FieldType;
121160

@@ -211,7 +250,7 @@ private static void SerializeChoice(Type typeT, object value, AsnWriter writer)
211250
}
212251
else
213252
{
214-
FieldInfo[] fieldInfos = typeT.GetFields(FieldFlags);
253+
FieldInfo[] fieldInfos = GetOrderedFields(typeT);
215254

216255
for (int i = 0; i < fieldInfos.Length; i++)
217256
{

0 commit comments

Comments
 (0)