From 2fbe96dc9234484aafe7329a68183ed4a678bc85 Mon Sep 17 00:00:00 2001 From: Washi Date: Thu, 26 Jan 2023 21:17:26 +0100 Subject: [PATCH 1/2] Add CA parameter count validation logic. --- .../DotNetDirectoryBuffer.CodedIndices.cs | 5 +++ .../Signatures/CustomAttributeSignature.cs | 37 +++++++++++++++++++ .../SerializedCustomAttributeSignature.cs | 6 +++ .../CustomAttributeTest.cs | 20 ++++++++++ 4 files changed, 68 insertions(+) diff --git a/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.CodedIndices.cs b/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.CodedIndices.cs index 2aeee551a..52e6c3df8 100644 --- a/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.CodedIndices.cs +++ b/src/AsmResolver.DotNet/Builder/DotNetDirectoryBuffer.CodedIndices.cs @@ -17,6 +17,11 @@ private void AddCustomAttribute(MetadataToken ownerToken, CustomAttribute attrib { var table = Metadata.TablesStream.GetSortedTable(TableIndex.CustomAttribute); + // Ensure the signature defines the right parameters w.r.t. the constructor's parameters. + if (attribute.Constructor is not null && attribute.Signature is not null) + attribute.Signature.IsCompatibleWith(attribute.Constructor, ErrorListener); + + // Add it. var encoder = Metadata.TablesStream.GetIndexEncoder(CodedIndex.HasCustomAttribute); var row = new CustomAttributeRow( encoder.EncodeToken(ownerToken), diff --git a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs index eaa877fdb..79bc6a216 100644 --- a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs @@ -156,6 +156,43 @@ protected override void WriteContents(in BlobSerializationContext context) for (int i = 0; i < NamedArguments.Count; i++) NamedArguments[i].Write(context); } + + /// + /// Validates whether the signature is compatible with the provided attribute constructor. + /// + /// The constructor to validate against. + /// true if the constructor is compatible, false otherwise. + public bool IsCompatibleWith(ICustomAttributeType constructor) + { + return IsCompatibleWith(constructor, EmptyErrorListener.Instance); + } + + /// + /// Validates whether the signature is compatible with the provided attribute constructor. + /// + /// The constructor to validate against. + /// The object responsible for reporting any errors during the validation of the signature. + /// true if the constructor is compatible, false otherwise. + public virtual bool IsCompatibleWith(ICustomAttributeType constructor, IErrorListener listener) + { + var signature = constructor.Signature; + + int expectedCount = signature?.ParameterTypes.Count ?? 0; + if (expectedCount != FixedArguments.Count) + { + listener.MetadataBuilder( + $"{constructor.SafeToString()} expects {expectedCount} arguments but the signature provided {FixedArguments.Count} arguments."); + return false; + } + + if (signature?.SentinelParameterTypes.Count > 0) + { + listener.MetadataBuilder($"{constructor.SafeToString()} defines sentinel parameters."); + return false; + } + + return true; + } } } diff --git a/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs b/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs index 4dfadd299..2b2138205 100644 --- a/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/SerializedCustomAttributeSignature.cs @@ -75,5 +75,11 @@ protected override void WriteContents(in BlobSerializationContext context) else _reader.Fork().WriteToOutput(context.Writer); } + + /// + public override bool IsCompatibleWith(ICustomAttributeType constructor, IErrorListener listener) + { + return !IsInitialized || base.IsCompatibleWith(constructor, listener); + } } } diff --git a/test/AsmResolver.DotNet.Tests/CustomAttributeTest.cs b/test/AsmResolver.DotNet.Tests/CustomAttributeTest.cs index b93e25f7e..ae0e5db79 100644 --- a/test/AsmResolver.DotNet.Tests/CustomAttributeTest.cs +++ b/test/AsmResolver.DotNet.Tests/CustomAttributeTest.cs @@ -646,5 +646,25 @@ public void NamedGenericTypeNullArgument(bool rebuild, bool access) Assert.Null(argument.Argument.Element); } + + [Fact] + public void TestSignatureCompatibility() + { + var module = new ModuleDefinition("Dummy"); + var factory = module.CorLibTypeFactory; + var ctor = factory.CorLibScope + .CreateTypeReference("System", "CLSCompliantAttribute") + .CreateMemberReference(".ctor", MethodSignature.CreateInstance(factory.Void, factory.Boolean)) + .ImportWith(module.DefaultImporter); + + var attribute = new CustomAttribute(ctor); + + // Empty signature is not compatible with a ctor that takes a boolean. + Assert.False(attribute.Signature!.IsCompatibleWith(attribute.Constructor!)); + + // If we add it, it should be compatible again. + attribute.Signature.FixedArguments.Add(new CustomAttributeArgument(factory.Boolean, true)); + Assert.True(attribute.Signature!.IsCompatibleWith(attribute.Constructor!)); + } } } From dc4ffd9f587b6531dfbd5a68177c8bbd14eb421c Mon Sep 17 00:00:00 2001 From: Washi Date: Thu, 26 Jan 2023 21:39:32 +0100 Subject: [PATCH 2/2] Clarify CA errors are coming from the CA constructor validation. --- src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs index 79bc6a216..f382851b4 100644 --- a/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/CustomAttributeSignature.cs @@ -181,13 +181,13 @@ public virtual bool IsCompatibleWith(ICustomAttributeType constructor, IErrorLis if (expectedCount != FixedArguments.Count) { listener.MetadataBuilder( - $"{constructor.SafeToString()} expects {expectedCount} arguments but the signature provided {FixedArguments.Count} arguments."); + $"Custom attribute constructor {constructor.SafeToString()} expects {expectedCount} arguments but the signature provided {FixedArguments.Count} arguments."); return false; } if (signature?.SentinelParameterTypes.Count > 0) { - listener.MetadataBuilder($"{constructor.SafeToString()} defines sentinel parameters."); + listener.MetadataBuilder($"Custom attribute constructor {constructor.SafeToString()} defines sentinel parameters."); return false; }