From 6354a91c6a12a24fb58933bf6cda936bcef2852e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 20 May 2024 11:55:48 +0200 Subject: [PATCH] try to handle the type and assembly name mangling --- .../src/Infos/ClassInfo.cs | 46 ++++++++++++++++--- .../src/Infos/MemberTypeInfo.cs | 6 +-- .../src/PayloadOptions.cs | 2 + .../src/Records/BinaryLibraryRecord.cs | 6 +-- .../src/Records/ClassRecord.cs | 4 +- .../src/Records/ClassWithIdRecord.cs | 2 +- .../Records/ClassWithMembersAndTypesRecord.cs | 6 +-- .../SystemClassWithMembersAndTypesRecord.cs | 4 +- .../src/Utils/BinaryReaderExtensions.cs | 11 ----- .../src/Utils/FormatterServices.cs | 3 ++ 10 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/ClassInfo.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/ClassInfo.cs index 640dd49e326d1..d756c7b85bfda 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/ClassInfo.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/ClassInfo.cs @@ -18,26 +18,29 @@ namespace System.Runtime.Serialization.BinaryFormat; /// /// /// -[DebuggerDisplay("{Name}")] +[DebuggerDisplay("{RawName}")] internal sealed class ClassInfo { - private ClassInfo(int objectId, TypeName name, Dictionary memberNames) + private ClassInfo(int objectId, string name, Dictionary memberNames, PayloadOptions payloadOptions) { ObjectId = objectId; - Name = name; + RawName = name; MemberNames = memberNames; + PayloadOptions = payloadOptions; } internal int ObjectId { get; } - internal TypeName Name { get; } + internal string RawName { get; } internal Dictionary MemberNames { get; } + internal PayloadOptions PayloadOptions { get; } + internal static ClassInfo Parse(BinaryReader reader, PayloadOptions payloadOptions) { int objectId = reader.ReadInt32(); - TypeName typeName = reader.ReadTypeName(payloadOptions); + string typeName = reader.ReadString(); int memberCount = reader.ReadInt32(); // The attackers could create an input with MANY member names. @@ -55,6 +58,37 @@ internal static ClassInfo Parse(BinaryReader reader, PayloadOptions payloadOptio memberNames.Add(reader.ReadString(), i); } - return new(objectId, typeName, memberNames); + return new(objectId, typeName, memberNames, payloadOptions); + } + + internal TypeName GetTypeNameEvenIfMangled(string libraryName) + { + if (TypeName.TryParse(RawName.AsSpan(), out TypeName? typeName, PayloadOptions.TypeNameParseOptions)) + { + if (typeName.AssemblyName is not null) + { + throw new SerializationException("Type names must not contain assembly names"); + } + } + else if (!PayloadOptions.SupportMangledNames) + { + throw new SerializationException($"Invalid type name: '{RawName}'"); + } + + // adsitnik: use array pool to avoid allocations (if it turns out to be the right direction) + string assemblyQualifiedName = $"{RawName}, {libraryName}"; + + if (!TypeName.TryParse(assemblyQualifiedName.AsSpan(), out typeName, PayloadOptions.TypeNameParseOptions)) + { + throw new SerializationException($"Invalid type name: '{RawName}' or library name: '{libraryName}'"); + } + + if (typeName.AssemblyName is null) + { + typeName = typeName.WithAssemblyName(FormatterServices.CoreLibRawName); + } + + Debug.Assert(typeName.AssemblyName is not null); + return typeName; } } diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/MemberTypeInfo.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/MemberTypeInfo.cs index a52aa4041cafd..0675dd9e29b3e 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/MemberTypeInfo.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Infos/MemberTypeInfo.cs @@ -172,7 +172,7 @@ internal bool IsElementType(Type typeElement, RecordMap recordMap) BinaryLibraryRecord libraryRecord = (BinaryLibraryRecord)recordMap[typeInfo.LibraryId]; string assemblyName = FormatterServices.GetAssemblyNameIncludingTypeForwards(typeElement); - return assemblyName == libraryRecord.LibraryName.FullName; + return assemblyName == libraryRecord.LibraryName; default: throw new NotSupportedException(); } @@ -252,8 +252,8 @@ internal TypeName GetElementTypeName(RecordMap recordMap) return ((TypeName)additionalInfo!).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName); case BinaryType.Class: ClassTypeInfo typeInfo = (ClassTypeInfo)additionalInfo!; - AssemblyNameInfo libraryName = ((BinaryLibraryRecord)recordMap[typeInfo.LibraryId]).LibraryName; - return typeInfo.TypeName.WithAssemblyName(libraryName.FullName); + string libraryName = ((BinaryLibraryRecord)recordMap[typeInfo.LibraryId]).LibraryName; + return typeInfo.TypeName.WithAssemblyName(libraryName); default: throw new NotSupportedException(); } diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/PayloadOptions.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/PayloadOptions.cs index 0371715f7fb3e..20e2db490ddf0 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/PayloadOptions.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/PayloadOptions.cs @@ -15,4 +15,6 @@ sealed class PayloadOptions public PayloadOptions() { } public TypeNameParseOptions? TypeNameParseOptions { get; set; } + + public bool SupportMangledNames { get; set; } = true; // adsitnik set to false and propagate } diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/BinaryLibraryRecord.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/BinaryLibraryRecord.cs index f0627254b5b00..752d6d5479418 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/BinaryLibraryRecord.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/BinaryLibraryRecord.cs @@ -18,7 +18,7 @@ namespace System.Runtime.Serialization.BinaryFormat; /// internal sealed class BinaryLibraryRecord : SerializationRecord { - private BinaryLibraryRecord(int libraryId, AssemblyNameInfo libraryName) + private BinaryLibraryRecord(int libraryId, string libraryName) { ObjectId = libraryId; LibraryName = libraryName; @@ -26,10 +26,10 @@ private BinaryLibraryRecord(int libraryId, AssemblyNameInfo libraryName) public override RecordType RecordType => RecordType.BinaryLibrary; - internal AssemblyNameInfo LibraryName { get; } + internal string LibraryName { get; } public override int ObjectId { get; } internal static BinaryLibraryRecord Parse(BinaryReader reader) - => new(reader.ReadInt32(), reader.ReadLibraryName()); + => new(reader.ReadInt32(), reader.ReadString()); } diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassRecord.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassRecord.cs index 86daf5e02d602..07717dde03d27 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassRecord.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassRecord.cs @@ -35,9 +35,9 @@ private protected ClassRecord(ClassInfo classInfo) MemberValues = []; } - public TypeName TypeName => _typeName ??= ClassInfo.Name.WithAssemblyName(LibraryName.FullName); + public TypeName TypeName => _typeName ??= ClassInfo.GetTypeNameEvenIfMangled(LibraryName); - internal abstract AssemblyNameInfo LibraryName { get; } + internal abstract string LibraryName { get; } // Currently we don't expose raw values, so we are not preserving the order here. public IEnumerable MemberNames => ClassInfo.MemberNames.Keys; diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithIdRecord.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithIdRecord.cs index 42bfff0248438..2aae4b8f0f7fe 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithIdRecord.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithIdRecord.cs @@ -26,7 +26,7 @@ private ClassWithIdRecord(int objectId, ClassRecord metadataClass) : base(metada public override RecordType RecordType => RecordType.ClassWithId; - internal override AssemblyNameInfo LibraryName => MetadataClass.LibraryName; + internal override string LibraryName => MetadataClass.LibraryName; public override int ObjectId { get; } diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithMembersAndTypesRecord.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithMembersAndTypesRecord.cs index 137e5ff551198..be3ded7e889e5 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithMembersAndTypesRecord.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/ClassWithMembersAndTypesRecord.cs @@ -27,7 +27,7 @@ private ClassWithMembersAndTypesRecord(ClassInfo classInfo, BinaryLibraryRecord public override RecordType RecordType => RecordType.ClassWithMembersAndTypes; - internal override AssemblyNameInfo LibraryName => Library.LibraryName; + internal override string LibraryName => Library.LibraryName; internal BinaryLibraryRecord Library { get; } @@ -36,8 +36,8 @@ private ClassWithMembersAndTypesRecord(ClassInfo classInfo, BinaryLibraryRecord internal override int ExpectedValuesCount => MemberTypeInfo.Infos.Count; public override bool IsTypeNameMatching(Type type) - => FormatterServices.GetTypeFullNameIncludingTypeForwards(type) == ClassInfo.Name.FullName - && FormatterServices.GetAssemblyNameIncludingTypeForwards(type) == Library.LibraryName.FullName; + => FormatterServices.GetTypeFullNameIncludingTypeForwards(type) == TypeName.FullName + && FormatterServices.GetAssemblyNameIncludingTypeForwards(type) == Library.LibraryName; internal static ClassWithMembersAndTypesRecord Parse(BinaryReader reader, RecordMap recordMap, PayloadOptions options) { diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/SystemClassWithMembersAndTypesRecord.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/SystemClassWithMembersAndTypesRecord.cs index 8de99ecd1e709..79e91877af5f3 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/SystemClassWithMembersAndTypesRecord.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Records/SystemClassWithMembersAndTypesRecord.cs @@ -16,7 +16,7 @@ private SystemClassWithMembersAndTypesRecord(ClassInfo classInfo, MemberTypeInfo public override RecordType RecordType => RecordType.SystemClassWithMembersAndTypes; - internal override AssemblyNameInfo LibraryName => FormatterServices.CoreLibAssemblyName; + internal override string LibraryName => FormatterServices.CoreLibRawName; internal MemberTypeInfo MemberTypeInfo { get; } @@ -24,7 +24,7 @@ private SystemClassWithMembersAndTypesRecord(ClassInfo classInfo, MemberTypeInfo public override bool IsTypeNameMatching(Type type) => type.Assembly == typeof(object).Assembly - && FormatterServices.GetTypeFullNameIncludingTypeForwards(type) == ClassInfo.Name.FullName; + && FormatterServices.GetTypeFullNameIncludingTypeForwards(type) == TypeName.FullName; internal static SystemClassWithMembersAndTypesRecord Parse(BinaryReader reader, PayloadOptions options) { diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/BinaryReaderExtensions.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/BinaryReaderExtensions.cs index be9cb83c37631..6b196d88497a4 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/BinaryReaderExtensions.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/BinaryReaderExtensions.cs @@ -77,15 +77,4 @@ internal static TypeName ReadTypeName(this BinaryReader binaryReader, PayloadOpt return typeName; } - - internal static AssemblyNameInfo ReadLibraryName(this BinaryReader binaryReader) - { - string name = binaryReader.ReadString(); - if (!AssemblyNameInfo.TryParse(name.AsSpan(), out AssemblyNameInfo? libraryName)) - { - throw new SerializationException($"Invalid library name: '{name}'"); - } - - return libraryName; - } } diff --git a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/FormatterServices.cs b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/FormatterServices.cs index 3ae7ad3f16440..e36e3efdbe16a 100644 --- a/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/FormatterServices.cs +++ b/src/libraries/System.Runtime.Serialization.BinaryFormat/src/Utils/FormatterServices.cs @@ -10,9 +10,12 @@ namespace System.Runtime.Serialization.BinaryFormat; internal static class FormatterServices { private static AssemblyNameInfo? s_coreLibAssemblyName; + private static string? s_coreLibRawName; internal static AssemblyNameInfo CoreLibAssemblyName => s_coreLibAssemblyName ??= AssemblyNameInfo.Parse(GetAssemblyNameIncludingTypeForwards(typeof(object)).AsSpan()); + internal static string CoreLibRawName => s_coreLibRawName ??= CoreLibAssemblyName.FullName; + internal static string GetAssemblyNameIncludingTypeForwards(Type type) { // Special case types like arrays