📄 findrefcomparison.java
字号:
this.propertySet = propertySet; this.location = location; } } private interface WarningDecorator { public void decorate(WarningWithProperties warn); } private void analyzeMethod(ClassContext classContext, final Method method) throws CFGBuilderException, DataflowAnalysisException { MethodGen methodGen = classContext.getMethodGen(method); if (methodGen == null) return; boolean sawCallToEquals = false; JavaClass jclass = classContext.getJavaClass(); ConstantPoolGen cpg = classContext.getConstantPoolGen(); // Enqueue all of the potential violations we find in the method. // Normally we'll only report the first highest-priority warning, // but if in relaxed mode or if REPORT_ALL_REF_COMPARISONS is set, // then we'll report everything. LinkedList<WarningWithProperties> refComparisonList = new LinkedList<WarningWithProperties>(); LinkedList<WarningWithProperties> stringComparisonList = new LinkedList<WarningWithProperties>(); CFG cfg = classContext.getCFG(method); DepthFirstSearch dfs = classContext.getDepthFirstSearch(method); ExceptionSetFactory exceptionSetFactory = classContext.getExceptionSetFactory(method); // Perform type analysis using our special type merger // (which handles String types specially, keeping track of // which ones appear to be dynamically created) RefComparisonTypeMerger typeMerger = new RefComparisonTypeMerger(bugReporter, exceptionSetFactory); RefComparisonTypeFrameModelingVisitor visitor = new RefComparisonTypeFrameModelingVisitor(methodGen.getConstantPool(), bugReporter); TypeAnalysis typeAnalysis = new TypeAnalysis(method, methodGen, cfg, dfs, typeMerger, visitor, bugReporter, exceptionSetFactory) { @Override public void initEntryFact(TypeFrame result) { super.initEntryFact(result); for(int i = 0; i < methodGen.getMaxLocals(); i++) { Type t = result.getValue(i); if (t.equals(ObjectType.STRING)) result.setValue(i, parameterStringTypeInstance); } } }; TypeDataflow typeDataflow = new TypeDataflow(cfg, typeAnalysis); typeDataflow.execute(); // Inspect Locations in the method for suspicious ref comparisons and calls to equals() for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) { Location location = i.next(); sawCallToEquals = inspectLocation( sawCallToEquals, jclass, cpg, method, methodGen, refComparisonList, stringComparisonList, visitor, typeDataflow, location); } // Add method-wide properties to BugInstances final boolean sawEquals = sawCallToEquals; decorateWarnings(stringComparisonList, new WarningDecorator(){ public void decorate(WarningWithProperties warn) { if (sawEquals) { warn.propertySet.addProperty(RefComparisonWarningProperty.SAW_CALL_TO_EQUALS); } if (false && !(method.isPublic() || method.isProtected())) { warn.propertySet.addProperty(RefComparisonWarningProperty.PRIVATE_METHOD); } } }); decorateWarnings(refComparisonList, new WarningDecorator() { public void decorate(WarningWithProperties warn) { if (sawEquals) { warn.propertySet.addProperty(RefComparisonWarningProperty.SAW_CALL_TO_EQUALS); } } }); // Report violations boolean relaxed = FindBugsAnalysisFeatures.isRelaxedMode(); reportBest(classContext, method, stringComparisonList, relaxed); reportBest(classContext, method, refComparisonList, relaxed); } private boolean inspectLocation( boolean sawCallToEquals, JavaClass jclass, ConstantPoolGen cpg, Method method, MethodGen methodGen, LinkedList<WarningWithProperties> refComparisonList, LinkedList<WarningWithProperties> stringComparisonList, RefComparisonTypeFrameModelingVisitor visitor, TypeDataflow typeDataflow, Location location) throws DataflowAnalysisException { Instruction ins = location.getHandle().getInstruction(); short opcode = ins.getOpcode(); if (opcode == Constants.IF_ACMPEQ || opcode == Constants.IF_ACMPNE) { checkRefComparison( location, jclass, methodGen, visitor, typeDataflow, stringComparisonList, refComparisonList); } else if (invokeInstanceSet.get(opcode)) { InvokeInstruction inv = (InvokeInstruction) ins; String methodName = inv.getMethodName(cpg); String methodSig = inv.getSignature(cpg); if (isEqualsMethod(methodName, methodSig)) { sawCallToEquals = true; checkEqualsComparison(location, jclass, method, methodGen, typeDataflow); } } return sawCallToEquals; } private void decorateWarnings( LinkedList<WarningWithProperties> stringComparisonList, WarningDecorator warningDecorator) { for (WarningWithProperties warn : stringComparisonList) { warningDecorator.decorate(warn); warn.instance.setPriority(warn.propertySet.computePriority(NORMAL_PRIORITY)); } } private void reportBest( ClassContext classContext, Method method, LinkedList<WarningWithProperties> warningList, boolean relaxed) { boolean reportAll = relaxed || REPORT_ALL_REF_COMPARISONS; WarningWithProperties best = null; for (WarningWithProperties warn : warningList) { if (best == null || warn.instance.getPriority() < best.instance.getPriority()) { best = warn; } if (reportAll) { if (relaxed) { // Add general warning properties WarningPropertyUtil.addPropertiesForLocation( warn.propertySet, classContext, method, warn.location); // Convert warning properties to bug properties warn.propertySet.decorateBugInstance(warn.instance); } bugReporter.reportBug(warn.instance); } } if (best != null && !reportAll) { bugReporter.reportBug(best.instance); } } private boolean isEqualsMethod(String methodName, String methodSig) { return (methodName.equals("equals") && methodSig.equals("(Ljava/lang/Object;)Z")) || (methodName.equals("equalIgnoreCases") && methodSig.equals("(Ljava/lang/String;)Z")); } private void checkRefComparison( Location location, JavaClass jclass, MethodGen methodGen, RefComparisonTypeFrameModelingVisitor visitor, TypeDataflow typeDataflow, List<WarningWithProperties> stringComparisonList, List<WarningWithProperties> refComparisonList) throws DataflowAnalysisException { InstructionHandle handle = location.getHandle(); TypeFrame frame = typeDataflow.getFactAtLocation(location); if (frame.getStackDepth() < 2) throw new DataflowAnalysisException("Stack underflow", methodGen, handle); int numSlots = frame.getNumSlots(); Type lhsType = frame.getValue(numSlots - 1); Type rhsType = frame.getValue(numSlots - 2); if (lhsType instanceof ReferenceType && rhsType instanceof ReferenceType) { String lhs = SignatureConverter.convert(lhsType.getSignature()); String rhs = SignatureConverter.convert(rhsType.getSignature()); if (!lhs.equals(rhs)) return; if (lhs.equals("java.lang.String")) { handleStringComparison(jclass, methodGen, visitor, stringComparisonList, location, lhsType, rhsType); } else if (suspiciousSet.contains(lhs)) { handleSuspiciousRefComparison(jclass, methodGen, refComparisonList, location, lhs); } } } private void handleStringComparison( JavaClass jclass, MethodGen methodGen, RefComparisonTypeFrameModelingVisitor visitor, List<WarningWithProperties> stringComparisonList, Location location, Type lhsType, Type rhsType) { if (DEBUG) System.out.println("String/String comparison at " + location.getHandle()); // Compute the priority: // - two static strings => do not report // - dynamic string and anything => high // - static string and unknown => medium // - all other cases => low // System.out.println("Compare " + lhsType + " == " + rhsType); byte type1 = lhsType.getType(); byte type2 = rhsType.getType(); String bugPattern = "ES_COMPARING_STRINGS_WITH_EQ"; // T1 T2 result // S S no-op // D ? high // ? D high // S ? normal // ? S normal WarningPropertySet propertySet = new WarningPropertySet(); if (type1 == T_STATIC_STRING && type2 == T_STATIC_STRING) { propertySet.addProperty(RefComparisonWarningProperty.COMPARE_STATIC_STRINGS); } else if (type1 == T_DYNAMIC_STRING || type2 == T_DYNAMIC_STRING) { propertySet.addProperty(RefComparisonWarningProperty.DYNAMIC_AND_UNKNOWN); } else if (type2 == T_PARAMETER_STRING || type1 == T_PARAMETER_STRING) { bugPattern = "ES_COMPARING_PARAMETER_STRING_WITH_EQ"; if (methodGen.isPublic() || methodGen.isProtected()) propertySet.addProperty(RefComparisonWarningProperty.STRING_PARAMETER_IN_PUBLIC_METHOD); else propertySet.addProperty(RefComparisonWarningProperty.STRING_PARAMETER); } else if (type1 == T_STATIC_STRING || type2 == T_STATIC_STRING) { propertySet.addProperty(RefComparisonWarningProperty.STATIC_AND_UNKNOWN); } else if (visitor.sawStringIntern()) { propertySet.addProperty(RefComparisonWarningProperty.SAW_INTERN); } String sourceFile = jclass.getSourceFileName(); BugInstance instance = new BugInstance(this, bugPattern, BASE_ES_PRIORITY) .addClassAndMethod(methodGen, sourceFile) .addType("Ljava/lang/String;").describe(TypeAnnotation.FOUND_ROLE) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()); WarningWithProperties warn = new WarningWithProperties(instance, propertySet, location); stringComparisonList.add(warn); } private void handleSuspiciousRefComparison( JavaClass jclass, MethodGen methodGen, List<WarningWithProperties> refComparisonList, Location location, String lhs) { String sourceFile = jclass.getSourceFileName(); BugInstance instance = new BugInstance(this, "RC_REF_COMPARISON", lhs.equals("java.lang.Boolean") ? NORMAL_PRIORITY : HIGH_PRIORITY) .addClassAndMethod(methodGen, sourceFile) .addType("L" + lhs.replace('.', '/')+";").describe(TypeAnnotation.FOUND_ROLE) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()); refComparisonList.add(new WarningWithProperties(instance, new WarningPropertySet(), location)); } private static boolean testLikeName(String name) { return name.toLowerCase().indexOf("test") >= 0; } private void checkEqualsComparison( Location location, JavaClass jclass, Method method, MethodGen methodGen, TypeDataflow typeDataflow) throws DataflowAnalysisException { InstructionHandle handle = location.getHandle(); String sourceFile = jclass.getSourceFileName(); TypeFrame frame = typeDataflow.getFactAtLocation(location); if (frame.getStackDepth() < 2) throw new DataflowAnalysisException("Stack underflow", methodGen, handle); int numSlots = frame.getNumSlots(); Type lhsType_ = frame.getValue(numSlots - 2); Type rhsType_ = frame.getValue(numSlots - 1); // Ignore top and bottom values if (lhsType_.getType() == T_TOP || lhsType_.getType() == T_BOTTOM || rhsType_.getType() == T_TOP || rhsType_.getType() == T_BOTTOM) return; boolean looksLikeTestCase = method.getName().startsWith("test") && method.isPublic() && method.getSignature().equals("()V") || testLikeName(jclass.getClassName())|| testLikeName(jclass.getSuperclassName()); int priorityModifier = 0; if (looksLikeTestCase) priorityModifier = 1; if (methodGen.getName().startsWith("test") && methodGen.getSignature().equals("()V")) { try { if (jclass.getSuperclassName().equals("junit.framework.TestCase") || Hierarchy.isSubtype(methodGen.getClassName(), "junit.framework.TestCase")) priorityModifier=2; } catch (ClassNotFoundException e) { AnalysisContext.reportMissingClass(e); } } if (!(lhsType_ instanceof ReferenceType) || !(rhsType_ instanceof ReferenceType)) { if (rhsType_.getType() == T_NULL) { // A literal null value was passed directly to equals(). if (!looksLikeTestCase) bugReporter.reportBug(new BugInstance(this, "EC_NULL_ARG", NORMAL_PRIORITY) .addClassAndMethod(methodGen, sourceFile) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle())); } else if (lhsType_.getType() == T_NULL) { // Hmm...in this case, equals() is being invoked on // a literal null value. This is really the // purview of FindNullDeref. So, we'll just do nothing. } else { bugReporter.logError("equals() used to compare non-object type(s) " + lhsType_ + " and " + rhsType_ + " in " + SignatureConverter.convertMethodSignature(methodGen) + " at " + location.getHandle()); } return; } if (lhsType_ instanceof ArrayType && rhsType_ instanceof ArrayType) bugReporter.reportBug(new BugInstance(this, "EC_BAD_ARRAY_COMPARE", NORMAL_PRIORITY) .addClassAndMethod(methodGen, sourceFile) .addFoundAndExpectedType(rhsType_.getSignature(), lhsType_.getSignature()) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()) ); IncompatibleTypes result = IncompatibleTypes.getPriorityForAssumingCompatible(lhsType_, rhsType_); if (result == IncompatibleTypes.ARRAY_AND_NON_ARRAY || result == IncompatibleTypes.ARRAY_AND_OBJECT) bugReporter.reportBug(new BugInstance(this, "EC_ARRAY_AND_NONARRAY", result.getPriority()) .addClassAndMethod(methodGen, sourceFile) .addFoundAndExpectedType(rhsType_.getSignature(), lhsType_.getSignature()) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()) ); else if (result == IncompatibleTypes.INCOMPATIBLE_CLASSES) { String lhsSig = lhsType_.getSignature(); String rhsSig = rhsType_.getSignature(); boolean core = lhsSig.startsWith("Ljava") && rhsSig.startsWith("Ljava"); if (core) { looksLikeTestCase = false; priorityModifier = 0; } if (!looksLikeTestCase) bugReporter.reportBug(new BugInstance(this, "EC_UNRELATED_TYPES", result.getPriority() + priorityModifier) .addClassAndMethod(methodGen, sourceFile) .addFoundAndExpectedType(rhsType_.getSignature(), lhsType_.getSignature()) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()) ); } else if (result == IncompatibleTypes.UNRELATED_CLASS_AND_INTERFACE) bugReporter.reportBug(new BugInstance(this, "EC_UNRELATED_CLASS_AND_INTERFACE", result.getPriority()) .addClassAndMethod(methodGen, sourceFile) .addFoundAndExpectedType(rhsType_.getSignature(), lhsType_.getSignature()) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()) ); else if (result == IncompatibleTypes.UNRELATED_INTERFACES) bugReporter.reportBug(new BugInstance(this, "EC_UNRELATED_INTERFACES", result.getPriority()) .addClassAndMethod(methodGen, sourceFile) .addFoundAndExpectedType(rhsType_.getSignature(), lhsType_.getSignature()) .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()) ); } public static void main(String[] argv) throws Exception { if (argv.length != 1) { System.err.println("Usage: " + FindRefComparison.class.getName() + " <class file>"); System.exit(1); } DataflowTestDriver<TypeFrame, TypeAnalysis> driver = new DataflowTestDriver<TypeFrame, TypeAnalysis>() { @Override public Dataflow<TypeFrame, TypeAnalysis> createDataflow(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException { RepositoryLookupFailureCallback lookupFailureCallback = classContext.getLookupFailureCallback(); MethodGen methodGen = classContext.getMethodGen(method); if (methodGen == null) throw new DataflowAnalysisException("Could not get methodGen for " + method.toString()); DepthFirstSearch dfs = classContext.getDepthFirstSearch(method); CFG cfg = classContext.getCFG(method); ExceptionSetFactory exceptionSetFactory = classContext.getExceptionSetFactory(method); TypeMerger typeMerger = new RefComparisonTypeMerger(lookupFailureCallback, exceptionSetFactory); TypeFrameModelingVisitor visitor = new RefComparisonTypeFrameModelingVisitor(methodGen.getConstantPool(), lookupFailureCallback); TypeAnalysis analysis = new TypeAnalysis(method, methodGen, cfg, dfs, typeMerger, visitor, lookupFailureCallback, exceptionSetFactory); Dataflow<TypeFrame, TypeAnalysis> dataflow = new Dataflow<TypeFrame, TypeAnalysis>(cfg, analysis); dataflow.execute(); return dataflow; } }; driver.execute(argv[0]); } public void report() { // do nothing }}//vim:ts=3
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -