From 2406781007fe9231a5745320b075d3f8035da99c Mon Sep 17 00:00:00 2001 From: Pkuyo Date: Fri, 4 Sep 2026 19:21:44 -0700 Subject: [PATCH 1/3] Fix ILVerify validation of static virtual method delegates --- .../ILTests/PrefixTests.il | 41 +++++++++++++++++++ .../tools/ILVerification/ILImporter.Verify.cs | 6 ++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/ILVerification.Tests/ILTests/PrefixTests.il b/src/coreclr/tools/ILVerification.Tests/ILTests/PrefixTests.il index ad4dc32c49e399..e816e039b4cd0d 100644 --- a/src/coreclr/tools/ILVerification.Tests/ILTests/PrefixTests.il +++ b/src/coreclr/tools/ILVerification.Tests/ILTests/PrefixTests.il @@ -14,6 +14,11 @@ .method public hidebysig newslot abstract virtual static void StaticAbstractMethod() cil managed { } + + .method public hidebysig newslot virtual static void StaticVirtualMethod() cil managed + { + ret + } } .class public sequential ansi sealed beforefieldinit ImplStruct @@ -120,6 +125,42 @@ ret } + .method static public hidebysig void ConstrainedLdftn.StaticAbstractDelegateOnTypeParam_Valid<(IStaticInterface) T>() cil managed + { + ldnull + constrained. !!T + ldftn void IStaticInterface::StaticAbstractMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + pop + ret + } + + .method static public hidebysig void ConstrainedLdftn.StaticVirtualDelegateOnTypeParam_Valid<(IStaticInterface) T>() cil managed + { + ldnull + constrained. !!T + ldftn void IStaticInterface::StaticVirtualMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + pop + ret + } + + .method static public hidebysig void Ldftn.StaticVirtualDelegateWithoutConstrained_Valid() cil managed + { + ldnull + ldftn void IStaticInterface::StaticVirtualMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + pop + ret + } + + .method static public hidebysig void Ldftn.StaticAbstractWithoutConstrained_Invalid_CallAbstract() cil managed + { + ldftn void IStaticInterface::StaticAbstractMethod() + pop + ret + } + .method static public hidebysig void Readonly.Ldelema_Valid() cil managed { .locals init (int32[] V_0) diff --git a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs index 94800ee022344a..e32ae3ebe7ecc0 100644 --- a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs +++ b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs @@ -1110,7 +1110,7 @@ void CheckDelegateCreation(StackValue ftn, StackValue obj) else { // See "Rules for non-virtual call to a non-final virtual method" in ImportCall - if (ftn.Method.IsVirtual && !ftn.Method.IsFinal && !obj.IsBoxedValueType) + if (!ftn.Method.Signature.IsStatic && ftn.Method.IsVirtual && !ftn.Method.IsFinal && !obj.IsBoxedValueType) { var methodTypeDef = ftn.Method.OwningType.GetTypeDefinition() as MetadataType; // Method is always considered final if owning type is sealed if (methodTypeDef == null || !methodTypeDef.IsSealed) @@ -1844,6 +1844,10 @@ void ImportLdFtn(int token, ILOpcode opCode) if (!_constrained.CanCastTo(method.OwningType)) VerificationError(VerifierError.ConstrainedTypeNoInterfaceImpl, _constrained, method.OwningType); } + else + { + Check(!method.IsAbstract, VerifierError.CallAbstract); + } } else if (opCode == ILOpcode.ldvirtftn) { From 93d9b035d560bce80832efef26e8d71a355b976f Mon Sep 17 00:00:00 2001 From: Pkuyo Date: Sun, 6 Sep 2026 20:01:49 -0700 Subject: [PATCH 2/3] Separate static method check in delegate verification --- src/coreclr/tools/ILVerification/ILImporter.Verify.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs index e32ae3ebe7ecc0..38390aebda2c2c 100644 --- a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs +++ b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs @@ -1108,9 +1108,10 @@ void CheckDelegateCreation(StackValue ftn, StackValue obj) return; } else + if (!ftn.Method.Signature.IsStatic) { // See "Rules for non-virtual call to a non-final virtual method" in ImportCall - if (!ftn.Method.Signature.IsStatic && ftn.Method.IsVirtual && !ftn.Method.IsFinal && !obj.IsBoxedValueType) + if (ftn.Method.IsVirtual && !ftn.Method.IsFinal && !obj.IsBoxedValueType) { var methodTypeDef = ftn.Method.OwningType.GetTypeDefinition() as MetadataType; // Method is always considered final if owning type is sealed if (methodTypeDef == null || !methodTypeDef.IsSealed) From 0e42ef792e08edbc785239aeaaec85b99f3bed64 Mon Sep 17 00:00:00 2001 From: Pkuyo Date: Sun, 6 Sep 2026 20:08:59 -0700 Subject: [PATCH 3/3] delete superfluous else Co-authored-by: Jan Kotas --- src/coreclr/tools/ILVerification/ILImporter.Verify.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs index 38390aebda2c2c..48383bf24ccd86 100644 --- a/src/coreclr/tools/ILVerification/ILImporter.Verify.cs +++ b/src/coreclr/tools/ILVerification/ILImporter.Verify.cs @@ -1107,7 +1107,6 @@ void CheckDelegateCreation(StackValue ftn, StackValue obj) VerificationError(VerifierError.DelegatePattern); return; } - else if (!ftn.Method.Signature.IsStatic) { // See "Rules for non-virtual call to a non-final virtual method" in ImportCall