Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.Reflection.CustomAttributeFormatException thrown on a retrieval of derived attribute with overridden property getter #110412

Open
SENya1990 opened this issue Dec 4, 2024 · 3 comments
Labels
area-System.Reflection bug help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@SENya1990
Copy link

SENya1990 commented Dec 4, 2024

Description

Hi .Net team,

I have recently encountered a strange behavior of the derived attributes which looks like a bug to me.
On an attempt to retrieve a derived attribute that overrides just a property getter of the base attribute the system throws the System.Reflection.CustomAttributeFormatException exception (see details in the Actual Behavior section):

I didn't manage to find a similar bug, and the same time there is no compiler error about it. At the same time this looks like a bug to me. I provided more reasons in the expected behavior.

If there already exists such bug, or this is not a bug but a designed behavior, then I ask sorry for the disturbance in advance.
If this is not a proper place to report this issue, please direct me to a proper one.

Reproduction Steps

Here is the minimal code snippet:

using System;

namespace Test
{
	public class BaseAttribute : Attribute
	{
		public virtual int P
                {
	             get;
	             set;
                }
	}

	public class DerivedAttribute : BaseAttribute
	{
		public override int P
		{
			get => 1;
		}
	}

	[Derived(P = 2)]
	public class Foo
	{ }

	public class Program
	{
		static void Main(string[] args)
		{
			var attr = typeof(Foo).GetCustomAttributes(inherit: true);   // this line throws the exception

			Console.WriteLine(attr.Length);
			Console.ReadLine();
		}
	}
}

Notice, that if I add an empty setter to the overridden property, then the code works as expected without an error:

public class DerivedAttribute : BaseAttribute
{
	public override int P
	{
		get => 1;
                set { }
	}
}

Expected behavior

The code works without throwing an error.

At first I thought that this is a problem of the C# compiler, that it doesn't display an error for this case. But for C# compiler it is perfectly fine to have only one property accessor overridden. This is discussed in this SO question: https://stackoverflow.com/questions/64595478/why-is-there-no-compiler-error-when-setting-an-overridden-property-that-is-read

Moreover, the following code works fine:

using System;

namespace Test
{
	public class BaseAttribute : Attribute
	{
		public virtual int P
		{
			get;
			set;
		}
	}
	public class DerivedAttribute : BaseAttribute
	{
		public override int P
		{
			get => 1;
		}
	}

	[Derived(P = 2)]
	public class Foo
	{ }

	public class Program
	{
		static void Main(string[] args)
		{
			var attr = new DerivedAttribute();
			attr.P = 1;

			Console.WriteLine(attr.P);
			Console.ReadLine();
		}
	}
}

So, my guess is that something is wrong with attributes.

Actual behavior

The line var attr = typeof(Foo).GetCustomAttributes(inherit: true); throws the following exception:

System.Reflection.CustomAttributeFormatException
  HResult=0x80131605
  Message='P' property specified was not found.
  Source=System.Private.CoreLib
  StackTrace:
   at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes) in /_/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs:line 1272

  This exception was originally thrown at this call stack:
    System.Reflection.CustomAttribute.AddCustomAttributes(ref System.RuntimeType.ListBuilder<object>, System.Reflection.RuntimeModule, int, System.RuntimeType, bool, System.RuntimeType.ListBuilder<object>) in RuntimeCustomAttributeData.cs

Inner Exception 1:
NullReferenceException: Object reference not set to an instance of an object.

Regression?

On my machine this issue reproduces both for .Net Framework 4.8.1 and .Net 8.

Known Workarounds

Just add an empty property setter to the overridden property.

Configuration

Configuration:
.Net Framework 4.8.1 or .Net 8.

OS: Windows 10 Pro 64 bit, x64-based processor, Version 22H2, OS build 19045.5131
Architecture: AnyCpu

I don't know for sure, if this is specific to this configuration, but it doesn't look like this due to NRE thrown in the .Net code which can be debugged.
This isn't related to Blazor, just a simple console app.

Other information

Simple debug shows that the real error is an NRE thrown in the System.Reflection.CustomAttribute type in the AddCustomAttributes method in the following line:

The NRE is caused because setMethod is not checked for null before the access to its IsPublic property.
My guess is that the previous line RuntimeMethodInfo setMethod = property.GetSetMethod(true)!; obtains the property setter method only from the current type.

I believe, that a fix that just checks the setMethod for null and skips the property wouldn't make a good fix. In my example, the property has a public setter in a base type. Nothing prevents the property value from being set when the attribute is applied. In my opinion, the proper fix should support my scenario by making a lookup for the setter in the type hierarchy of the type containing the property.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Dec 4, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-reflection
See info in area-owners.md if you want to be subscribed.

@steveharter
Copy link
Member

Verified; the LOC does not check to see if the setter is null:

@steveharter steveharter added bug and removed untriaged New issue has not been triaged by the area owner labels Dec 5, 2024
@steveharter steveharter added this to the 10.0.0 milestone Dec 5, 2024
@steveharter
Copy link
Member

Not a regression.

@steveharter steveharter added the help wanted [up-for-grabs] Good issue for external contributors label Dec 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Reflection bug help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

No branches or pull requests

2 participants