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);
        }
Example #6
0
        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");
        }
Example #7
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);
            }
        }
Example #8
0
        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 ();
		}