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; } }
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; } } }