private static string GetLoadStringFormatInstruction (Instruction call, MethodDefinition method,
			int formatPosition)
		{
			Instruction loadString = call.TraceBack (method, -formatPosition);
			if (loadString == null)
				return null;

			// If we find a variable load, search the store
			while (loadString.IsLoadLocal ()) {
				Instruction storeIns = GetStoreLocal (loadString, method);
				if (storeIns == null)
					return null;
				loadString = storeIns.TraceBack (method);
				if (loadString == null)
					return null;
			}

			switch (loadString.OpCode.Code) {
			case Code.Call:
			case Code.Callvirt:
				return GetLoadStringFromCall (loadString.Operand as MethodReference);
			case Code.Ldstr:
				return loadString.Operand as string;
			default:
				return null;
			}
		}
Пример #2
0
		void CheckReturn (Instruction ins, MethodDefinition method)
		{
			// trace back what is being returned
			Instruction previous = ins.TraceBack (method);
			while (previous != null) {
				// most of the time we'll find the null value on the first trace back call
				if (previous.OpCode.Code == Code.Ldnull) {
					Report (method, ins);
					break;
				}

				// but CSC non-optimized code generation results in strange IL that needs a few 
				// more calls. e.g. "return null" == "nop | ldnull | stloc.0 | br.s | ldloc.0 | ret"
				if ((previous.OpCode.FlowControl == FlowControl.Branch) || (previous.IsLoadLocal ()
					|| previous.IsStoreLocal ())) {
					previous = previous.TraceBack (method);
				} else
					break;
			}
		}
		// ctors are identical for ArgumentNullException, ArgumentOutOfRangeException and DuplicateWaitObjectException
		private void CheckOtherExceptions (IMethodSignature constructor, Instruction ins, MethodDefinition method)
		{
			// OK		public ArgumentNullException ()
			if (!constructor.HasParameters)
				return;

			// OK		protected ArgumentNullException (SerializationInfo info, StreamingContext context)
			// OK		public ArgumentNullException (string message, Exception innerException)
			IList<ParameterDefinition> pdc = constructor.Parameters;
			if ((pdc.Count == 2) && (pdc [1].ParameterType.FullName != "System.String"))
				return;

			// CHECK	public ArgumentNullException (string paramName)
			// CHECK	public ArgumentNullException (string paramName, string message)
			Instruction call = ins.TraceBack (method, 0);
			string name = call.Operand as string;
			if (MatchesAnyParameter (method, name))
				return;

			Report (method, ins, name);
		}
		private void CheckArgumentException (IMethodSignature ctor, Instruction ins, MethodDefinition method)
		{
			// OK		public ArgumentException ()
			if (!ctor.HasParameters)
				return;

			// OK		public ArgumentException (string message)
			IList<ParameterDefinition> pdc = ctor.Parameters;
			if (pdc.Count < 2)
				return;

			// OK		public ArgumentException (string message, Exception innerException)
			if (pdc [1].ParameterType.FullName != "System.String")
				return;

			// CHECK	public ArgumentException (string message, string paramName)
			// CHECK	public ArgumentException (string message, string paramName, Exception innerException)
			Instruction call = ins.TraceBack (method, -1);
			string name = call.Operand as string;
			if (MatchesAnyParameter (method, name))
				return;

			Report (method, ins, name);
		}
		static Instruction LocalTraceBack (IMethodSignature method, Instruction ins)
		{
			ins = ins.TraceBack (method);
			while (ins != null) {
				if (ins.IsLoadLocal () || ins.IsStoreLocal ())
					return ins;
				ins = ins.TraceBack (method);
			}
			return null;
		}
		// This is like the TraceBack rock except that it continues traversing backwards
		// until it finds an instruction which does not pop any values off the stack. This
		// allows us to check all of the instructions used to compute the method's
		// arguments.
		internal static Instruction FullTraceBack (IMethodSignature method, Instruction end)
		{
			Instruction first = end.TraceBack (method);
			
			while (first != null && first.GetPopCount (method) > 0) {
				first = first.TraceBack (method);
			}
			
			return first;
		}
		public override void Analyze (MethodDefinition method, MethodReference enter, Instruction ins)
		{
			Instruction locker = ins.TraceBack (method);
			if (locker.OpCode.Code == Code.Dup)
				locker = locker.TraceBack (method);

			string msg = CheckLocker (method, locker);
			if (msg.Length > 0)
				Runner.Report (method, ins, Severity.High, Confidence.High, msg);
		}
        void CheckReplace(MethodDefinition method, Instruction ins)
        {
            string p1 = GetString (ins.TraceBack (method, -1));
            if (p1.Length != 1)
                return;

            string p2 = GetString (ins.TraceBack (method, -2));
            if (p2.Length != 1)
                return;

            string msg = String.Format (CultureInfo.InvariantCulture,
                "Prefer the use of: Replace('{0}','{1}');", p1, p2);
            // confidence is higher since there's no StringComparison to consider
            Runner.Report (method, ins, Severity.Medium, Confidence.High, msg);
        }
		void CheckString (Instruction ins, int argumentOffset)
		{
			Instruction ld = ins.TraceBack (method, argumentOffset);
			if (null == ld)
				return;

			switch (ld.OpCode.Code) {
			case Code.Ldstr:
				CheckString (ins, (string) ld.Operand);
				break;
			case Code.Ldsfld:
				FieldReference f = (FieldReference) ld.Operand;
				if (f.Name == "Empty" && f.DeclaringType.FullName == "System.String")
					CheckString (ins, null);
				break;
			case Code.Ldnull:
				CheckString (ins, null);
				break;
			}
		}
		private static bool TryComputeArraySize (Instruction call, MethodDefinition method, int lastParameterPosition,
			out int elementsPushed)
		{
			elementsPushed = 0;
			Instruction loadArray = call.TraceBack (method, -lastParameterPosition);

			if (loadArray == null)
				return false;

			while (loadArray.OpCode != OpCodes.Newarr) {
				if (loadArray.OpCode == OpCodes.Dup)
					loadArray = loadArray.TraceBack (method);
				else if (loadArray.IsLoadLocal ()) {
					Instruction storeIns = GetStoreLocal (loadArray, method);
					if (storeIns == null)
						return false;
					loadArray = storeIns.TraceBack (method);
				} else
					return false;

				if (loadArray == null)
					return false;
			}

			if (loadArray.Previous == null)
				return false;

			// Previous operand should be a ldc.I4 instruction type
			object previousOperand = loadArray.Previous.GetOperand (method);
			if (!(previousOperand is int))
				return false;
			elementsPushed = (int) previousOperand;
			return true;
		}
		static bool Compare (Instruction left, Instruction right, MethodDefinition method)
		{
			if (left == null)
				return (right == null);
			else if (right == null)
				return false;

			// is it on the same instance ?
			Instruction origin_left = left.TraceBack (method);
			Instruction origin_right = right.TraceBack (method);

			// if this is an array access the it must be the same element
			if (origin_left.IsLoadElement () && origin_right.IsLoadElement ()) {
				if (!CompareOperand (origin_left.Previous, origin_right.Previous, method))
					return false;
			} else {
				if (!CompareOperand (origin_left, origin_right, method))
					return false;
			}

			return Compare (origin_left, origin_right, method);
		}
		// ctors are identical for ArgumentNullException, ArgumentOutOfRangeException and DuplicateWaitObjectException
		private void CheckOtherExceptions (IMethodSignature constructor, Instruction ins, MethodDefinition method)
		{
			// OK		public ArgumentNullException ()
			if (!constructor.HasParameters)
				return;

			// OK		protected ArgumentNullException (SerializationInfo info, StreamingContext context)
			// OK		public ArgumentNullException (string message, Exception innerException)
			IList<ParameterDefinition> pdc = constructor.Parameters;
			if ((pdc.Count == 2) && !pdc [1].ParameterType.IsNamed ("System", "String"))
				return;

			// CHECK	public ArgumentNullException (string paramName)
			// CHECK	public ArgumentNullException (string paramName, string message)
			Instruction call = ins.TraceBack (method, 0);
			
			// call will be null if there is branching logic in the selection of a message - just fon't check in this case
			if (call == null)
				return;

			string name = call.Operand as string;
			if (MatchesAnyParameter (method, name))
				return;

			Report (method, ins, name);
		}
        private static string GetLoadStringInstruction(Instruction call, MethodDefinition method, int formatPosition)
        {
            Instruction loadString = call.TraceBack(method, -formatPosition);
            if (loadString == null)
                return null;

            // If we find a variable load, search the store
            while (loadString.IsLoadLocal())
            {
                Instruction storeIns = GetStoreLocal(loadString, method);
                if (storeIns == null)
                    return null;
                loadString = storeIns.TraceBack(method);
                if (loadString == null)
                    return null;
            }

            var mr = loadString.Operand as MethodReference;
            if (mr != null && mr.DeclaringType.FullName == "System.String")
            {
                if (mr.Name == "Concat")
                {
                    return GetLoadStringInstruction(loadString, method, 0);
                }
            }

            switch (loadString.OpCode.Code)
            {
                case Code.Call:
                case Code.Callvirt:
                    return GetLoadStringFromCall(loadString.Operand as MethodReference);
                case Code.Ldstr:
                    return loadString.Operand as string;
                default:
                    return null;
            }
        }
		void CheckCall (MethodDefinition method, Instruction ins, MethodReference call)
		{
			if (null == call) //resolution did not work
				return;
			if (!call.HasParameters)
				return;

			TypeReference type = call.DeclaringType;
			if (!type.IsNamed ("System.Text.RegularExpressions", "Regex") && !type.IsNamed ("System.Configuration", "RegexStringValidator"))
				return;

			MethodDefinition mdef = call.Resolve ();
			if (null == mdef)
				return;
			//check only constructors and static non-property methods
			if (!mdef.IsConstructor && (mdef.HasThis || mdef.IsProperty ()))
				return;

			foreach (ParameterDefinition p in mdef.Parameters) {
				string pname = p.Name;
				if ((pname == "pattern" || pname == "regex") && p.ParameterType.IsNamed ("System", "String")) {
					Instruction ld = ins.TraceBack (method, -(call.HasThis ? 0 : p.Index));
					if (ld != null)
						CheckArguments (method, ins, ld);
					return;
				}
			}
		}
		private static bool CheckParameters (MethodDefinition method, Instruction ins)
		{
			Instruction prev;
			if (ins.IsLoadLocal ()) {
				prev = ins.Previous;
				while (null != prev) {
					// look for a STLOC* instruction and compare the variable indexes
					if (prev.IsStoreLocal () && AreMirrorInstructions (ins, prev, method))
						return IsGetNow (prev.Previous);
					prev = prev.Previous;
				}
			} else if (ins.OpCode.Code == Code.Ldobj) {
				prev = ins.TraceBack (method);
				ParameterDefinition p = prev.GetParameter (method);
				if (p == null)
					return false;
				int arg = p.GetSequence ();
				prev = prev.Previous;
				while (null != prev) {
					// look for a STOBJ instruction and compare the objects
					if (prev.OpCode.Code == Code.Stobj) {
						prev = prev.TraceBack (method);
						p = prev.GetParameter (method);
						return (p == null) ? false : (arg == p.GetSequence ());
					}
					prev = prev.Previous;
				}
			} else {
				return IsGetNow (ins);
			}
			return false;
		}
		private static bool CheckUsage (MethodDefinition method, Instruction ins)
		{
			// track the two parameters given to DateTime.op_Substraction
			Instruction param1 = ins.TraceBack (method, -1);
			Instruction param2 = ins.TraceBack (method);
			return CheckParameters (method, param1) && CheckParameters (method, param2);
		}
		void CheckForOutParameters (MethodDefinition method, Instruction ins)
		{
			Instruction iref = ins.TraceBack (method);
			if (iref == null)
				return;
			ParameterDefinition p = iref.GetParameter (method);
			if ((p != null) && p.IsOut) {
				ins = ins.Previous;
				if (ins.IsLoadLocal ())
					Clear (method, ins);
			}
		}
        void CheckIndexOf(MethodDefinition method, MethodReference call, Instruction ins)
        {
            // check that first parameter is a string of length equal to one
            string p1 = GetString (ins.TraceBack (method, -1));
            if (p1.Length != 1)
                return;

            IList<ParameterDefinition> pdc = call.Parameters;
            int last = pdc.Count;
            if (!pdc [last - 1].ParameterType.IsNamed ("System", "StringComparison")) {
                // confidence is normal because it's possible that the code expects a
                // culture sensitive comparison (but that will break in .NET 4).
                Report (method, ins, Confidence.Normal, call, p1);
                return;
            }

            // we try to find out what's the StringComparison
            Instruction sc = ins.TraceBack (method, -last);
            switch (sc.OpCode.Code) {
            case Code.Ldc_I4_4:
                // if it's StringComparison.Ordinal (4) then it's identical to what a Char would do
                Report (method, ins, Confidence.High, call, p1);
                break;
            case Code.Ldc_I4_5:
                // if it's StringComparison.OrdinalIgnoreCase (5) then it's identical as long as the Char is not case sensitive
                if (p1 == p1.ToLowerInvariant () && p1 == p1.ToUpperInvariant ()) {
                    Report (method, ins, Confidence.High, call, p1);
                }
                break;
            }
            // otherwise the Char overload is not usable as a direct replacement
        }
		void CheckDisposeCalls (MethodDefinition method, Instruction ins)
		{
			Instruction instance = ins.TraceBack (method);
			if (instance == null)
				return;

			VariableDefinition v = instance.GetVariable (method);
			ulong index = v == null ? UInt64.MaxValue : (ulong) v.Index;
			if (v != null && locals.Get (index)) {
				if (!IsInsideFinallyBlock (method, ins)) {
					string msg = String.Format (CultureInfo.InvariantCulture,
						"Local {0}is not guaranteed to be disposed of.",
						GetFriendlyNameOrEmpty (v));
					Runner.Report (method, Severity.Medium, Confidence.Normal, msg);
				}
				locals.Clear (index);
			}
		}
		private void CheckCall (MethodDefinition method, Instruction ins)
		{
			MethodDefinition md = (ins.Operand as MethodReference).Resolve ();
			if (md == null)
				return;

			// no dereference possible by calling a static method
			if (!md.IsStatic) {
				Instruction instance = ins.TraceBack (method);
				CheckParameter (instance.GetParameter (method));
			}

			// if was pass one of our parameter to another call then
			// this new call "takes" responsability of the null check
			// note: not perfect but greatly reduce false positives
			if (!md.HasParameters)
				return;
			for (int i = 0; i < md.Parameters.Count; i++) {
				Instruction pi = ins.TraceBack (method, -(md.IsStatic ? 0 : 1 + i));
				if (pi == null)
					continue;
				// generic types will be be boxed, skip that
				if (pi.OpCode.Code == Code.Box)
					pi = pi.Previous;
				ParameterDefinition p = pi.GetParameter (method);
				if (p != null)
					has_null_check.Set (p.GetSequence ());
			}
		}
		bool CheckCallsToOtherInstances (MethodDefinition method, Instruction ins, MethodReference call)
		{
			Instruction p = ins.TraceBack (method, 0);
			if (p.Is (Code.Ldarg_0))
				return false;

			if (call.HasParameters) {
				for (int i = 1; i <= call.Parameters.Count; i++) {
					p = ins.TraceBack (method, -i);
					Clear (method, p);
				}
			}
			return true;
		}
		private void CheckParameters (IMethodSignature concat, MethodDefinition caller, Instruction ins)
		{
			// check for boxed (likely char, but could be other types too) on any parameter
			for (int i = 0; i < concat.Parameters.Count; i++) {
				Instruction source = ins.TraceBack (caller, -i);
				if ((source == null) || (source.OpCode.Code != Code.Box))
					continue;
				ReportBoxing (caller, source, Confidence.High);
			}
		}
		static string CheckDoubleAssignementOnInstanceFields (MethodDefinition method, Instruction ins, Instruction next)
		{
			// for an instance fiels the pattern is more complex because we must check that we're assigning to the same instance
			// first we go forward: DUP, STLOC, STFLD, LDLOC, STFLD

			Instruction load = next.Next;
			if ((load == null) || !load.IsLoadLocal ())
				return String.Empty;

			// check that this is the same variable
			VariableDefinition vd1 = ins.GetVariable (method);
			VariableDefinition vd2 = load.GetVariable (method);
			if (vd1.Index != vd2.Index)
				return String.Empty;

			Instruction stfld = load.Next;
			if ((stfld == null) || (stfld.OpCode.Code != Code.Stfld))
				return String.Empty;

			// check that we're assigning the same field twice
			FieldReference fd1 = (next.Operand as FieldReference);
			FieldReference fd2 = (stfld.Operand as FieldReference);
			if (fd1.MetadataToken.RID != fd2.MetadataToken.RID)
				return String.Empty;

			// backward: DUP, (possible CONV), LD (value to be duplicated), LD instance, LD instance
			if (stfld.TraceBack (method).GetOperand (method) != next.TraceBack (method).GetOperand (method))
				return String.Empty;

			return String.Format ("Instance field '{0}' on same variable '{1}'.", fd1.Name, vd1.Name);
		}
        static string GetKey(MethodDefinition caller, MethodDefinition callee, Instruction ins)
        {
            if (callee.IsStatic)
                return callee.GetFullName ();

            IMetadataTokenProvider chain = callee;
            Instruction instance = ins.TraceBack (caller);

            StringBuilder sb = new StringBuilder ();
            while (instance != null) {
                MemberReference mr = (chain as MemberReference);
                if (mr == null)
                    sb.Append (chain.ToString ()); // ?? "null")
                else
                    sb.Append (mr.GetFullName ());
                sb.Append ('.');
                chain = (instance.Operand as IMetadataTokenProvider);
                if (chain == null) {
                    sb.Append (instance.GetOperand (caller));
                    break;
                }
                instance = instance.TraceBack (caller);
            }
            if (chain != null)
                sb.Append (chain.ToString ());
            return sb.ToString ();
        }
		void CheckCall (MethodDefinition method, Instruction ins, MethodReference call)
		{
			if (null == call) //resolution did not work
				return;
			if (!call.HasParameters)
				return;
			string tname = call.DeclaringType.FullName;
			if (tname != RegexClass && tname != ValidatorClass)
				return;

			MethodDefinition mdef = call.Resolve ();
			if (null == mdef)
				return;
			//check only constructors and static non-property methods
			if (!mdef.IsConstructor && (mdef.HasThis || mdef.IsProperty ()))
				return;

			foreach (ParameterDefinition p in mdef.Parameters) {
				string pname = p.Name;
				if ((pname == "pattern" || pname == "regex") && p.ParameterType.FullName == "System.String") {
					Instruction ld = ins.TraceBack (method, -(call.HasThis ? 0 : -1 + p.GetSequence ()));
					if (ld != null)
						CheckArguments (method, ins, ld);
					return;
				}
			}
		}