From bceb331c5117172b85ebcf15e17be76c1f45a7a9 Mon Sep 17 00:00:00 2001 From: Dredsen Date: Sun, 10 May 2026 18:13:50 -0400 Subject: [PATCH 1/5] Some IL2CPP metadata contains properties whose getter and setter have both been stripped completely. Il2CppPropertyDefinition.PropertyType / RawPropertyType derived their result solely from the accessor signatures and dereferenced the setter with null-forgiving operators, NREing when both were null. Reproduced on Relic Arena (Unity 6000.3.14f1) where ZstdSharp.Unsafe.Methods contains a bad property, crashing CopyPropertiesInType and failing afterwards. - Il2CppPropertyDefinition: PropertyType, RawPropertyType, and IsStatic now return null / false instead of NRE when both accessors are missing. - AsmResolverAssemblyPopulator.CopyPropertiesInType: skip properties with no accessors - AsmResolverAssemblyPopulator.PopulateCustomAttributes: skip properties that were skipped above --- .../AsmResolverAssemblyPopulator.cs | 12 +++++++++- .../Metadata/Il2CppPropertyDefinition.cs | 24 ++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs index 37843b43..95c91ed8 100644 --- a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs +++ b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs @@ -278,7 +278,13 @@ public static void PopulateCustomAttributes(AssemblyAnalysisContext asmContext) CopyCustomAttributes(field, field.GetExtraData("AsmResolverField")!.CustomAttributes); foreach (var property in type.Properties) - CopyCustomAttributes(property, property.GetExtraData("AsmResolverProperty")!.CustomAttributes); + { + // Property may have been skipped in CopyPropertiesInType (e.g. badly stripped metadata can lead to + // properties with no accessors). Skip custom attribute copy too. + var propertyDef = property.GetExtraData("AsmResolverProperty"); + if (propertyDef == null) continue; + CopyCustomAttributes(property, propertyDef.CustomAttributes); + } foreach (var eventDefinition in type.Events) CopyCustomAttributes(eventDefinition, eventDefinition.GetExtraData("AsmResolverEvent")!.CustomAttributes); @@ -443,6 +449,10 @@ private static void CopyPropertiesInType(ReferenceImporter importer, TypeAnalysi { foreach (var propertyCtx in typeContext.Properties) { + // Skip bad properties with neither getter nor setter — their type can't be resolved. + if (propertyCtx.Getter == null && propertyCtx.Setter == null) + continue; + var propertyTypeSig = propertyCtx.ToTypeSignature(importer.TargetModule); var propertySignature = propertyCtx.IsStatic ? PropertySignature.CreateStatic(propertyTypeSig) diff --git a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs index 2d1b598d..bf6fb4b3 100644 --- a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs @@ -39,11 +39,29 @@ public Il2CppTypeDefinition? DeclaringType public Il2CppMethodDefinition? Setter => LibCpp2IlMain.TheMetadata == null || set.IsNull || DeclaringType == null ? null : LibCpp2IlMain.TheMetadata.GetMethodDefinitionFromIndex(DeclaringType.FirstMethodIdx + set); - public Il2CppTypeReflectionData? PropertyType => LibCpp2IlMain.TheMetadata == null ? null : Getter == null ? Setter!.Parameters![0].Type : Getter!.ReturnType; + public Il2CppTypeReflectionData? PropertyType + { + get + { + if (LibCpp2IlMain.TheMetadata == null) return null; + if (Getter != null) return Getter.ReturnType; + if (Setter != null && Setter.Parameters is { Length: > 0 }) return Setter.Parameters[0].Type; + return null; + } + } - public Il2CppType? RawPropertyType => LibCpp2IlMain.TheMetadata == null ? null : Getter == null ? Setter!.Parameters![0].RawType : Getter!.RawReturnType; + public Il2CppType? RawPropertyType + { + get + { + if (LibCpp2IlMain.TheMetadata == null) return null; + if (Getter != null) return Getter.RawReturnType; + if (Setter != null && Setter.Parameters is { Length: > 0 }) return Setter.Parameters[0].RawType; + return null; + } + } - public bool IsStatic => Getter == null ? Setter!.IsStatic : Getter!.IsStatic; + public bool IsStatic => Getter?.IsStatic ?? Setter?.IsStatic ?? false; public uint Token => token; public override void Read(ClassReadingBinaryReader reader) From 029ea4750ee1ec93e09ca7f72d3efb3ed1731902 Mon Sep 17 00:00:00 2001 From: Dreddy Date: Sun, 10 May 2026 18:49:26 -0400 Subject: [PATCH 2/5] Update LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs Co-authored-by: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> --- LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs index bf6fb4b3..2573b875 100644 --- a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs @@ -45,7 +45,7 @@ public Il2CppTypeReflectionData? PropertyType { if (LibCpp2IlMain.TheMetadata == null) return null; if (Getter != null) return Getter.ReturnType; - if (Setter != null && Setter.Parameters is { Length: > 0 }) return Setter.Parameters[0].Type; + if (Setter != null && Setter.Parameters is { Length: > 0 }) return Setter.Parameters[^1].Type; return null; } } From fdf888f8e4e1020a748414eeff328b3e227ffaf7 Mon Sep 17 00:00:00 2001 From: Dreddy Date: Sun, 10 May 2026 18:49:36 -0400 Subject: [PATCH 3/5] Update Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs Co-authored-by: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> --- Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs index 95c91ed8..a3cb658e 100644 --- a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs +++ b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs @@ -449,7 +449,7 @@ private static void CopyPropertiesInType(ReferenceImporter importer, TypeAnalysi { foreach (var propertyCtx in typeContext.Properties) { - // Skip bad properties with neither getter nor setter — their type can't be resolved. + // Skip properties whose getter and setter were both stripped. if (propertyCtx.Getter == null && propertyCtx.Setter == null) continue; From 0837584582eb50e981ccb6ec7d3199e4ef4a603e Mon Sep 17 00:00:00 2001 From: Dreddy Date: Sun, 10 May 2026 18:49:42 -0400 Subject: [PATCH 4/5] Update Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs Co-authored-by: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> --- Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs index a3cb658e..5accac44 100644 --- a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs +++ b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs @@ -279,8 +279,8 @@ public static void PopulateCustomAttributes(AssemblyAnalysisContext asmContext) foreach (var property in type.Properties) { - // Property may have been skipped in CopyPropertiesInType (e.g. badly stripped metadata can lead to - // properties with no accessors). Skip custom attribute copy too. + // Property with no accessors are skipped in CopyPropertiesInType. + // Skip copying custom attributes, too. var propertyDef = property.GetExtraData("AsmResolverProperty"); if (propertyDef == null) continue; CopyCustomAttributes(property, propertyDef.CustomAttributes); From 2bf5b745ce65c23458e8bd991a47c003a963b6c4 Mon Sep 17 00:00:00 2001 From: Dreddy Date: Sun, 10 May 2026 18:49:48 -0400 Subject: [PATCH 5/5] Update LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs Co-authored-by: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> --- LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs index 2573b875..3743ff0b 100644 --- a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs @@ -56,7 +56,7 @@ public Il2CppType? RawPropertyType { if (LibCpp2IlMain.TheMetadata == null) return null; if (Getter != null) return Getter.RawReturnType; - if (Setter != null && Setter.Parameters is { Length: > 0 }) return Setter.Parameters[0].RawType; + if (Setter != null && Setter.Parameters is { Length: > 0 }) return Setter.Parameters[^1].RawType; return null; } }