Repository navigation
NativeAOT: reflection on static abstract interface methods crashes in .NET 11 (10.0 throws NotSupportedException) #135565
Description
Description
On NativeAOT in .NET 11, reflection on a static abstract interface member calls through a null entry point. MethodInfo.Invoke, MethodInvoker.Invoke and PropertyInfo.GetValue crash the process with an access violation, a delegate from MethodInfo.CreateDelegate crashes when called, and RuntimeMethodHandle.GetFunctionPointer returns zero. On 10.0 all of these throw NotSupportedException. The change coincides with #125875, which made static virtual methods reflection-invokable on NativeAOT.
Reproduction Steps
using System;
using System.Reflection;
Run("MethodInfo.Invoke, ISortable.Sort", () => typeof(ISortable).GetMethod("Sort")!.Invoke(null, null));
Run("MethodInfo.Invoke, ISortable<int>.Sort(int)", () => typeof(ISortable<int>).GetMethod("Sort")!.Invoke(null, new object[] { 1 }));
Run("MethodInvoker.Invoke, ISortable.Sort", () => MethodInvoker.Create(typeof(ISortable).GetMethod("Sort")!).Invoke(null));
Run("PropertyInfo.GetValue, ICounted.Count", () => typeof(ICounted).GetProperty("Count")!.GetValue(null));
Run("CreateDelegate, ISortable.Sort, then call", () => typeof(ISortable).GetMethod("Sort")!.CreateDelegate<Action>()());
Run("GetFunctionPointer, ISortable.Sort", () => Console.WriteLine($"0x{typeof(ISortable).GetMethod("Sort")!.MethodHandle.GetFunctionPointer():X}"));
Run("MethodInfo.Invoke, IDefault.Sort (static virtual with a body)", () => typeof(IDefault).GetMethod("Sort")!.Invoke(null, null));
static void Run(string label, Action action)
{
try
{
action();
Console.WriteLine($"{label}: no exception");
}
catch (Exception e)
{
Console.WriteLine($"{label}: {e.GetType()}: {e.Message}");
}
}
public interface ISortable { static abstract void Sort(); }
public interface ISortable<T> { static abstract void Sort(T value); }
public interface ICounted { static abstract int Count { get; } }
public interface IDefault { static virtual void Sort() { } }<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<Nullable>enable</Nullable>
<AssemblyName>Repro</AssemblyName>
</PropertyGroup>
</Project>dotnet publish -c Release -r win-x64 -p:PublishAot=true
bin\Release\net11.0\win-x64\publish\Repro.exe
Expected behavior
The six calls on static abstract members throw a managed exception; the last call prints no exception.
Actual behavior
The process prints nothing and exits with 0xC0000005 (access violation). Run one at a time, the four invoke calls and the delegate call each exit with 0xC0000005, GetFunctionPointer prints 0x0, and the static virtual call with a body prints no exception.
Regression?
Yes, from 10.0. The same program published with SDK 10.0.401 (net10.0, ILCompiler 10.0.12) prints System.NotSupportedException: This object cannot be invoked because no code was generated for it: 'ISortable.Sort()'. for the first call, the same exception for every other call, the static virtual one included, and exits 0.
PR #125875 (ac11e33, in release/11.0, not in release/10.0) removed this exclusion from MetadataManager.IsMethodSupportedInReflectionInvoke:
// TODO: Reflection invoking static virtual methods
if (method.IsVirtual && method.Signature.IsStatic)
return false;Static abstract methods now get invoke map entries, but the compiler only sets HasEntrypoint for non-abstract methods:
ReflectionInvokeMapNode.cs:164-165
if (!method.IsAbstract)
flags |= InvokeTableFlags.HasEntrypoint;Known Workarounds
Not checked.
Configuration
.NET SDK 11.0.100-rc.1.26420.103 with ILCompiler 11.0.0-rc.1.26420.103, Windows 11 x64, win-x64. For the 10.0 comparison, SDK 10.0.401 with ILCompiler 10.0.12.
Other information
Proposed fix: leave static abstract methods out of the reflection invoke map (MetadataManager.ShouldMethodBeInInvokeMap), keeping static virtual methods with a body invokable. That restores the 10.0 behaviour for all six calls, is a small compiler-only change, and suits a backport to release/11.0.
Options not taken:
- Excluding static abstract methods in
IsMethodSupportedInReflectionInvokeagain. That method also drives dependency and dataflow analysis, so twoILCompiler.Trimming.Testscases that Fix COMDelegate::BindToMethod with static virtuals #125875 updated fail:StaticInterfaceMethodDataflow(ImplIFoo.AbstractMethod()is no longer kept) andVirtualMethodHierarchyDataflowAnnotationValidation(IL2092, IL2093 and IL2095 warnings are missing). - A NativeAOT runtime invoker for static abstract methods that throws
MemberAccessException, the exception I'll propose for Incorrect error message when trying to dynamically invoke a static abstract interface method #101320 (PR to follow). It depends on that change, so it belongs with it rather than in a backportable regression fix.
I have a fix with a test ready and will open a PR for it shortly. Could this be assigned to me?
Note
AI-generated, written at my direction and reviewed by me before posting.
Verified on Windows 11 x64: the program above published with dotnet publish -c Release -r win-x64 -p:PublishAot=true using SDK 11.0.100-rc.1.26420.103 (exit 0xC0000005, no output) and, with net10.0, SDK 10.0.401 (the NotSupportedException output); the one-at-a-time results are from a copy of the program that runs only the call named on its command line. The commit's effect is from git show ac11e33bf5d and git merge-base --is-ancestor against origin/release/11.0 and origin/release/10.0; the code excerpts are from dotnet/runtime 2eb7113245f7c536ab876dca5e3533fb96c81bbf and from the parent of ac11e33. The proposed fix's result is the program above published with a compiler built locally from 2eb7113245f plus the fix; the trimming test failures are ILCompiler.Trimming.Tests run on the same commit with the first option not taken.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
dotnet-policy-service commentedon Oct 10, 2026
Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.