diff --git a/Mono.Cecil.Cil/CodeWriter.cs b/Mono.Cecil.Cil/CodeWriter.cs index 2003f104f..919001831 100644 --- a/Mono.Cecil.Cil/CodeWriter.cs +++ b/Mono.Cecil.Cil/CodeWriter.cs @@ -217,7 +217,7 @@ void WriteOperand (Instruction instruction) case OperandType.ShortInlineBrTarget: { var target = (Instruction) operand; var offset = target != null ? GetTargetOffset (target) : body.code_size; - WriteSByte ((sbyte) (offset - (instruction.Offset + opcode.Size + 1))); + WriteSByte (checked ((sbyte) (offset - (instruction.Offset + opcode.Size + 1)))); break; } case OperandType.InlineBrTarget: { @@ -227,16 +227,16 @@ void WriteOperand (Instruction instruction) break; } case OperandType.ShortInlineVar: - WriteByte ((byte) GetVariableIndex ((VariableDefinition) operand)); + WriteByte (checked ((byte) GetVariableIndex ((VariableDefinition) operand))); break; case OperandType.ShortInlineArg: - WriteByte ((byte) GetParameterIndex ((ParameterDefinition) operand)); + WriteByte (checked ((byte) GetParameterIndex ((ParameterDefinition) operand))); break; case OperandType.InlineVar: - WriteInt16 ((short) GetVariableIndex ((VariableDefinition) operand)); + WriteInt16 (checked ((short) GetVariableIndex ((VariableDefinition) operand))); break; case OperandType.InlineArg: - WriteInt16 ((short) GetParameterIndex ((ParameterDefinition) operand)); + WriteInt16 (checked ((short) GetParameterIndex ((ParameterDefinition) operand))); break; case OperandType.InlineSig: WriteMetadataToken (GetStandAloneSignature ((CallSite) operand)); diff --git a/Test/Mono.Cecil.Tests/BaseTestFixture.cs b/Test/Mono.Cecil.Tests/BaseTestFixture.cs index 0112725f1..e80cd710b 100644 --- a/Test/Mono.Cecil.Tests/BaseTestFixture.cs +++ b/Test/Mono.Cecil.Tests/BaseTestFixture.cs @@ -124,6 +124,25 @@ static void Run (TestCase testCase) using (var runner = new TestRunner (testCase, TestCaseType.WriteFromImmediate)) runner.RunTest (); } + + public static void SaveModuleAndRun (ModuleDefinition module, Action action) + { + var filename = Path.GetTempFileName (); + var domain = AppDomain.CreateDomain ("test", null, AppDomain.CurrentDomain.SetupInformation); + try + { + using (var stream = File.Create (filename)) + module.Write (stream); + + domain.DoCallBack ((CrossAppDomainDelegate) Delegate.CreateDelegate (typeof (CrossAppDomainDelegate), + filename, action.Method)); + } + finally + { + AppDomain.Unload (domain); + File.Delete (filename); + } + } } abstract class TestCase { diff --git a/Test/Mono.Cecil.Tests/MethodBodyTests.cs b/Test/Mono.Cecil.Tests/MethodBodyTests.cs index 15fd5fb42..a2c6df647 100644 --- a/Test/Mono.Cecil.Tests/MethodBodyTests.cs +++ b/Test/Mono.Cecil.Tests/MethodBodyTests.cs @@ -76,6 +76,65 @@ .locals init (System.String V_0) }); } + [Test] + [ExpectedException (typeof (OverflowException))] + public void BranchOffsetTruncation () + { + TestModule ("libhello.dll", module => { + var lib = module.GetType ("Library"); + Assert.IsNotNull (lib); + + var method = lib.GetMethod ("GetHelloString"); + Assert.IsNotNull (method); + + // insert some valid IL + // truncated offset will branch between Ldnull and Ret + var insn = method.Body.Instructions.First (_ => _.OpCode == OpCodes.Ldloc_0); + var il = method.Body.GetILProcessor (); + for (int i = 0 ; i < 128 ; ++i) + { + il.InsertBefore (insn, il.Create (OpCodes.Ldnull)); + il.InsertBefore (insn, il.Create (OpCodes.Ret)); + } + + il.InsertBefore (insn, il.Create (OpCodes.Nop)); + }); + } + + [Test] + [ExpectedException (typeof (OverflowException))] + public void BranchOffsetTruncation2 () + { + TestModule ("libhello.dll", module => { + var lib = module.GetType ("Library"); + Assert.IsNotNull (lib); + + var method = lib.GetMethod ("GetHelloString"); + Assert.IsNotNull (method); + + // insert some valid IL + // truncated offset will branch before Ldnull + var insn = method.Body.Instructions.First (_ => _.OpCode == OpCodes.Ldloc_0); + var il = method.Body.GetILProcessor (); + for (int i = 0 ; i < 128 ; ++i) + { + il.InsertBefore (insn, il.Create (OpCodes.Ldnull)); + il.InsertBefore (insn, il.Create (OpCodes.Ret)); + } + + SaveModuleAndRun (module, BranchOffsetTruncation2Delegate); + }); + } + + static void BranchOffsetTruncation2Delegate (string filename) + { + var obj = Activator.CreateInstanceFrom (filename, "Library").Unwrap (); + + // should be equal because inserted instructions never execute, + // but is null without the truncation check + Assert.AreEqual ("hello world of tomorrow", obj.GetType ().GetMethod ("GetHelloString").Invoke (obj, null)); + } + [Test] public void Switch () { diff --git a/Test/Resources/assemblies/libhello.dll b/Test/Resources/assemblies/libhello.dll index 7b43e0114..154560cb3 100644 Binary files a/Test/Resources/assemblies/libhello.dll and b/Test/Resources/assemblies/libhello.dll differ