diff options
author | Samuel Arzt <arzt.samuel@live.de> | 2017-10-18 07:18:24 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2017-10-18 07:18:24 +0300 |
commit | 77022feb4c9c4a4c51ec2d4c72c7b036b8f1f918 (patch) | |
tree | 33217ba7a65bed9354e89209301db189edac12d4 /src/ILVerify | |
parent | 3523960fbcdd8ae85712a87a58ed4b38c0d08e10 (diff) |
[ILVerify] Implement delegate creation checks (#4733)
* Implemented verification of delegate creation.
* Added test cases for delegate creation pattern.
* Implemented delegate compatibility check.
* Added tests for delegate compatibility checks.
* Added checks for call to non-final virtual methods.
* Fixed using call on sealed type methods reporting this mismatch.
* Added tests for this mismatch verification.
* Added additional tests for modifying this pointer after base call.
Diffstat (limited to 'src/ILVerify')
-rw-r--r-- | src/ILVerify/src/ILImporter.StackValue.cs | 5 | ||||
-rw-r--r-- | src/ILVerify/src/ILImporter.Verify.cs | 300 | ||||
-rw-r--r-- | src/ILVerify/src/Resources/Strings.resx | 9 | ||||
-rw-r--r-- | src/ILVerify/src/VerifierError.cs | 308 | ||||
-rw-r--r-- | src/ILVerify/tests/ILTests/CallTests.il | 133 | ||||
-rw-r--r-- | src/ILVerify/tests/ILTests/CastingTests.il | 2 | ||||
-rw-r--r-- | src/ILVerify/tests/ILTests/FtnTests.il | 95 |
7 files changed, 649 insertions, 203 deletions
diff --git a/src/ILVerify/src/ILImporter.StackValue.cs b/src/ILVerify/src/ILImporter.StackValue.cs index f7ce9e1bc..30f4c7f32 100644 --- a/src/ILVerify/src/ILImporter.StackValue.cs +++ b/src/ILVerify/src/ILImporter.StackValue.cs @@ -76,6 +76,11 @@ namespace Internal.IL get { return Kind == StackValueKind.NativeInt && Method != null; } } + public bool IsBoxedValueType + { + get { return Kind == StackValueKind.ObjRef && Type.IsValueType; } + } + public StackValue DereferenceByRef() { Debug.Assert(Kind == StackValueKind.ByRef && Type != null, "Cannot dereference"); diff --git a/src/ILVerify/src/ILImporter.Verify.cs b/src/ILVerify/src/ILImporter.Verify.cs index c45ab1541..980f718c7 100644 --- a/src/ILVerify/src/ILImporter.Verify.cs +++ b/src/ILVerify/src/ILImporter.Verify.cs @@ -58,8 +58,11 @@ namespace Internal.IL int _stackTop = 0; bool _isThisInitialized; + bool _modifiesThisPtr; bool _trackObjCtorState; + int? _delegateCreateStart; + class ExceptionRegion { public ILExceptionRegion ILRegion; @@ -204,6 +207,7 @@ namespace Internal.IL FindBasicBlocks(); FindEnclosingExceptionRegions(); + FindThisPtrModification(); ImportBasicBlocks(); } @@ -270,6 +274,137 @@ namespace Internal.IL } } + private void FindThisPtrModification() + { + _modifiesThisPtr = false; + + if (_thisType == null) + return; // Early exit: no this pointer in this method + + _currentOffset = 0; + + while (_currentOffset < _ilBytes.Length) + { + ILOpcode opCode = (ILOpcode)ReadILByte(); + +again: + switch (opCode) + { + case ILOpcode.starg_s: + case ILOpcode.ldarga_s: + if (ReadILByte() == 0) + { + _modifiesThisPtr = true; + return; + } + break; + case ILOpcode.starg: + case ILOpcode.ldarga: + if (ReadILUInt16() == 0) + { + _modifiesThisPtr = true; + return; + } + break; + // Skip all other Opcodes + case ILOpcode.ldarg_s: + case ILOpcode.ldloc_s: + case ILOpcode.ldloca_s: + case ILOpcode.stloc_s: + case ILOpcode.ldc_i4_s: + case ILOpcode.unaligned: + case ILOpcode.br_s: + case ILOpcode.leave_s: + case ILOpcode.brfalse_s: + case ILOpcode.brtrue_s: + case ILOpcode.beq_s: + case ILOpcode.bge_s: + case ILOpcode.bgt_s: + case ILOpcode.ble_s: + case ILOpcode.blt_s: + case ILOpcode.bne_un_s: + case ILOpcode.bge_un_s: + case ILOpcode.bgt_un_s: + case ILOpcode.ble_un_s: + case ILOpcode.blt_un_s: + SkipIL(1); + break; + case ILOpcode.ldarg: + case ILOpcode.ldloc: + case ILOpcode.ldloca: + case ILOpcode.stloc: + SkipIL(2); + break; + case ILOpcode.ldc_i4: + case ILOpcode.ldc_r4: + case ILOpcode.jmp: + case ILOpcode.call: + case ILOpcode.calli: + case ILOpcode.callvirt: + case ILOpcode.cpobj: + case ILOpcode.ldobj: + case ILOpcode.ldstr: + case ILOpcode.newobj: + case ILOpcode.castclass: + case ILOpcode.isinst: + case ILOpcode.unbox: + case ILOpcode.ldfld: + case ILOpcode.ldflda: + case ILOpcode.stfld: + case ILOpcode.ldsfld: + case ILOpcode.ldsflda: + case ILOpcode.stsfld: + case ILOpcode.stobj: + case ILOpcode.box: + case ILOpcode.newarr: + case ILOpcode.ldelema: + case ILOpcode.ldelem: + case ILOpcode.stelem: + case ILOpcode.unbox_any: + case ILOpcode.refanyval: + case ILOpcode.mkrefany: + case ILOpcode.ldtoken: + case ILOpcode.ldftn: + case ILOpcode.ldvirtftn: + case ILOpcode.initobj: + case ILOpcode.constrained: + case ILOpcode.sizeof_: + case ILOpcode.br: + case ILOpcode.leave: + case ILOpcode.brfalse: + case ILOpcode.brtrue: + case ILOpcode.beq: + case ILOpcode.bge: + case ILOpcode.bgt: + case ILOpcode.ble: + case ILOpcode.blt: + case ILOpcode.bne_un: + case ILOpcode.bge_un: + case ILOpcode.bgt_un: + case ILOpcode.ble_un: + case ILOpcode.blt_un: + SkipIL(4); + break; + case ILOpcode.ldc_i8: + case ILOpcode.ldc_r8: + SkipIL(8); + break; + case ILOpcode.prefix1: + opCode = (ILOpcode)(0x100 + ReadILByte()); + goto again; + case ILOpcode.switch_: + { + uint count = ReadILUInt32(); + for (uint i = 0; i < count; i++) + SkipIL(4); + } + break; + default: + continue; + } + } + } + void AbortBasicBlockVerification() { throw new LocalVerificationException(); @@ -635,6 +770,87 @@ namespace Internal.IL } } + void CheckDelegateCreation(StackValue ftn, StackValue obj) + { + if (!_delegateCreateStart.HasValue) + { + VerificationError(VerifierError.DelegatePattern); + return; + } + + int delegateStart = _delegateCreateStart.Value; + + if (_currentInstructionOffset - delegateStart == 6) // ldftn <tok> takes 6 bytes + { + if (GetOpcodeAt(delegateStart) != ILOpcode.ldftn) + { + VerificationError(VerifierError.DelegatePattern); + return; + } + else + { + // See "Rules for non-virtual call to a non-final virtual method" in ImportCall + var owningTypeDef = (MetadataType)ftn.Method.OwningType.GetTypeDefinition(); + + if (ftn.Method.IsVirtual && !(ftn.Method.IsFinal || owningTypeDef.IsSealed) && !obj.IsBoxedValueType) + Check(obj.IsThisPtr && !_modifiesThisPtr, VerifierError.LdftnNonFinalVirtual); + } + } + else if (_currentInstructionOffset - _delegateCreateStart == 7) // dup, ldvirtftn <tok> takes 7 bytes + { + if (GetOpcodeAt(delegateStart) != ILOpcode.dup || + GetOpcodeAt(delegateStart + 1) != ILOpcode.ldvirtftn) + { + VerificationError(VerifierError.DelegatePattern); + return; + } + } + else + VerificationError(VerifierError.DelegatePattern); + } + + void CheckIsDelegateAssignable(MethodDesc ftn, TypeDesc delegateType) + { + if (!IsDelegateAssignable(ftn, delegateType)) + VerificationError(VerifierError.DelegateCtor); + } + + bool IsDelegateAssignable(MethodDesc ftn, TypeDesc delegateType) + { + var invokeMethod = delegateType.GetMethod("Invoke", null); + if (invokeMethod == null) + return false; + + var ftnSignature = ftn.Signature; + var delegateSignature = invokeMethod.Signature; + + // Compare calling convention ignoring distinction between static and instance + if ((ftnSignature.Flags & ~MethodSignatureFlags.Static) != (delegateSignature.Flags & ~MethodSignatureFlags.Static)) + return false; + + // Compare signature parameters + if (ftnSignature.Length != delegateSignature.Length) + return false; + + for (int i = 0; i < ftnSignature.Length; i++) + { + if (!IsAssignable(ftnSignature[i], delegateSignature[i])) + return false; + } + + // Compare return type + return IsAssignable(delegateSignature.ReturnType, ftnSignature.ReturnType); + } + + ILOpcode GetOpcodeAt(int instructionOffset) + { + var opCode = (ILOpcode)_ilBytes[instructionOffset]; + if (opCode == ILOpcode.prefix1) + opCode = (ILOpcode)(0x100 + _ilBytes[instructionOffset + 1]); + + return opCode; + } + void Unverifiable() { VerificationError(VerifierError.Unverifiable); @@ -714,6 +930,7 @@ namespace Internal.IL void StartImportingBasicBlock(BasicBlock basicBlock) { + _delegateCreateStart = null; _isThisInitialized = basicBlock.IsThisInitialized; if (basicBlock.TryStart) @@ -831,8 +1048,11 @@ namespace Internal.IL CheckIsNotPointer(varType); var stackValue = StackValue.CreateFromType(varType); - if (index == 0 && argument) + if (index == 0 && argument && _thisType != null) + { + Debug.Assert(varType == _thisType); stackValue.SetIsThisPtr(); + } Push(stackValue); } @@ -859,8 +1079,9 @@ namespace Internal.IL Check(!varType.IsByRef, VerifierError.ByrefOfByref); var stackValue = StackValue.CreateByRef(varType); - if (index == 0 && argument) + if (index == 0 && argument && _thisType != null) { + Debug.Assert(varType == _thisType); stackValue.SetIsThisPtr(); Check(!_trackObjCtorState || _isThisInitialized, VerifierError.ThisUninitStore); @@ -873,6 +1094,9 @@ namespace Internal.IL { var value = Pop(allowUninitThis: true); + // this could be the beginning of a delegate create + _delegateCreateStart = _currentInstructionOffset; + Push(value); Push(value); } @@ -964,20 +1188,9 @@ namespace Internal.IL CheckIsAssignable(actualObj, declaredObj); Check(actualObj.Kind == StackValueKind.ObjRef, VerifierError.DelegateCtorSigO, actualObj); -#if false - Verify(verCheckDelegateCreation(opcode, vstate, codeAddr, delegateMethodRef, - tiActualFtn, tiActualObj), - MVER_E_DLGT_PATTERN); - - Verify(m_jitInfo->isCompatibleDelegate(objTypeHandle, - parentTypeHandle, - tiActualFtn.GetMethod(), - methodClassHnd, - getCurrentModuleHandle(), - delegateMethodRef, - memberRef), - MVER_E_DLGT_CTOR); -#endif + CheckDelegateCreation(actualFtn, actualObj); + + CheckIsDelegateAssignable(actualFtn.Method, methodType); } else { @@ -1039,36 +1252,31 @@ namespace Internal.IL } CheckIsAssignable(actualThis, declaredThis); -#if false - // Rules for non-virtual call to a non-final virtual method: - - // Define: - // The "this" pointer is considered to be "possibly written" if - // 1. Its address have been taken (LDARGA 0) anywhere in the method. - // (or) - // 2. It has been stored to (STARG.0) anywhere in the method. - - // A non-virtual call to a non-final virtual method is only allowed if - // 1. The this pointer passed to the callee is an instance of a boxed value type. - // (or) - // 2. The this pointer passed to the callee is the current method's this pointer. - // (and) The current method's this pointer is not "possibly written". - - // Thus the rule is that if you assign to this ANYWHERE you can't make "base" calls to - // virtual methods. (Luckily this does affect .ctors, since they are not virtual). - // This is stronger that is strictly needed, but implementing a laxer rule is significantly - // hard and more error prone. - - if (opcode == ReaderBaseNS::CEE_CALL - && (mflags & CORINFO_FLG_VIRTUAL) - && ((mflags & CORINFO_FLG_FINAL) == 0) - && (!verIsBoxedValueType(tiThis))) + if (opcode == ILOpcode.call) { - // always enforce for peverify - Verify(tiThis.IsThisPtr() && !thisPossiblyModified, - MVER_E_THIS_MISMATCH); + // Rules for non-virtual call to a non-final virtual method (ECMA III.3.19: Verifiability of 'call'): + + // Define: + // The "this" pointer is considered to be "possibly written" if + // 1. Its address have been taken (LDARGA 0) anywhere in the method. + // (or) + // 2. It has been stored to (STARG.0) anywhere in the method. + + // A non-virtual call to a non-final virtual method is only allowed if + // 1. The this pointer passed to the callee is an instance of a boxed value type. + // (or) + // 2. The this pointer passed to the callee is the current method's this pointer. + // (and) The current method's this pointer is not "possibly written". + + // Thus the rule is that if you assign to this ANYWHERE you can't make "base" calls to + // virtual methods. (Luckily this does not affect .ctors, since they are not virtual). + // This is stronger than is strictly needed, but implementing a laxer rule is significantly + // harder and more error prone. + var methodTypeDef = (MetadataType)methodType.GetTypeDefinition(); // Method is always considered final if owning type is sealed + + if (method.IsVirtual && !(method.IsFinal || methodTypeDef.IsSealed) && !actualThis.IsBoxedValueType) + Check(actualThis.IsThisPtr && !_modifiesThisPtr, VerifierError.ThisMismatch); } -#endif if (tailCall) { @@ -1169,9 +1377,7 @@ namespace Internal.IL if (opCode == ILOpcode.ldftn) { -#if false - vstate->delegateCreateStart = codeAddr; -#endif + _delegateCreateStart = _currentInstructionOffset; } else if (opCode == ILOpcode.ldvirtftn) { diff --git a/src/ILVerify/src/Resources/Strings.resx b/src/ILVerify/src/Resources/Strings.resx index 065e2e5a1..868a917fb 100644 --- a/src/ILVerify/src/Resources/Strings.resx +++ b/src/ILVerify/src/Resources/Strings.resx @@ -168,6 +168,9 @@ <data name="DelegateCtorSigO" xml:space="preserve"> <value>Unrecognized delegate .ctor signature; expected Object.</value> </data> + <data name="DelegatePattern" xml:space="preserve"> + <value>Dup, ldvirtftn, newobj delegate::.ctor() pattern expected (in the same basic block).</value> + </data> <data name="Endfilter" xml:space="preserve"> <value>Endfilter from outside an exception filter block.</value> </data> @@ -210,6 +213,9 @@ <data name="LdftnConstructor" xml:space="preserve"> <value>ldftn/ldvirtftn not allowed on .ctor.</value> </data> + <data name="LdftnNonFinalVirtual" xml:space="preserve"> + <value>Cannot LDFTN a non-final virtual method for delegate creation if target object is potentially not the same type as the method class.</value> + </data> <data name="LdvirtftnOnStatic" xml:space="preserve"> <value>ldvirtftn on static.</value> </data> @@ -285,6 +291,9 @@ <data name="TailRetVoid" xml:space="preserve"> <value>Void ret type expected for tail call.</value> </data> + <data name="ThisMismatch" xml:space="preserve"> + <value>The 'this' parameter to the call must be the calling method's 'this' parameter.</value> + </data> <data name="ThisUninitReturn" xml:space="preserve"> <value>Return from .ctor when this is uninitialized.</value> </data> diff --git a/src/ILVerify/src/VerifierError.cs b/src/ILVerify/src/VerifierError.cs index 0c599c5f5..33862ef92 100644 --- a/src/ILVerify/src/VerifierError.cs +++ b/src/ILVerify/src/VerifierError.cs @@ -36,187 +36,187 @@ namespace ILVerify //E_STACK_TOO_LARGE "Stack is too large." //E_ARRAY_NAME_LONG "Array name is too long." - //E_FALLTHRU "fall through end of the method without returning." - //E_TRY_GTEQ_END "try start >= try end." - //E_TRYEND_GT_CS "try end > code size." - //E_HND_GTEQ_END "handler start >= handler end." - //E_HNDEND_GT_CS "handler end > code size." - //E_TRY_START "Try starts in the middle of an instruction." - //E_HND_START "Handler starts in the middle of an instruction." - //E_TRY_OVERLAP "Try block overlap with another block." - //E_TRY_EQ_HND_FIL "Try and filter/handler blocks are equivalent." - //E_TRY_SHARE_FIN_FAL "Shared try has finally or fault handler." - //E_HND_OVERLAP "Handler block overlaps with another block." - //E_HND_EQ "Handler block is the same as another block." - //E_FIL_OVERLAP "Filter block overlaps with another block." - //E_FIL_EQ "Filter block is the same as another block." - //E_FIL_CONT_TRY "Filter contains try." - //E_FIL_CONT_HND "Filter contains handler." - //E_FIL_CONT_FIL "Nested filters." - //E_FIL_GTEQ_CS "filter >= code size." - //E_FIL_START "Filter starts in the middle of an instruction." - //E_FALLTHRU_EXCEP "fallthru the end of an exception block." - //E_FALLTHRU_INTO_HND "fallthru into an exception handler." - //E_FALLTHRU_INTO_FIL "fallthru into an exception filter." - //E_LEAVE "Leave from outside a try or catch block." - Rethrow, //"Rethrow from outside a catch handler." - Endfinally, //"Endfinally from outside a finally handler." - Endfilter, //"Endfilter from outside an exception filter block." - //E_ENDFILTER_MISSING "Missing Endfilter." - BranchIntoTry, //"Branch into try block." - BranchIntoHandler, //"Branch into exception handler block." - BranchIntoFilter, //"Branch into exception filter block." - BranchOutOfTry, //"Branch out of try block." - BranchOutOfHandler, //"Branch out of exception handler block." - BranchOutOfFilter, //"Branch out of exception filter block." - //E_BR_OUTOF_FIN "Branch out of finally block." - ReturnFromTry, //"Return out of try block." - ReturnFromHandler, //"Return out of exception handler block." - ReturnFromFilter, //"Return out of exception filter block." - //E_BAD_JMP_TARGET "jmp / exception into the middle of an instruction." - //E_PATH_LOC "Non-compatible types depending on path." - //E_PATH_THIS "Init state for this differs depending on path." - PathStackUnexpected, //"Non-compatible types on stack depending on path." - PathStackDepth, //"Stack depth differs depending on path." - //E_THIS "Instance variable (this) missing." - //E_THIS_UNINIT_EXCEP "Uninitialized this on entering a try block." - ThisUninitStore, // Store into this when it is uninitialized. - ThisUninitReturn, // Return from .ctor when this is uninitialized. - //E_THIS_UNINIT_V_RET "Return from .ctor before all fields are initialized." - //E_THIS_UNINIT_BR "Branch back when this is uninitialized." - LdftnCtor, //"ldftn/ldvirtftn not allowed on .ctor." - //StackNotEq, // "Non-compatible types on the stack." + //E_FALLTHRU "fall through end of the method without returning." + //E_TRY_GTEQ_END "try start >= try end." + //E_TRYEND_GT_CS "try end > code size." + //E_HND_GTEQ_END "handler start >= handler end." + //E_HNDEND_GT_CS "handler end > code size." + //E_TRY_START "Try starts in the middle of an instruction." + //E_HND_START "Handler starts in the middle of an instruction." + //E_TRY_OVERLAP "Try block overlap with another block." + //E_TRY_EQ_HND_FIL "Try and filter/handler blocks are equivalent." + //E_TRY_SHARE_FIN_FAL "Shared try has finally or fault handler." + //E_HND_OVERLAP "Handler block overlaps with another block." + //E_HND_EQ "Handler block is the same as another block." + //E_FIL_OVERLAP "Filter block overlaps with another block." + //E_FIL_EQ "Filter block is the same as another block." + //E_FIL_CONT_TRY "Filter contains try." + //E_FIL_CONT_HND "Filter contains handler." + //E_FIL_CONT_FIL "Nested filters." + //E_FIL_GTEQ_CS "filter >= code size." + //E_FIL_START "Filter starts in the middle of an instruction." + //E_FALLTHRU_EXCEP "fallthru the end of an exception block." + //E_FALLTHRU_INTO_HND "fallthru into an exception handler." + //E_FALLTHRU_INTO_FIL "fallthru into an exception filter." + //E_LEAVE "Leave from outside a try or catch block." + Rethrow, // Rethrow from outside a catch handler. + Endfinally, // Endfinally from outside a finally handler. + Endfilter, // Endfilter from outside an exception filter block. + //E_ENDFILTER_MISSING "Missing Endfilter." + BranchIntoTry, // Branch into try block. + BranchIntoHandler, // Branch into exception handler block. + BranchIntoFilter, // Branch into exception filter block. + BranchOutOfTry, // Branch out of try block. + BranchOutOfHandler, // Branch out of exception handler block. + BranchOutOfFilter, // Branch out of exception filter block. + //E_BR_OUTOF_FIN "Branch out of finally block." + ReturnFromTry, // Return out of try block. + ReturnFromHandler, // Return out of exception handler block. + ReturnFromFilter, // Return out of exception filter block. + //E_BAD_JMP_TARGET "jmp / exception into the middle of an instruction." + //E_PATH_LOC "Non-compatible types depending on path." + //E_PATH_THIS "Init state for this differs depending on path." + PathStackUnexpected, // Non-compatible types on stack depending on path. + PathStackDepth, // Stack depth differs depending on path. + //E_THIS "Instance variable (this) missing." + //E_THIS_UNINIT_EXCEP "Uninitialized this on entering a try block." + ThisUninitStore, // Store into this when it is uninitialized. + ThisUninitReturn, // Return from .ctor when this is uninitialized. + //E_THIS_UNINIT_V_RET "Return from .ctor before all fields are initialized." + //E_THIS_UNINIT_BR "Branch back when this is uninitialized." + LdftnCtor, // ldftn/ldvirtftn not allowed on .ctor. + //StackNotEq, // Non-compatible types on the stack. StackUnexpected, // Unexpected type on the stack. StackUnexpectedArrayType, // Unexpected array type on the stack. - //E_STACK_EXCEPTION "Missing stack slot for exception." + //E_STACK_EXCEPTION "Missing stack slot for exception." StackOverflow, // Stack overflow. StackUnderflow, // Stack underflow. - //E_STACK_EMPTY "Stack empty." + //E_STACK_EMPTY "Stack empty." UninitStack, // Uninitialized item on stack. ExpectedIntegerType, // Expected I, I4, or I8 on the stack. ExpectedFloatType, // Expected R, R4, or R8 on the stack. - //E_STACK_NO_R_I8 "unexpected R, R4, R8, or I8 on the stack." + //E_STACK_NO_R_I8 "unexpected R, R4, R8, or I8 on the stack." ExpectedNumericType, // Expected numeric type on the stack. - StackObjRef, // "Expected an ObjRef on the stack." - //E_STACK_P_OBJREF "Expected address of an ObjRef on the stack." + StackObjRef, // Expected an ObjRef on the stack. + //E_STACK_P_OBJREF "Expected address of an ObjRef on the stack." StackByRef, // Expected ByRef on the stack. - StackMethod, // Expected pointer to function on the stack. - //E_STACK_ARRAY_SD "Expected single dimension array on the stack." - //E_STACK_VALCLASS "Expected value type instance on the stack." - //E_STACK_P_VALCLASS "Expected address of value type on the stack." - //E_STACK_NO_VALCLASS "Unexpected value type instance on the stack." - //E_LOC_DEAD "Local variable is unusable at this point." + StackMethod, // Expected pointer to function on the stack. + //E_STACK_ARRAY_SD "Expected single dimension array on the stack." + //E_STACK_VALCLASS "Expected value type instance on the stack." + //E_STACK_P_VALCLASS "Expected address of value type on the stack." + //E_STACK_NO_VALCLASS "Unexpected value type instance on the stack." + //E_LOC_DEAD "Local variable is unusable at this point." UnrecognizedLocalNumber, // Unrecognized local variable number. UnrecognizedArgumentNumber, // Unrecognized argument number. ExpectedTypeToken, // Expected type token. TokenResolve, // Unable to resolve token. - //E_TOKEN_TYPE "Unable to resolve type of the token." + //E_TOKEN_TYPE "Unable to resolve type of the token." ExpectedMethodToken, // Expected memberRef, memberDef or methodSpec token. - //E_TOKEN_TYPE_FIELD "Expected memberRef or fieldDef token." - //E_TOKEN_TYPE_SIG "Expected signature token." + //E_TOKEN_TYPE_FIELD "Expected memberRef or fieldDef token." + //E_TOKEN_TYPE_SIG "Expected signature token." ExpectedFieldToken, // Expected field token. - // E_TOKEN_TYPE_SIG "Expected signature token." + //E_TOKEN_TYPE_SIG "Expected signature token." Unverifiable, // Instruction can not be verified. StringOperand, // Operand does not point to a valid string ref. ReturnPtrToStack, // Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. ReturnVoid, // Stack must be empty on return from a void function. ReturnMissing, // Return value missing on the stack. ReturnEmpty, // Stack must contain only the return value. - //E_RET_UNINIT "Return uninitialized data." - //E_ARRAY_ACCESS "Illegal array access." - //E_ARRAY_V_STORE "Store non Object type into Object array." + //E_RET_UNINIT "Return uninitialized data." + //E_ARRAY_ACCESS "Illegal array access." + //E_ARRAY_V_STORE "Store non Object type into Object array." ExpectedArray, // Expected single-dimension zero-based array. - //E_ARRAY_SD_PTR "Expected single dimension array of pointer types." - //E_ARRAY_FIELD "Array field access is denied." - //E_ARGLIST "Allowed only in vararg methods." + //E_ARRAY_SD_PTR "Expected single dimension array of pointer types." + //E_ARRAY_FIELD "Array field access is denied." + //E_ARGLIST "Allowed only in vararg methods." ValueTypeExpected, // Value type expected. - //E_OPEN_DLGT_PROT_ACC "Protected method access through an open instance delegate is not verifiable." - //E_METHOD_ACCESS "Method is not visible." - //E_FIELD_ACCESS "Field is not visible." - //E_DEAD "Item is unusable at this point." + //E_OPEN_DLGT_PROT_ACC "Protected method access through an open instance delegate is not verifiable." + //E_METHOD_ACCESS "Method is not visible." + //E_FIELD_ACCESS "Field is not visible." + //E_DEAD "Item is unusable at this point." ExpectedStaticField, // Expected static field. - //E_FIELD_NO_STATIC "Expected non-static field." - //E_ADDR "Address of not allowed for this item." - //E_ADDR_BYREF "Address of not allowed for ByRef." - //E_ADDR_LITERAL "Address of not allowed for literal field." - //E_INITONLY "Cannot change initonly field outside its .ctor." - //E_WRITE_RVA_STATIC "Cannot modify an imaged based (RVA) static" - //E_THROW "Cannot throw this object." - CallVirtOnValueType, // Callvirt on a value type method. - //E_CALL_SIG "Call signature mismatch." - //E_CALL_STATIC "Static function expected." - //E_CTOR ".ctor expected." - //E_CTOR_VIRT "Cannot use callvirt on .ctor." - //E_CTOR_OR_SUPER "Only super::ctor or typeof(this)::ctor allowed here." - //E_CTOR_MUL_INIT "Possible call to .ctor more than once." - //E_SIG "Unrecognized signature." - //E_SIG_ARRAY "Cannot resolve Array type." - //E_SIG_ARRAY_PTR "Array of ELEMENT_TYPE_PTR." + //E_FIELD_NO_STATIC "Expected non-static field." + //E_ADDR "Address of not allowed for this item." + //E_ADDR_BYREF "Address of not allowed for ByRef." + //E_ADDR_LITERAL "Address of not allowed for literal field." + //E_INITONLY "Cannot change initonly field outside its .ctor." + //E_WRITE_RVA_STATIC "Cannot modify an imaged based (RVA) static" + //E_THROW "Cannot throw this object." + CallVirtOnValueType, // Callvirt on a value type method. + //E_CALL_SIG "Call signature mismatch." + //E_CALL_STATIC "Static function expected." + //E_CTOR ".ctor expected." + //E_CTOR_VIRT "Cannot use callvirt on .ctor." + //E_CTOR_OR_SUPER "Only super::ctor or typeof(this)::ctor allowed here." + //E_CTOR_MUL_INIT "Possible call to .ctor more than once." + //E_SIG "Unrecognized signature." + //E_SIG_ARRAY "Cannot resolve Array type." + //E_SIG_ARRAY_PTR "Array of ELEMENT_TYPE_PTR." ArrayByRef, // Array of ELEMENT_TYPE_BYREF or ELEMENT_TYPE_TYPEDBYREF. - //E_SIG_ELEM_PTR "ELEMENT_TYPE_PTR cannot be verified." - //E_SIG_VARARG "Unexpected vararg." - //E_SIG_VOID "Unexpected Void." + //E_SIG_ELEM_PTR "ELEMENT_TYPE_PTR cannot be verified." + //E_SIG_VARARG "Unexpected vararg." + //E_SIG_VOID "Unexpected Void." ByrefOfByref, // ByRef of ByRef. - //E_CODE_SIZE_ZERO "Code size is zero." - //E_BAD_VARARG "Unrecognized use of vararg." + //E_CODE_SIZE_ZERO "Code size is zero." + //E_BAD_VARARG "Unrecognized use of vararg." TailCall, // Missing call/callvirt/calli. TailByRef, // Cannot pass ByRef to a tail call. - //E_TAIL_RET "Missing ret." + //E_TAIL_RET "Missing ret." TailRetVoid, // Void ret type expected for tail call. - //E_TAIL_RET_TYPE "Tail call return type not compatible." + //E_TAIL_RET_TYPE "Tail call return type not compatible." TailStackEmpty, // Stack not empty after tail call. - //E_METHOD_END "Method ends in the middle of an instruction." - //E_BAD_BRANCH "Branch out of the method." - //E_FIN_OVERLAP "Finally handler blocks overlap." - //E_LEXICAL_NESTING "Lexical nesting." + //E_METHOD_END "Method ends in the middle of an instruction." + //E_BAD_BRANCH "Branch out of the method." + //E_FIN_OVERLAP "Finally handler blocks overlap." + //E_LEXICAL_NESTING "Lexical nesting." Volatile, // Missing ldsfld, stsfld, ldind, stind, ldfld, stfld, ldobj, stobj, initblk, or cpblk. Unaligned, // Missing ldind, stind, ldfld, stfld, ldobj, stobj, initblk, cpblk. - //E_INNERMOST_FIRST "Innermost exception blocks should be declared first." - //E_CALLI_VIRTUAL "Calli not allowed on virtual methods." - CallAbstract, // Call not allowed on abstract methods. - //E_NOT_IN_GC_HEAP "Value type with NotInGCHeap attribute being created on the GC heap." - TryNonEmptyStack, // Attempt to enter a try block with nonempty stack. - DelegateCtor, // Unrecognized arguments for delegate .ctor. - //E_DLGT_BB "Delegate .ctor not allowed at the start of a basic block when the function pointer argument is a virtual method." - //E_DLGT_PATTERN "Dup, ldvirtftn, newobj delegate::.ctor() pattern expected (in the same basic block)." - //E_DLGT_LDFTN "Ldftn or ldvirtftn instruction required before call to a delegate .ctor." - //E_FTN_ABSTRACT "Attempt to load address of an abstract method." - //E_SIG_C_VC "ELEMENT_TYPE_CLASS ValueClass in signature." - //E_SIG_VC_C "ELEMENT_TYPE_VALUETYPE non-ValueClass in signature." - //E_BOX_PTR_TO_STACK "Box operation on TypedReference, ArgHandle, or ArgIterator." - //E_SIG_BYREF_TB_AH "ByRef of TypedReference, ArgHandle, or ArgIterator." - //E_SIG_ARRAY_TB_AH "Array of TypedReference, ArgHandle, or ArgIterator." - EndfilterStack, //"Stack not empty when leaving an exception filter." - DelegateCtorSigI, // Unrecognized delegate .ctor signature; expected Native Int. - DelegateCtorSigO, // Unrecognized delegate .ctor signature; expected Object. - //E_RA_PTR_TO_STACK "Mkrefany on TypedReference, ArgHandle, or ArgIterator." - //E_CATCH_VALUE_TYPE "Value type not allowed as catch type." - //E_CATCH_BYREF "ByRef not allowed as catch type." - //E_FIL_PRECEED_HND "filter block should immediately precede handler block" - LdvirtftnOnStatic, // ldvirtftn on static. - CallVirtOnStatic, // callvirt on static. - InitLocals, // initlocals must be set for verifiable methods with one or more local variables. - //E_BR_TO_EXCEPTION "branch/leave to the beginning of a catch/filter handler" - CallCtor, // call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj. - + //E_INNERMOST_FIRST "Innermost exception blocks should be declared first." + //E_CALLI_VIRTUAL "Calli not allowed on virtual methods." + CallAbstract, // Call not allowed on abstract methods. + //E_NOT_IN_GC_HEAP "Value type with NotInGCHeap attribute being created on the GC heap." + TryNonEmptyStack, // Attempt to enter a try block with nonempty stack. + DelegateCtor, // Unrecognized arguments for delegate .ctor. + //E_DLGT_BB "Delegate .ctor not allowed at the start of a basic block when the function pointer argument is a virtual method." + DelegatePattern, // Dup, ldvirtftn, newobj delegate::.ctor() pattern expected (in the same basic block). + //E_DLGT_LDFTN "Ldftn or ldvirtftn instruction required before call to a delegate .ctor." + //E_FTN_ABSTRACT "Attempt to load address of an abstract method." + //E_SIG_C_VC "ELEMENT_TYPE_CLASS ValueClass in signature." + //E_SIG_VC_C "ELEMENT_TYPE_VALUETYPE non-ValueClass in signature." + //E_BOX_PTR_TO_STACK "Box operation on TypedReference, ArgHandle, or ArgIterator." + //E_SIG_BYREF_TB_AH "ByRef of TypedReference, ArgHandle, or ArgIterator." + //E_SIG_ARRAY_TB_AH "Array of TypedReference, ArgHandle, or ArgIterator." + EndfilterStack, // Stack not empty when leaving an exception filter. + DelegateCtorSigI, // Unrecognized delegate .ctor signature; expected Native Int. + DelegateCtorSigO, // Unrecognized delegate .ctor signature; expected Object. + //E_RA_PTR_TO_STACK "Mkrefany on TypedReference, ArgHandle, or ArgIterator." + //E_CATCH_VALUE_TYPE "Value type not allowed as catch type." + //E_CATCH_BYREF "ByRef not allowed as catch type." + //E_FIL_PRECEED_HND "filter block should immediately precede handler block" + LdvirtftnOnStatic, // ldvirtftn on static. + CallVirtOnStatic, // callvirt on static. + InitLocals, // initlocals must be set for verifiable methods with one or more local variables. + //E_BR_TO_EXCEPTION "branch/leave to the beginning of a catch/filter handler" + CallCtor, // call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj. + ////@GENERICSVER: new generics related error messages ExpectedValClassObjRefVariable, // Value type, ObjRef type or variable type expected. - //E_STACK_P_VALCLASS_OBJREF_VAR "Expected address of value type, ObjRef type or variable type on the stack." - //E_SIG_VAR_PARAM "Unrecognized type parameter of enclosing class." - //E_SIG_MVAR_PARAM "Unrecognized type parameter of enclosing method." - //E_SIG_VAR_ARG "Unrecognized type argument of referenced class instantiation." - //E_SIG_MVAR_ARG "Unrecognized type argument of referenced method instantiation." - //E_SIG_GENERICINST "Cannot resolve generic type." - //E_SIG_METHOD_INST "Method instantiation contains non boxable type arguments." - //E_SIG_METHOD_PARENT_INST "Method parent instantiation contains non boxable type arguments." - //E_SIG_FIELD_PARENT_INST "Field parent instantiation contains non boxable type arguments." - //E_CALLCONV_NOT_GENERICINST "Unrecognized calling convention for an instantiated generic method." - //E_TOKEN_BAD_METHOD_SPEC "Unrecognized generic method in method instantiation." + //E_STACK_P_VALCLASS_OBJREF_VAR "Expected address of value type, ObjRef type or variable type on the stack." + //E_SIG_VAR_PARAM "Unrecognized type parameter of enclosing class." + //E_SIG_MVAR_PARAM "Unrecognized type parameter of enclosing method." + //E_SIG_VAR_ARG "Unrecognized type argument of referenced class instantiation." + //E_SIG_MVAR_ARG "Unrecognized type argument of referenced method instantiation." + //E_SIG_GENERICINST "Cannot resolve generic type." + //E_SIG_METHOD_INST "Method instantiation contains non boxable type arguments." + //E_SIG_METHOD_PARENT_INST "Method parent instantiation contains non boxable type arguments." + //E_SIG_FIELD_PARENT_INST "Field parent instantiation contains non boxable type arguments." + //E_CALLCONV_NOT_GENERICINST "Unrecognized calling convention for an instantiated generic method." + //E_TOKEN_BAD_METHOD_SPEC "Unrecognized generic method in method instantiation." ReadOnly, // Missing ldelema or call following readonly prefix. Constrained, // Missing callvirt following constrained prefix. - //E_CIRCULAR_VAR_CONSTRAINTS "Method parent has circular class type parameter constraints." - //E_CIRCULAR_MVAR_CONSTRAINTS "Method has circular method type parameter constraints." + //E_CIRCULAR_VAR_CONSTRAINTS "Method parent has circular class type parameter constraints." + //E_CIRCULAR_MVAR_CONSTRAINTS "Method has circular method type parameter constraints." UnsatisfiedMethodInst, // Method instantiation has unsatisfied method type parameter constraints. UnsatisfiedMethodParentInst, // Method parent instantiation has unsatisfied class type parameter constraints. @@ -225,18 +225,18 @@ namespace ILVerify ConstrainedCallWithNonByRefThis, // The 'this' argument to a constrained call must have ByRef type. //E_CONSTRAINED_OF_NON_VARIABLE_TYPE "The operand to a constrained prefix instruction must be a type parameter." //E_READONLY_UNEXPECTED_CALLEE "The readonly prefix may only be applied to calls to array methods returning ByRefs." - ReadOnlyIllegalWrite, // "Illegal write to readonly ByRef." + ReadOnlyIllegalWrite, // Illegal write to readonly ByRef. //E_READONLY_IN_MKREFANY "A readonly ByRef cannot be used with mkrefany." - //E_UNALIGNED_ALIGNMENT "Alignment specified for 'unaligned' prefix must be 1, 2, or 4." - //E_TAILCALL_INSIDE_EH "The tail.call (or calli or callvirt) instruction cannot be used to transfer control out of a try, filter, catch, or finally block." - //E_BACKWARD_BRANCH "Stack height at all points must be determinable in a single forward scan of IL." - //E_CALL_TO_VTYPE_BASE "Call to base type of valuetype." - //E_NEWOBJ_OF_ABSTRACT_CLASS "Cannot construct an instance of abstract class." + //E_UNALIGNED_ALIGNMENT "Alignment specified for 'unaligned' prefix must be 1, 2, or 4." + //E_TAILCALL_INSIDE_EH "The tail.call (or calli or callvirt) instruction cannot be used to transfer control out of a try, filter, catch, or finally block." + //E_BACKWARD_BRANCH "Stack height at all points must be determinable in a single forward scan of IL." + //E_CALL_TO_VTYPE_BASE "Call to base type of valuetype." + //E_NEWOBJ_OF_ABSTRACT_CLASS "Cannot construct an instance of abstract class." UnmanagedPointer, // Unmanaged pointers are not a verifiable type. - //E_LDFTN_NON_FINAL_VIRTUAL "Cannot LDFTN a non-final virtual method for delegate creation if target object is potentially not the same type as the method class." - //E_FIELD_OVERLAP "Accessing type with overlapping fields." - //E_THIS_MISMATCH "The 'this' parameter to the call must be the calling method's 'this' parameter." - //E_STACK_I_I4 "Expected I4 on the stack." + LdftnNonFinalVirtual, // Cannot LDFTN a non-final virtual method for delegate creation if target object is potentially not the same type as the method class. + //E_FIELD_OVERLAP "Accessing type with overlapping fields." + ThisMismatch, // The 'this' parameter to the call must be the calling method's 'this' parameter. + //E_STACK_I_I4 "Expected I4 on the stack." //E_BAD_PE "Unverifiable PE Header/native stub." //E_BAD_MD "Unrecognized metadata, unable to verify IL." diff --git a/src/ILVerify/tests/ILTests/CallTests.il b/src/ILVerify/tests/ILTests/CallTests.il new file mode 100644 index 000000000..e39ce22ff --- /dev/null +++ b/src/ILVerify/tests/ILTests/CallTests.il @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +.assembly extern System.Runtime +{ +} + +.assembly CallTests +{ +} + +.class public auto ansi beforefieldinit SimpleClass + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig newslot virtual instance void VirtualMethod() cil managed + { + ret + } +} + +.class public auto ansi beforefieldinit DerivedClass + extends SimpleClass +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + ldarg.0 + call instance void SimpleClass::.ctor() + ret + } + + .method public hidebysig virtual instance void VirtualMethod() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ret + } + + .method public hidebysig instance void Call.BaseMethod_Valid() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ret + } + + .method public hidebysig instance void Call.BaseMethodModThisLdarga_Invalid_ThisMismatch() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ldarga 0 + pop + ret + } + + .method public hidebysig instance void Call.BaseMethodModThisStarg_Invalid_ThisMismatch() cil managed + { + ldarg.0 + call instance void SimpleClass::VirtualMethod() + ldarg.0 + starg 0 + ret + } +} + +.class public auto ansi beforefieldinit CallTestsType + extends [System.Runtime]System.Object +{ + .method public hidebysig instance void Call.IntToStringManagedPtr_Valid() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + stloc.0 + ldloca.s 0 + call instance string [System.Runtime]System.Int32::ToString() + pop + ret + } + + .method public hidebysig instance void Call.IntToStringBoxed_Valid() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + box [System.Runtime]System.Int32 + call instance string [System.Runtime]System.Object::ToString() + pop + ret + } + + .method public hidebysig instance void Callvirt.IntToStringBoxed_Valid() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + box [System.Runtime]System.Int32 + callvirt instance string [System.Runtime]System.Object::ToString() + pop + ret + } + + .method public hidebysig instance void Call.IntToStringUnboxed_Invalid_StackUnexpected() cil managed + { + .locals init ( + [0] int32 + ) + + ldc.i4.0 + call instance string [System.Runtime]System.Int32::ToString() + pop + ret + } + + .method public hidebysig instance void Call.ExternBaseMethod_Invalid_ThisMismatch(class DerivedClass c) cil managed + { + ldarg.1 + call instance void SimpleClass::VirtualMethod() + ret + } +} diff --git a/src/ILVerify/tests/ILTests/CastingTests.il b/src/ILVerify/tests/ILTests/CastingTests.il index bddd3bcd3..8387ea85f 100644 --- a/src/ILVerify/tests/ILTests/CastingTests.il +++ b/src/ILVerify/tests/ILTests/CastingTests.il @@ -106,7 +106,7 @@ } } -.class public sequential ansi beforefieldinit GenericOtherFieldsType`1<T> +.class public sequential ansi sealed beforefieldinit GenericOtherFieldsType`1<T> extends [System.Runtime]System.ValueType { .field public !T GenericField; diff --git a/src/ILVerify/tests/ILTests/FtnTests.il b/src/ILVerify/tests/ILTests/FtnTests.il index 5384d7b38..cb76a104f 100644 --- a/src/ILVerify/tests/ILTests/FtnTests.il +++ b/src/ILVerify/tests/ILTests/FtnTests.il @@ -94,6 +94,12 @@ ret } + .method public hidebysig static int32 StaticIntMethod() cil managed + { + ldc.i4.0 + ret + } + .method public hidebysig static void StaticMethodRefConstr<class T>() cil managed { ret @@ -223,7 +229,7 @@ ret } - .method static public hidebysig void LdvirtFtn.ValueTypeWrongBox_Invalid_StackUnexpected.DelegateCtorSigO(int32 i) cil managed + .method static public hidebysig void LdvirtFtn.ValueTypeWrongBox_Invalid_StackUnexpected.DelegateCtorSigO.DelegatePattern(int32 i) cil managed { // (int i) // var f = new System.Func<int, int>(i.CompareTo); @@ -270,4 +276,91 @@ pop ret } + + .method static public hidebysig void LdFtn.NopInDelegatePattern_Invalid_DelegatePattern() cil managed + { + // var a = new System.Action(TestClass.StaticMethod); + // a(); + + ldnull + ldftn void TestClass::StaticMethod() + nop + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.NopInDelegatePattern_Invalid_DelegatePattern(class TestClass c) cil managed + { + // (TestClass c) + // var a = new System.Action(c.InstanceMethod); + // a(); + + ldarg.0 + dup + nop + ldvirtftn instance void TestClass::InstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.BranchIntoDelegatePattern_Invalid_DelegatePattern() cil managed + { + ldnull + ldftn void TestClass::StaticMethod() + br lbl_newobj + + ldnull + ldftn void TestClass::StaticMethod() + + lbl_newobj: + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdvirtFtn.BranchIntoDelegatePattern_Invalid_DelegatePattern(class TestClass t) cil managed + { + ldarg.0 + dup + br lbl_ldvirt + + ldarg.0 + dup + + lbl_ldvirt: + ldvirtftn instance void TestClass::InstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.DelegateMissingArgument_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn void TestClass::StaticMethod() + newobj instance void class [System.Runtime]System.Action`1<int32>::.ctor(object, native int) + ldc.i4.0 + callvirt instance void class [System.Runtime]System.Action`1<int32>::Invoke(!0) + ret + } + + .method static public hidebysig void LdFtn.DelegateWrongReturnType_Invalid_DelegateCtor() cil managed + { + ldnull + ldftn int32 TestClass::StaticIntMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } + + .method static public hidebysig void LdFtn.VirtualMethodNonThisPtr_Invalid_LdftnNonFinalVirtual(class TestClass c) cil managed + { + ldarg.0 + ldftn instance void TestClass::VirtInstanceMethod() + newobj instance void [System.Runtime]System.Action::.ctor(object, native int) + callvirt instance void [System.Runtime]System.Action::Invoke() + ret + } } |