public void TestSimpleReturn () { MethodDefinition m = GetTest ("SimpleReturn"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (1, result.Length); Assert.AreEqual (OpCodes.Ret, result [0].Instruction.OpCode); }
public void TestMultipleCatch() { MethodDefinition m = GetTest("MultipleCatch"); StackEntryAnalysis sea = new StackEntryAnalysis(m); StackEntryUsageResult [] result = sea.GetStackEntryUsage(GetFirstNewObj(m)); Assert.AreEqual(2, result.Length, "result-Length-2"); //no "return a"; Assert.AreEqual(OpCodes.Callvirt, result [0].Instruction.OpCode, "result[0]-Opcode-Callvirt"); //return b.ToString (); Assert.AreEqual(OpCodes.Callvirt, result [1].Instruction.OpCode, "result[1]-Opcode-Callvirt"); //return c.GetHashCode (); }
public void TestBranch() { MethodDefinition m = GetTest("Branch"); StackEntryAnalysis sea = new StackEntryAnalysis(m); StackEntryUsageResult [] result = sea.GetStackEntryUsage(GetFirstNewObj(m)); Assert.AreEqual(2, result.Length, "result-Length-2"); Assert.AreEqual(OpCodes.Ret, result [0].Instruction.OpCode, "result[0]-Opcode-Ret"); Assert.AreEqual(OpCodes.Callvirt, result [1].Instruction.OpCode, "result[1]-Opcode-Callvirt"); }
public void TestOutArg2() { MethodDefinition m = GetTest("OutArg2"); StackEntryAnalysis sea = new StackEntryAnalysis(m); StackEntryUsageResult [] result = sea.GetStackEntryUsage(GetFirstNewObj(m)); Assert.AreEqual(2, result.Length, "result-Length-2"); Assert.AreEqual(OpCodes.Stind_Ref, result [0].Instruction.OpCode, "result[0]-Opcode-Stind_Ref"); Assert.AreEqual(OpCodes.Ret, result [1].Instruction.OpCode, "result[1]-Opcode-Ret"); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } // is there any Newobj instructions in this method if (!OpCodeEngine.GetBitmask(method).Get(Code.Newobj)) { return(RuleResult.DoesNotApply); } StackEntryAnalysis sea = null; foreach (Instruction ins in method.Body.Instructions) { if (ins.OpCode.Code != Code.Newobj) { continue; } MethodReference constructor = (MethodReference)ins.Operand; if (!constructor.DeclaringType.IsNamed("System.Threading", "Thread")) { continue; } if (ins.Next != null && (ins.Next.OpCode.Code == Code.Call || ins.Next.OpCode.Code == Code.Callvirt)) //quick check to safe resources { MethodReference calledMethod = (MethodReference)ins.Next.Operand; if (calledMethod.IsNamed("System.Threading", "Thread", "Start")) { continue; } } if (sea == null) { sea = new StackEntryAnalysis(method); } StackEntryUsageResult [] usageResults = sea.GetStackEntryUsage(ins); if (!CheckUsage(usageResults)) { // Critical because code cannot work as intented Runner.Report(method, ins, Severity.Critical, Confidence.High, String.Empty); } } return(Runner.CurrentRuleResult); }
public void TestStackOffset() { MethodDefinition m = GetTest("StackOffset"); StackEntryAnalysis sea = new StackEntryAnalysis(m); StackEntryUsageResult [] result = sea.GetStackEntryUsage(GetFirstNewObj(m)); Assert.AreEqual(4, result.Length, "result-Length-4"); Assert.AreEqual(0, result [0].StackOffset, "result[0]-StackOffset-0"); Assert.AreEqual(1, result [1].StackOffset, "result[1]-StackOffset-1"); Assert.AreEqual(0, result [2].StackOffset, "result[2]-StackOffset-0"); Assert.AreEqual(0, result [3].StackOffset, "result[3]-StackOffset-0"); }
private void CheckParameters(MethodDefinition method) { Dictionary <ParameterDefinition, List <StackEntryUsageResult> > usages = new Dictionary <ParameterDefinition, List <StackEntryUsageResult> > (); foreach (Instruction ins in method.Body.Instructions) { if (!ins.IsLoadArgument()) { continue; } ParameterDefinition parameter = ins.GetParameter(method); // this is `this`, we do not care if ((parameter == null) || (parameter.Index == -1)) { continue; } // is parameter already known ? if (!usages.ContainsKey(parameter)) { if (parameter.IsOut || parameter.IsOptional || parameter.ParameterType.IsValueType) { continue; } if (parameter.ParameterType.IsArray || parameter.ParameterType.IsDelegate()) { continue; //TODO: these are more complex to handle, not supported for now } if (null == sea || sea.Method != method) { sea = new StackEntryAnalysis(method); } usages [parameter] = new List <StackEntryUsageResult> (); } usages [parameter].AddRange(sea.GetStackEntryUsage(ins)); } foreach (var usage in usages) { UpdateParameterLeastType(usage.Key, usage.Value); } }
public void TestSwitch2() { //I could not find c# code that loads a reference onto the stack before executing switch //newobj System.Object.ctor <- reference is on the stack //ldc.i4.0 //switch +2,+3 //ret <- default //ret <- switch1 //ret <- switch2 MethodDefinition m = new MethodDefinition("Switch2", Mono.Cecil.MethodAttributes.Public, this.type); ILProcessor il = m.Body.GetILProcessor(); Instruction switch1 = il.Create(OpCodes.Ret); Instruction switch2 = il.Create(OpCodes.Ret); il.Emit(OpCodes.Newobj, (MethodReference)GetFirstNewObj(GetTest("Switch")).Operand); //get object.ctor() il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Switch, new Instruction [] { switch1, switch2 }); //default il.Emit(OpCodes.Ret); //switch 1 and 2 il.Append(switch1); il.Append(switch2); StackEntryAnalysis sea = new StackEntryAnalysis(m); StackEntryUsageResult [] result = sea.GetStackEntryUsage(GetFirstNewObj(m)); Assert.AreEqual(3, result.Length, "result-Length-3"); Assert.AreEqual(OpCodes.Ret, result [0].Instruction.OpCode, "result[0]-Opcode-Ret"); Assert.AreEqual(OpCodes.Ret, result [1].Instruction.OpCode, "result[1]-Opcode-Ret"); Assert.AreEqual(OpCodes.Ret, result [2].Instruction.OpCode, "result[2]-Opcode-Ret"); }
public RuleResult CheckMethod(MethodDefinition method) { if (!method.HasBody) { return(RuleResult.DoesNotApply); } // is there any Newobj instructions in this method if (!OpCodeEngine.GetBitmask(method).Get(Code.Newobj)) { return(RuleResult.DoesNotApply); } StackEntryAnalysis sea = null; foreach (Instruction ins in method.Body.Instructions) { if (ins.OpCode.Code != Code.Newobj) { continue; } MethodReference constructor = (MethodReference)ins.Operand; if (!constructor.DeclaringType.Inherits("System", "Exception")) { continue; } // quick check to save resources if (ins.Next != null && ins.Next.OpCode.Code == Code.Throw) { continue; } if (sea == null) { sea = new StackEntryAnalysis(method); } StackEntryUsageResult [] usageResults = sea.GetStackEntryUsage(ins); bool exceptionUsed = false; foreach (var usage in usageResults) { switch (usage.Instruction.OpCode.Code) { case Code.Throw: //throw case Code.Ret: //return case Code.Stind_I: //out / ref case Code.Stind_I1: case Code.Stind_I2: case Code.Stind_I4: case Code.Stind_I8: case Code.Stind_R4: case Code.Stind_R8: case Code.Stind_Ref: //case Code.Stfld: exceptionUsed = true; break; case Code.Call: //call (to the exception or as an argument) case Code.Calli: case Code.Callvirt: case Code.Newobj: case Code.Initobj: IMethodSignature calledMethod = (IMethodSignature)usage.Instruction.Operand; int pcount = calledMethod.HasParameters ? calledMethod.Parameters.Count : 0; if (pcount <= usage.StackOffset) { break; //not used as a parameter } exceptionUsed = true; break; } if (exceptionUsed) { break; } } if (!exceptionUsed) { // Critical because code cannot work as intented Runner.Report(method, ins, Severity.Critical, Confidence.High); } } return(Runner.CurrentRuleResult); }
public void TestSwitch () { MethodDefinition m = GetTest ("Switch"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (2, result.Length, "result-Length-2"); Assert.AreEqual (OpCodes.Callvirt, result [0].Instruction.OpCode, "result[0]-Opcode-Callvirt"); Assert.AreEqual (OpCodes.Callvirt, result [1].Instruction.OpCode, "result[1]-Opcode-Callvirt"); }
public void TestStaticField () { MethodDefinition m = GetTest ("StaticField"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (2, result.Length); Assert.AreEqual (Code.Stsfld, result [0].Instruction.OpCode.Code); Assert.AreEqual (Code.Callvirt, result [1].Instruction.OpCode.Code); }
public void TestStackOffset () { MethodDefinition m = GetTest ("StackOffset"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (4, result.Length); Assert.AreEqual (0, result [0].StackOffset); Assert.AreEqual (1, result [1].StackOffset); Assert.AreEqual (0, result [2].StackOffset); Assert.AreEqual (0, result [3].StackOffset); }
public void TestCastclass () { MethodDefinition m = GetTest ("Castclass"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (1, result.Length); Assert.AreEqual (OpCodes.Throw, result [0].Instruction.OpCode); }
public void TestSwitch2 () { //I could not find c# code that loads a reference onto the stack before executing switch //newobj System.Object.ctor <- reference is on the stack //ldc.i4.0 //switch +2,+3 //ret <- default //ret <- switch1 //ret <- switch2 MethodDefinition m = new MethodDefinition ("Switch2", Mono.Cecil.MethodAttributes.Public, this.type); ILProcessor il = m.Body.GetILProcessor (); Instruction switch1 = il.Create (OpCodes.Ret); Instruction switch2 = il.Create (OpCodes.Ret); il.Emit (OpCodes.Newobj, (MethodReference) GetFirstNewObj (GetTest ("Switch")).Operand); //get object.ctor() il.Emit (OpCodes.Ldc_I4_0); il.Emit (OpCodes.Switch, new Instruction [] { switch1, switch2 }); //default il.Emit (OpCodes.Ret); //switch 1 and 2 il.Append (switch1); il.Append (switch2); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (3, result.Length); Assert.AreEqual (OpCodes.Ret, result [0].Instruction.OpCode); Assert.AreEqual (OpCodes.Ret, result [1].Instruction.OpCode); Assert.AreEqual (OpCodes.Ret, result [2].Instruction.OpCode); }
public void TestOutArg2 () { MethodDefinition m = GetTest ("OutArg2"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (2, result.Length); Assert.AreEqual (OpCodes.Stind_Ref, result [0].Instruction.OpCode); Assert.AreEqual (OpCodes.Ret, result [1].Instruction.OpCode); }
public void TestMultipleCatch () { MethodDefinition m = GetTest ("MultipleCatch"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (2, result.Length); //no "return a"; Assert.AreEqual (OpCodes.Callvirt, result [0].Instruction.OpCode); //return b.ToString (); Assert.AreEqual (OpCodes.Callvirt, result [1].Instruction.OpCode); //return c.GetHashCode (); }
public void TestStargStatic () { MethodDefinition m = GetTest ("StargStatic"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (1, result.Length, "result-Length-1"); Assert.AreEqual (OpCodes.Ret, result [0].Instruction.OpCode, "result-Opcode-Ret"); }
private void CheckParameters (MethodDefinition method) { Dictionary<ParameterDefinition, List<StackEntryUsageResult>> usages = new Dictionary<ParameterDefinition, List<StackEntryUsageResult>> (); foreach (Instruction ins in method.Body.Instructions) { if (!ins.IsLoadArgument ()) continue; ParameterDefinition parameter = ins.GetParameter (method); // this is `this`, we do not care if ((parameter == null) || (parameter.GetSequence () == 0)) continue; // is parameter already known ? if (!usages.ContainsKey (parameter)) { if (parameter.IsOut || parameter.IsOptional || parameter.ParameterType.IsValueType) continue; if (parameter.ParameterType.IsArray || parameter.ParameterType.IsDelegate ()) continue; //TODO: these are more complex to handle, not supported for now if (null == sea || sea.Method != method) sea = new StackEntryAnalysis (method); usages [parameter] = new List<StackEntryUsageResult> (); } usages [parameter].AddRange (sea.GetStackEntryUsage (ins)); } foreach (var usage in usages) UpdateParameterLeastType (usage.Key, usage.Value); }
public RuleResult CheckMethod (MethodDefinition method) { if (!method.HasBody) return RuleResult.DoesNotApply; // is there any Newobj instructions in this method if (!OpCodeEngine.GetBitmask (method).Get (Code.Newobj)) return RuleResult.DoesNotApply; StackEntryAnalysis sea = null; foreach (Instruction ins in method.Body.Instructions) { if (ins.OpCode.Code != Code.Newobj) continue; MethodReference constructor = (MethodReference) ins.Operand; if (!constructor.DeclaringType.Inherits ("System", "Exception")) continue; // quick check to save resources if (ins.Next != null && ins.Next.OpCode.Code == Code.Throw) continue; if (sea == null) sea = new StackEntryAnalysis (method); StackEntryUsageResult [] usageResults = sea.GetStackEntryUsage (ins); bool exceptionUsed = false; foreach (var usage in usageResults) { switch (usage.Instruction.OpCode.Code) { case Code.Throw: //throw case Code.Ret: //return case Code.Stind_I: //out / ref case Code.Stind_I1: case Code.Stind_I2: case Code.Stind_I4: case Code.Stind_I8: case Code.Stind_R4: case Code.Stind_R8: case Code.Stind_Ref: //case Code.Stfld: exceptionUsed = true; break; case Code.Call: //call (to the exception or as an argument) case Code.Calli: case Code.Callvirt: case Code.Newobj: case Code.Initobj: IMethodSignature calledMethod = (IMethodSignature) usage.Instruction.Operand; int pcount = calledMethod.HasParameters ? calledMethod.Parameters.Count : 0; if (pcount <= usage.StackOffset) break; //not used as a parameter exceptionUsed = true; break; } if (exceptionUsed) break; } if (!exceptionUsed) { // Critical because code cannot work as intented Runner.Report (method, ins, Severity.Critical, Confidence.High); } } return Runner.CurrentRuleResult; }
public RuleResult CheckMethod (MethodDefinition method) { if (!method.HasBody) return RuleResult.DoesNotApply; // is there any Newobj instructions in this method if (!OpCodeEngine.GetBitmask (method).Get (Code.Newobj)) return RuleResult.DoesNotApply; StackEntryAnalysis sea = null; foreach (Instruction ins in method.Body.Instructions) { if (ins.OpCode.Code != Code.Newobj) continue; MethodReference constructor = (MethodReference) ins.Operand; if (constructor.DeclaringType.FullName != Thread) continue; if (ins.Next != null && (ins.Next.OpCode.Code == Code.Call || ins.Next.OpCode.Code == Code.Callvirt)) { //quick check to safe resources MethodReference calledMethod = (MethodReference) ins.Next.Operand; if ((calledMethod.DeclaringType.FullName == Thread) && (calledMethod.Name == "Start")) continue; } if (sea == null) sea = new StackEntryAnalysis (method); StackEntryUsageResult [] usageResults = sea.GetStackEntryUsage (ins); if (!CheckUsage (usageResults)) { // Critical because code cannot work as intented Runner.Report (method, ins, Severity.Critical, Confidence.High, String.Empty); } } return Runner.CurrentRuleResult; }
public void TestField2 () { MethodDefinition m = GetTest ("Field2"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (1, result.Length, "result-Length-1"); Assert.AreEqual (Code.Stfld, result [0].Instruction.OpCode.Code, "result-Opcode-Stfld"); }
private bool CheckPInvoke(Instruction startInstruction) { branches.Clear(); branches.Add(new Branch(startInstruction.Next, false)); for (int i = 0; i < branches.Count; i++) //follow all branches { Instruction ins = branches [i].Instruction; bool dirty = branches [i].DirtyMethodCalled; bool getLastErrorFound = false; while (true) //follow the branch //check if a method is called { if (ins.OpCode.FlowControl == FlowControl.Call) { MethodReference mRef = ins.Operand as MethodReference; if (mRef == null) { continue; } MethodDefinition mDef = mRef.Resolve(); if (mDef != null && mDef.IsPInvokeImpl) //check if another pinvoke method is called, this counts as "GetLastError not called" { break; } string s = (mDef == null) ? String.Empty : mDef.DeclaringType.GetFullName(); switch (s) { case "System.Runtime.InteropServices.Marshal": getLastErrorFound = (mDef.Name == "GetLastWin32Error"); break; //found case "System.Runtime.InteropServices.SafeHandle": dirty = (mDef.Name != "get_IsInvalid"); break; case "System.IntPtr": case "System.UIntPtr": string name = mDef.Name; dirty = ((name != "op_Inequality") && (name != "op_Equality")); break; default: dirty = true; break; } if (getLastErrorFound) { break; } } //fetch the next instruction object alternatives; ins = StackEntryAnalysis.GetNextInstruction(ins, out alternatives); if (ins == null) { break; } if (alternatives != null) { Instruction alt_ins = (alternatives as Instruction); if (alt_ins != null) { branches.AddIfNew(new Branch(alt_ins, dirty)); } else { Instruction [] alts = (Instruction [])alternatives; foreach (Instruction altIns in alts) { branches.AddIfNew(new Branch(altIns, dirty)); } } } //avoid infinity loop if (ins.OpCode.FlowControl == FlowControl.Branch) { branches.AddIfNew(new Branch(ins, dirty)); break; } } //report error if (getLastErrorFound && dirty) { return(false); } } return(true); }
public void TestPop () { MethodDefinition m = GetTest ("Pop"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (0, result.Length); }
public void TestNestedTryFinally2 () { MethodDefinition m = GetTest ("NestedTryFinally2"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (1, result.Length); Assert.AreEqual (OpCodes.Callvirt, result [0].Instruction.OpCode); }
public void TestTryCatchFinally () { MethodDefinition m = GetTest ("TryCatchFinally"); StackEntryAnalysis sea = new StackEntryAnalysis (m); StackEntryUsageResult [] result = sea.GetStackEntryUsage (GetFirstNewObj (m)); Assert.AreEqual (2, result.Length, "result-Length-2"); //no "return a"; Assert.AreEqual (OpCodes.Callvirt, result [0].Instruction.OpCode, "result[0]-Opcode-Callvirt"); //return a.ToString (); Assert.AreEqual (OpCodes.Callvirt, result [1].Instruction.OpCode, "result[1]-Opcode-Callvirt"); //return c.GetHashCode (); }