diff options
author | Samuel Arzt <arzt.samuel@live.de> | 2017-08-17 17:57:35 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2017-08-17 17:57:35 +0300 |
commit | 55b48213fff4ee0eb131a682a0e7f276ce5afa42 (patch) | |
tree | 30576890de1bd81de3ce19caf31daf324e219423 /src/ILVerify | |
parent | 232f423b9aa50545635321d4ff6cef71f56c3aaf (diff) |
[ILVerify] Implement switch instruction (#4328)
* Added value pop for switch instruction.
* Added switch instruction tests and todos for jump target verification.
* Added branch target verification with test cases.
* Moved switch test cases into seperate file.
* Refactored switch/branch tests to use meaningful labels and not use locals.
Diffstat (limited to 'src/ILVerify')
-rw-r--r-- | src/ILVerify/src/ILImporter.Verify.cs | 59 | ||||
-rw-r--r-- | src/ILVerify/src/Resources/Strings.resx | 18 | ||||
-rw-r--r-- | src/ILVerify/src/VerifierError.cs | 12 | ||||
-rw-r--r-- | src/ILVerify/tests/ILTests/BranchingTests.il | 78 | ||||
-rw-r--r-- | src/ILVerify/tests/ILTests/SwitchTests.il | 214 |
5 files changed, 373 insertions, 8 deletions
diff --git a/src/ILVerify/src/ILImporter.Verify.cs b/src/ILVerify/src/ILImporter.Verify.cs index 2d734b8f5..8d47f5d46 100644 --- a/src/ILVerify/src/ILImporter.Verify.cs +++ b/src/ILVerify/src/ILImporter.Verify.cs @@ -360,6 +360,57 @@ namespace Internal.IL VerificationError(VerifierError.StackUnexpected, src, dst); } + void CheckIsValidBranchTarget(BasicBlock src, BasicBlock target) + { + if (src.TryIndex != target.TryIndex) + { + if (src.TryIndex == null) + VerificationError(VerifierError.BranchIntoTry); + else if (target.TryIndex == null) + VerificationError(VerifierError.BranchOutOfTry); + else + { + if (_exceptionRegions[(int)src.TryIndex].ILRegion.TryOffset < _exceptionRegions[(int)target.TryIndex].ILRegion.TryOffset) + VerificationError(VerifierError.BranchIntoTry); + else + VerificationError(VerifierError.BranchOutOfTry); + } + return; + } + + if (src.FilterIndex != target.FilterIndex) + { + if (src.FilterIndex == null) + VerificationError(VerifierError.BranchIntoFilter); + else if (target.HandlerIndex == null) + VerificationError(VerifierError.BranchOutOfFilter); + else + { + if (_exceptionRegions[(int)src.FilterIndex].ILRegion.FilterOffset < _exceptionRegions[(int)target.FilterIndex].ILRegion.FilterOffset) + VerificationError(VerifierError.BranchIntoFilter); + else + VerificationError(VerifierError.BranchOutOfFilter); + } + return; + } + + if (src.HandlerIndex != target.HandlerIndex) + { + if (src.HandlerIndex == null) + VerificationError(VerifierError.BranchIntoHandler); + else if (target.HandlerIndex == null) + VerificationError(VerifierError.BranchOutOfHandler); + else + { + if (_exceptionRegions[(int)src.HandlerIndex].ILRegion.HandlerOffset < _exceptionRegions[(int)target.HandlerIndex].ILRegion.HandlerOffset) + VerificationError(VerifierError.BranchIntoHandler); + else + VerificationError(VerifierError.BranchOutOfHandler); + } + return; + } + } + // For now, match PEVerify type formating to make it easy to compare with baseline static string TypeToStringForIsAssignable(TypeDesc type) { @@ -1036,20 +1087,24 @@ namespace Internal.IL void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) { + var value = Pop(); + CheckIsAssignable(value, StackValue.CreatePrimitive(StackValueKind.Int32)); + for (int i = 0; i < jmpDelta.Length; i++) { BasicBlock target = _basicBlocks[jmpBase + jmpDelta[i]]; + CheckIsValidBranchTarget(_currentBasicBlock, target); ImportFallthrough(target); } if (fallthrough != null) ImportFallthrough(fallthrough); - - throw new NotImplementedException($"{nameof(ImportSwitchJump)} not implemented"); } void ImportBranch(ILOpcode opcode, BasicBlock target, BasicBlock fallthrough) { + CheckIsValidBranchTarget(_currentBasicBlock, target); + switch (opcode) { case ILOpcode.br: diff --git a/src/ILVerify/src/Resources/Strings.resx b/src/ILVerify/src/Resources/Strings.resx index c14d1a18f..3d4808683 100644 --- a/src/ILVerify/src/Resources/Strings.resx +++ b/src/ILVerify/src/Resources/Strings.resx @@ -120,6 +120,24 @@ <data name="ArrayByRef" xml:space="preserve"> <value>Array of ELEMENT_TYPE_BYREF or ELEMENT_TYPE_TYPEDBYREF.</value> </data> + <data name="BranchIntoFilter" xml:space="preserve"> + <value>Branch into exception filter block.</value> + </data> + <data name="BranchIntoHandler" xml:space="preserve"> + <value>Branch into exception handler block.</value> + </data> + <data name="BranchIntoTry" xml:space="preserve"> + <value>Branch into try block.</value> + </data> + <data name="BranchOutOfFilter" xml:space="preserve"> + <value>Branch out of exception filter block.</value> + </data> + <data name="BranchOutOfHandler" xml:space="preserve"> + <value>Branch out of exception handler block.</value> + </data> + <data name="BranchOutOfTry" xml:space="preserve"> + <value>Branch out of try block.</value> + </data> <data name="CallAbstract" xml:space="preserve"> <value>Call not allowed on abstract methods.</value> </data> diff --git a/src/ILVerify/src/VerifierError.cs b/src/ILVerify/src/VerifierError.cs index 014448588..3ead0c61e 100644 --- a/src/ILVerify/src/VerifierError.cs +++ b/src/ILVerify/src/VerifierError.cs @@ -63,12 +63,12 @@ namespace ILVerify Endfinally, //"Endfinally from outside a finally handler." Endfilter, //"Endfilter from outside an exception filter block." //E_ENDFILTER_MISSING "Missing Endfilter." - //E_BR_INTO_TRY "Branch into try block." - //E_BR_INTO_HND "Branch into exception handler block." - //E_BR_INTO_FIL "Branch into exception filter block." - //E_BR_OUTOF_TRY "Branch out of try block." - //E_BR_OUTOF_HND "Branch out of exception handler block." - //E_BR_OUTOF_FIL "Branch out of exception filter block." + 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." //E_RET_FROM_TRY "Return out of try block." //E_RET_FROM_HND "Return out of exception handler block." diff --git a/src/ILVerify/tests/ILTests/BranchingTests.il b/src/ILVerify/tests/ILTests/BranchingTests.il index 57554cafe..09b3b35bf 100644 --- a/src/ILVerify/tests/ILTests/BranchingTests.il +++ b/src/ILVerify/tests/ILTests/BranchingTests.il @@ -66,4 +66,82 @@ IL_0013: pop IL_0014: ret } + + .method static public hidebysig void Branching.InsideTry_Valid() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_leave + + lbl_true: nop + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.OutOfTry_Invalid_BranchOutOfTry() cil managed + { + .maxstack 2 + + .try + { + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_ret + + lbl_true: nop + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Branching.IntoTry_Invalid_BranchIntoTry() cil managed + { + .maxstack 2 + + ldc.i4.s 10 + ldc.i4.s 5 + bne.un.s lbl_true + + nop + br.s lbl_ret + + .try + { + lbl_true: nop + + leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } } diff --git a/src/ILVerify/tests/ILTests/SwitchTests.il b/src/ILVerify/tests/ILTests/SwitchTests.il new file mode 100644 index 000000000..22fc68e3c --- /dev/null +++ b/src/ILVerify/tests/ILTests/SwitchTests.il @@ -0,0 +1,214 @@ +// 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 SwitchTests +{ +} + +.class public auto ansi beforefieldinit SwitchTestsType + extends [System.Runtime]System.Object +{ + .method static public hidebysig void Switch.Int32Value_Valid() cil managed + { + .maxstack 1 + + ldc.i4.s 10 + switch (lbl_ret, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_ret + + lbl_a: nop + br.s lbl_ret + + lbl_b: nop + + lbl_ret: ret + } + + .method static public hidebysig void Switch.Int64Value_Invalid_StackUnexpected() cil managed + { + .maxstack 1 + + ldc.i8 10 + switch (lbl_ret, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_ret + + lbl_a: nop + br.s lbl_ret + + lbl_b: nop + + lbl_ret: ret + } + + .method static public hidebysig void Switch.InsideTry_Valid() cil managed + { + .maxstack 1 + + .try + { + ldc.i4.s 10 + switch (lbl_leave, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + lbl_b: nop + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + nop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.IntoTry_Invalid_BranchIntoTry() cil managed + { + .maxstack 1 + + ldc.i4.s 10 + switch (lbl_ret, lbl_a) + + br.s lbl_ret + + .try + { + lbl_a: nop + br.s lbl_leave + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + nop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.OutOfTry_Invalid_BranchOutOfTry() cil managed + { + .maxstack 1 + + .try + { + ldc.i4.s 10 + switch (lbl_ret, lbl_a) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + nop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.NestedOutOfTry_Invalid_BranchOutOfTry() cil managed + { + .maxstack 1 + + .try + { + .try + { + ldc.i4.s 10 + switch (lbl_leave, lbl_a, lbl_leave2) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + lbl_leave: leave.s lbl_leave2 + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_leave2 + } + + lbl_leave2: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.NestedIntoTry_Invalid_BranchIntoTry() cil managed + { + .maxstack 1 + + .try + { + ldc.i4.s 10 + switch (lbl_leave, lbl_a, lbl_b) + + br.s lbl_leave + + lbl_a: nop + br.s lbl_leave + + .try + { + lbl_b: nop + leave.s lbl_leave + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_leave + } + + lbl_leave: leave.s lbl_ret + } + catch [System.Runtime]System.Object + { + pop + leave.s lbl_ret + } + + lbl_ret: ret + } + + .method static public hidebysig void Switch.ObjectValue_Invalid_StackUnexpected() cil managed + { + .maxstack 1 + + ldnull + switch (lbl_ret, lbl_a, lbl_a, lbl_b, lbl_b) + + br.s lbl_ret + + lbl_a: nop + br.s lbl_ret + + lbl_b: nop + + lbl_ret: ret + } +} |