dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.41k stars 4.76k forks source link

Delegate constructor is reported as public by reflection API #110022

Closed sergey-rybalkin closed 1 day ago

sergey-rybalkin commented 1 day ago

Description

Using reflection API Type.GetConstructors() on delegate-based types like Action<...> and Func<...> is a bit confusing. They don't have public constructors with the actual construction logic implemented inside the runtime. However, a call to the typeof(Action).GetConstructors() returns an array with single ConstructorInfo element that has IsPublic property set to true.

Reproduction Steps

Type targetType = typeof(Action);
ConstructorInfo[] allConstructors = targetType.GetConstructors();
ConstructorInfo[] publicConstructors = targetType.GetConstructors(BindingFlags.Public);

Console.WriteLine($"Public constructors: {publicConstructors.Length}");
Console.WriteLine($"Constructor found. IsPublic: {allConstructors[0].IsPublic}; IsPrivate: {allConstructors[0].IsPrivate}");

Expected behavior

Type targetType = typeof(Action);
ConstructorInfo[] allConstructors = targetType.GetConstructors();
ConstructorInfo[] publicConstructors = targetType.GetConstructors(BindingFlags.Public);

Console.WriteLine($"Public constructors: {publicConstructors.Length}");
Console.WriteLine($"Constructor found. IsPublic: {allConstructors[0].IsPublic}; IsPrivate: {allConstructors[0].IsPrivate}");

Output:

Public constructors: 0
Constructor found. IsPublic: False; IsPrivate: True

Actual behavior

Type targetType = typeof(Action);
ConstructorInfo[] allConstructors = targetType.GetConstructors();
ConstructorInfo[] publicConstructors = targetType.GetConstructors(BindingFlags.Public);

Console.WriteLine($"Public constructors: {publicConstructors.Length}");
Console.WriteLine($"Constructor found. IsPublic: {allConstructors[0].IsPublic}; IsPrivate: {allConstructors[0].IsPrivate}");

Output:

Public constructors: 0
Constructor found. IsPublic: True; IsPrivate: False

Regression?

No response

Known Workarounds

No response

Configuration

.NET SDK: Version: 9.0.100 Commit: 59db016f11 Workload version: 9.0.100-manifests.c6f19616 MSBuild version: 17.12.7+5b8665660

Runtime Environment: OS Name: Windows OS Version: 10.0.22631 OS Platform: Windows RID: win-x64

Other information

Not sure whether this is the correct behavior, but it definitely causes problems in libraries that try to generate fake data using reflection API. Delegate constructor appears as public and rather safe to call with some randomly generated IntPtr's which causes the whole application to crash with ExecutionEngineException. See related issue in AutoBogus library.

dotnet-policy-service[bot] commented 1 day ago

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

MichalPetryka commented 1 day ago

Delegate constructors are public and the compiler emits a call to them on every delegate creation, on CoreCLR you can call themselves with reflection too but only with valid data (on NativeAOT that's not supported since they're pattern matched in IL).

But in general trying to pass random inputs to various APIs and expecting them to either work or throw is a totally invalid assumption, there are many APIs in the BCL that will crash the process on invalid inputs (examples being NativeMemory.Free or other APIs taking pointers).

MichalStrehovsky commented 1 day ago

This is by design, they do have a public constructor. Randomly constructing objects with bogus data is a recipe for a disaster, there are plenty of other types that are going to cause trouble, e.g. SafeHandle is going to close a random handle.