@Override public void visitClassContext(ClassContext classContext) { if (!enabled()) { return; } JavaClass jClass = classContext.getJavaClass(); XClass xClass = classContext.getXClass(); try { if (!isJunit3TestCase(xClass)) { return; } if ((jClass.getAccessFlags() & ACC_ABSTRACT) == 0) { if (!hasTestMethods(jClass)) { bugReporter.reportBug( new BugInstance(this, "IJU_NO_TESTS", LOW_PRIORITY).addClass(jClass)); } } directChildOfTestCase = "junit.framework.TestCase".equals(jClass.getSuperclassName()); jClass.accept(this); } catch (ClassNotFoundException cnfe) { bugReporter.reportMissingClass(cnfe); } }
@Override public void visitClassContext(ClassContext classContext) { JavaClass javaClass = classContext.getJavaClass(); // The class extends WebChromeClient boolean isWebChromeClient = InterfaceUtils.isSubtype(javaClass, "android.webkit.WebChromeClient"); // Not the target of this detector if (!isWebChromeClient) return; Method[] methodList = javaClass.getMethods(); for (Method m : methodList) { MethodGen methodGen = classContext.getMethodGen(m); if (DEBUG) System.out.println(">>> Method: " + m.getName()); // The presence of onGeolocationPermissionsShowPrompt is not enforce for the moment if (!m.getName().equals("onGeolocationPermissionsShowPrompt")) { continue; } // Since the logic implemented need to be analyze by a human, all implementation will be // flagged. bugReporter.reportBug( new BugInstance(this, ANDROID_GEOLOCATION_TYPE, Priorities.NORMAL_PRIORITY) // .addClassAndMethod(javaClass, m)); } }
@Override public void visitClassContext(ClassContext classContext) { if (!testingEnabled) { return; } analysisContext = AnalysisContext.currentAnalysisContext(); Method[] methodList = classContext.getJavaClass().getMethods(); for (Method method : methodList) { if (method.getCode() == null) { continue; } try { analyzeMethod(classContext, method); } catch (CFGBuilderException e) { bugReporter.logError( "Error checking for infinite recursive loop in " + SignatureConverter.convertMethodSignature(classContext.getJavaClass(), method), e); } catch (DataflowAnalysisException e) { bugReporter.logError( "Error checking for infinite recursive loop in " + SignatureConverter.convertMethodSignature(classContext.getJavaClass(), method), e); } } }
/** * @param classContext * @param method */ private void analyzeMethod(ClassContext classContext, Method method) throws MethodUnprofitableException, CFGBuilderException, DataflowAnalysisException { if (method.isSynthetic() || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE) return; CFG cfg = classContext.getCFG(method); TypeDataflow typeDataflow = classContext.getTypeDataflow(method); ConstantPoolGen constantPoolGen = classContext.getConstantPoolGen(); locationLoop: for (Iterator<Location> iter = cfg.locationIterator(); iter.hasNext(); ) { Location location = iter.next(); InstructionHandle handle = location.getHandle(); Instruction ins = handle.getInstruction(); // Only consider invoke instructions if (!(ins instanceof InvokeInstruction)) continue; if (ins instanceof INVOKEINTERFACE) continue; InvokeInstruction inv = (InvokeInstruction) ins; TypeFrame frame = typeDataflow.getFactAtLocation(location); String methodName = inv.getMethodName(constantPoolGen); if (methodName.toLowerCase().indexOf("unsupported") >= 0) continue; String methodSig = inv.getSignature(constantPoolGen); if (methodSig.equals("()Ljava/lang/UnsupportedOperationException;")) continue; Set<XMethod> targets; try { targets = Hierarchy2.resolveMethodCallTargets(inv, frame, constantPoolGen); } catch (ClassNotFoundException e) { AnalysisContext.reportMissingClass(e); continue locationLoop; } if (targets.isEmpty()) continue locationLoop; int priority = targets.size() == 1 ? Priorities.HIGH_PRIORITY : Priorities.NORMAL_PRIORITY; for (XMethod m : targets) { if (!m.isUnsupported()) continue locationLoop; XClass xc = AnalysisContext.currentXFactory().getXClass(m.getClassDescriptor()); if (!(inv instanceof INVOKESTATIC) && !(m.isFinal() || xc.isFinal())) priority = Priorities.NORMAL_PRIORITY; if (xc == null || xc.isAbstract()) { try { if (!AnalysisContext.currentAnalysisContext() .getSubtypes2() .hasSubtypes(m.getClassDescriptor())) continue locationLoop; } catch (ClassNotFoundException e) { AnalysisContext.reportMissingClass(e); continue locationLoop; } } } BugInstance bug = new BugInstance(this, "DMI_UNSUPPORTED_METHOD", priority) .addClassAndMethod(classContext.getJavaClass(), method) .addCalledMethod(constantPoolGen, inv) .addSourceLine(classContext, method, location); bugReporter.reportBug(bug); } }
@Override public void visitClassContext(ClassContext classContext) { ConstantPoolGen cpg = classContext.getConstantPoolGen(); List<InjectionSource> selectedSources = new ArrayList<InjectionSource>(); for (InjectionSource source : getInjectionSource()) { if (source.isCandidate(cpg)) { selectedSources.add(source); } } if (selectedSources.isEmpty()) { // return; // analysis still must be requested } for (Method method : classContext.getMethodsInCallOrder()) { MethodGen methodGen = classContext.getMethodGen(method); if (methodGen == null) { continue; } try { analyzeMethod(classContext, method, selectedSources); } catch (CheckedAnalysisException e) { logException(classContext, method, e); } catch (RuntimeException e) { logException(classContext, method, e); } } }
@Override public void visitClassContext(ClassContext classContext) { JavaClass javaClass = classContext.getJavaClass(); Method[] methodList = javaClass.getMethods(); for (Method m : methodList) { MethodGen methodGen = classContext.getMethodGen(m); if (DEBUG) { System.out.println(">>> Method: " + m.getName()); } // To suspect that an invalid String representation is being build, // we identify the construction of a MessageDigest and // the use of a function that trim leading 0. boolean invokeMessageDigest = false; boolean invokeToHexString = false; ConstantPoolGen cpg = classContext.getConstantPoolGen(); if (methodGen == null || methodGen.getInstructionList() == null) { continue; // No instruction .. nothing to do } for (Iterator itIns = methodGen.getInstructionList().iterator(); itIns.hasNext(); ) { Instruction inst = ((InstructionHandle) itIns.next()).getInstruction(); if (DEBUG) { ByteCode.printOpCode(inst, cpg); } if (inst instanceof INVOKEVIRTUAL) { // MessageDigest.digest is called INVOKEVIRTUAL invoke = (INVOKEVIRTUAL) inst; if ("java.security.MessageDigest".equals(invoke.getClassName(cpg)) && "digest".equals(invoke.getMethodName(cpg))) { invokeMessageDigest = true; } } else if (inst instanceof INVOKESTATIC && invokeMessageDigest) { // The conversion must occurs after the digest was created INVOKESTATIC invoke = (INVOKESTATIC) inst; if ("java.lang.Integer".equals(invoke.getClassName(cpg)) && "toHexString".equals(invoke.getMethodName(cpg))) { invokeToHexString = true; } } } if (invokeMessageDigest && invokeToHexString) { bugReporter.reportBug( new BugInstance(this, BAD_HEXA_CONVERSION_TYPE, Priorities.NORMAL_PRIORITY) // .addClassAndMethod(javaClass, m)); } } }
/** * overrides the visitor to initialize and tear down the opcode stack * * @param classContext the context object of the currently parsed class */ @Override public void visitClassContext(ClassContext classContext) { try { String clsName = classContext.getJavaClass().getClassName(); isInnerClass = clsName.contains("$"); clsSignature = SignatureUtils.classToSignature(clsName); stack = new OpcodeStack(); localSpecialObjects = new HashMap<>(); fieldSpecialObjects = new HashMap<>(); super.visitClassContext(classContext); if (!isInnerClass && !fieldSpecialObjects.isEmpty()) { for (Map.Entry<String, String> entry : fieldSpecialObjects.entrySet()) { String fieldName = entry.getKey(); String signature = entry.getValue(); bugReporter.reportBug( makeFieldBugInstance().addClass(this).addField(clsName, fieldName, signature, false)); } } } finally { stack = null; localSpecialObjects = null; fieldSpecialObjects = null; } }
/** * The detector is only meaningful for Java5 class libraries. * * @param classContext the context object that holds the JavaClass parsed */ @Override public void visitClassContext(ClassContext classContext) { int majorVersion = classContext.getJavaClass().getMajor(); if (majorVersion >= MAJOR_1_5) { super.visitClassContext(classContext); } }
private static Iterator<Location> getLocationIterator(ClassContext classContext, Method method) throws CheckedAnalysisException { try { return classContext.getCFG(method).locationIterator(); } catch (CFGBuilderException ex) { throw new CheckedAnalysisException("cannot get control flow graph", ex); } }
/** Use this to screen out methods that do not contain invocations. */ public boolean prescreen(ClassContext classContext, Method method) { BitSet bytecodeSet = classContext.getBytecodeSet(method); return bytecodeSet != null && (bytecodeSet.get(Constants.INVOKEINTERFACE) || bytecodeSet.get(Constants.INVOKEVIRTUAL) || bytecodeSet.get(Constants.INVOKESPECIAL) || bytecodeSet.get(Constants.INVOKESTATIC) || bytecodeSet.get(Constants.INVOKENONVIRTUAL)); }
@Override public void visitClassContext(ClassContext context) { try { stack = new OpcodeStack(); clsName = context.getJavaClass().getClassName(); super.visitClassContext(context); } finally { stack = null; } }
public static @CheckForNull LocalVariableAnnotation findMatchingIgnoredParameter( ClassContext classContext, Method method, String name, String signature) { try { Dataflow<BitSet, LiveLocalStoreAnalysis> llsaDataflow = classContext.getLiveLocalStoreDataflow(method); CFG cfg; cfg = classContext.getCFG(method); LocalVariableAnnotation match = null; int lowestCost = Integer.MAX_VALUE; BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry()); int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature()); int startIndex = 0; if (!method.isStatic()) startIndex = 1; SignatureParser parser = new SignatureParser(method.getSignature()); Iterator<String> signatureIterator = parser.parameterSignatureIterator(); for (int i = startIndex; i < localsThatAreParameters + startIndex; i++) { String sig = signatureIterator.next(); if (!liveStoreSetAtEntry.get(i) && signature.equals(sig)) { // parameter isn't live and signatures match LocalVariableAnnotation potentialMatch = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0); potentialMatch.setDescription(DID_YOU_MEAN_ROLE); if (!potentialMatch.isNamed()) return potentialMatch; int distance = EditDistance.editDistance(name, potentialMatch.getName()); if (distance < lowestCost) { match = potentialMatch; match.setDescription(DID_YOU_MEAN_ROLE); lowestCost = distance; } else if (distance == lowestCost) { // not unique best match match = null; } } } return match; } catch (DataflowAnalysisException e) { AnalysisContext.logError("", e); } catch (CFGBuilderException e) { AnalysisContext.logError("", e); } return null; }
private void analyzeMethod( ClassContext classContext, Method method, Collection<InjectionSource> selectedSources) throws DataflowAnalysisException, CheckedAnalysisException { TaintDataflow dataflow = getTaintDataFlow(classContext, method); ConstantPoolGen cpg = classContext.getConstantPoolGen(); String currentMethod = getFullMethodName(classContext.getMethodGen(method)); for (Iterator<Location> i = getLocationIterator(classContext, method); i.hasNext(); ) { Location location = i.next(); InstructionHandle handle = location.getHandle(); Instruction instruction = handle.getInstruction(); if (!(instruction instanceof InvokeInstruction)) { continue; } InvokeInstruction invoke = (InvokeInstruction) instruction; TaintFrame fact = dataflow.getFactAtLocation(location); assert fact != null; if (!fact.isValid()) { continue; } SourceLineAnnotation sourceLine = SourceLineAnnotation.fromVisitedInstruction(classContext, method, handle); checkTaintSink(getFullMethodName(cpg, invoke), fact, sourceLine, currentMethod); InjectionPoint injectionPoint = getInjectionPoint(invoke, cpg, handle, selectedSources); for (int offset : injectionPoint.getInjectableArguments()) { Taint parameterTaint = fact.getStackValue(offset); int priority = getPriority(parameterTaint); if (priority == Priorities.IGNORE_PRIORITY) { continue; } BugInstance bugInstance = new BugInstance(this, injectionPoint.getBugType(), priority); bugInstance.addClassAndMethod(classContext.getJavaClass(), method); bugInstance.addSourceLine(sourceLine); if (injectionPoint.getInjectableMethod() != null) { bugInstance.addString(injectionPoint.getInjectableMethod()); } reportBug(bugInstance, parameterTaint, currentMethod); } } }
/** * implements the visitor to create and clear the stack and syncRegs * * @param classContext the context object of the currently parsed class */ @Override public void visitClassContext(ClassContext classContext) { try { stack = new OpcodeStack(); syncRegs = new HashMap<Integer, CollectionRegInfo>(); classVersion = classContext.getJavaClass().getMajor(); super.visitClassContext(classContext); } finally { stack = null; syncRegs = null; } }
@Override public void visitClassContext(ClassContext classContext) { try { JavaClass cls = classContext.getJavaClass(); if (cls.getMajor() >= MAJOR_1_4) { stack = new OpcodeStack(); regValueType = new HashMap<Integer, State>(); super.visitClassContext(classContext); } } finally { stack = null; regValueType = null; } }
public void visitClassContext(ClassContext classContext) { JavaClass jclass = classContext.getJavaClass(); for (Method method : jclass.getMethods()) { XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method); ParameterProperty nonnullParameters = AnalysisContext.currentAnalysisContext() .getUnconditionalDerefParamDatabase() .getProperty(xmethod.getMethodDescriptor()); if (nonnullParameters != null) { for (int p : nonnullParameters.iterable()) { TypeQualifierAnnotation directTypeQualifierAnnotation = TypeQualifierApplications.getDirectTypeQualifierAnnotation( xmethod, p, nonnullTypeQualifierValue); if (directTypeQualifierAnnotation != null && directTypeQualifierAnnotation.when == When.UNKNOWN) { // // The LocalVariableAnnotation is constructed using the local variable // number of the parameter, not the parameter number. // int paramLocal = xmethod.isStatic() ? p : p + 1; reporter.reportBug( new BugInstance( this, "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE", NORMAL_PRIORITY) .addClassAndMethod(jclass, method) .add( LocalVariableAnnotation.getParameterLocalVariableAnnotation( method, paramLocal))); } } } } }
/** * implements the detector to collect the super classes * * @param classContext the context object for the currently parsed class */ @Override public void visitClassContext(ClassContext classContext) { try { clsContext = classContext; JavaClass cls = classContext.getJavaClass(); if (cls.isInterface()) return; superClasses = cls.getSuperClasses(); cls.accept(this); } catch (ClassNotFoundException cnfe) { bugReporter.reportMissingClass(cnfe); } finally { clsContext = null; superClasses = null; } }
@Override public void visitClassContext(ClassContext classContext) { if (appletClass == null) { return; } JavaClass cls = classContext.getJavaClass(); try { if (cls.instanceOf(appletClass)) { cls.accept(this); } } catch (ClassNotFoundException cnfe) { bugReporter.reportMissingClass(cnfe); } }
public void visitClassContext(ClassContext classContext) { JavaClass javaClass = classContext.getJavaClass(); Method[] methodList = javaClass.getMethods(); for (Method method : methodList) { if (method.getCode() == null) continue; try { analyzeMethod(classContext, method); } catch (MethodUnprofitableException e) { assert true; // move along; nothing to see } catch (CFGBuilderException e) { String msg = "Detector " + this.getClass().getName() + " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature(); bugReporter.logError(msg, e); } catch (DataflowAnalysisException e) { String msg = "Detector " + this.getClass().getName() + " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature(); bugReporter.logError(msg, e); } } }
private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException { if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Const.ACC_BRIDGE) == Const.ACC_BRIDGE) { return; } CFG cfg = classContext.getCFG(method); ConstantPoolGen cpg = classContext.getConstantPoolGen(); TypeDataflow typeDataflow = classContext.getTypeDataflow(method); for (Iterator<BasicBlock> i = cfg.blockIterator(); i.hasNext(); ) { BasicBlock basicBlock = i.next(); // Check if it's a method invocation. if (!basicBlock.isExceptionThrower()) { continue; } InstructionHandle thrower = basicBlock.getExceptionThrower(); Instruction ins = thrower.getInstruction(); if (!(ins instanceof InvokeInstruction)) { continue; } InvokeInstruction inv = (InvokeInstruction) ins; boolean foundThrower = false; boolean foundNonThrower = false; if (inv instanceof INVOKEINTERFACE) { continue; } String className = inv.getClassName(cpg); Location loc = new Location(thrower, basicBlock); TypeFrame typeFrame = typeDataflow.getFactAtLocation(loc); XMethod primaryXMethod = XFactory.createXMethod(inv, cpg); // if (primaryXMethod.isAbstract()) continue; Set<XMethod> targetSet = null; try { if (className.startsWith("[")) { continue; } String methodSig = inv.getSignature(cpg); if (!methodSig.endsWith("V")) { continue; } targetSet = Hierarchy2.resolveMethodCallTargets(inv, typeFrame, cpg); for (XMethod xMethod : targetSet) { if (DEBUG) { System.out.println("\tFound " + xMethod); } boolean isUnconditionalThrower = xMethod.isUnconditionalThrower() && !xMethod.isUnsupported() && !xMethod.isSynthetic(); if (isUnconditionalThrower) { foundThrower = true; if (DEBUG) { System.out.println("Found thrower"); } } else { foundNonThrower = true; if (DEBUG) { System.out.println("Found non thrower"); } } } } catch (ClassNotFoundException e) { analysisContext.getLookupFailureCallback().reportMissingClass(e); } boolean newResult = foundThrower && !foundNonThrower; if (newResult) { bugReporter.reportBug( new BugInstance(this, "TESTING", Priorities.NORMAL_PRIORITY) .addClassAndMethod(classContext.getJavaClass(), method) .addString("Call to method that always throws Exception") .addMethod(primaryXMethod) .describe(MethodAnnotation.METHOD_CALLED) .addSourceLine(classContext, method, loc)); } } }
private void logException(ClassContext classContext, Method method, Exception ex) { bugReporter.logError( "Exception while analyzing " + classContext.getFullyQualifiedMethodName(method), ex); }
private static TaintDataflow getTaintDataFlow(ClassContext classContext, Method method) throws CheckedAnalysisException { MethodDescriptor descriptor = BCELUtil.getMethodDescriptor(classContext.getJavaClass(), method); return Global.getAnalysisCache().getMethodAnalysis(TaintDataflow.class, descriptor); }
public void visitClassContext(ClassContext classContext) { classContext.getJavaClass().accept(this); }
private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException { if (isSynthetic(method) || !prescreen(classContext, method)) return; XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method); if (xmethod.isSynthetic()) return; BugAccumulator accumulator = new BugAccumulator(bugReporter); CFG cfg = classContext.getCFG(method); TypeDataflow typeDataflow = classContext.getTypeDataflow(method); ValueNumberDataflow vnDataflow = classContext.getValueNumberDataflow(method); ConstantPoolGen cpg = classContext.getConstantPoolGen(); MethodGen methodGen = classContext.getMethodGen(method); if (methodGen == null) return; String fullMethodName = methodGen.getClassName() + "." + methodGen.getName(); String sourceFile = classContext.getJavaClass().getSourceFileName(); if (DEBUG) { System.out.println("\n" + fullMethodName); } // Process each instruction for (Iterator<Location> iter = cfg.locationIterator(); iter.hasNext(); ) { Location location = iter.next(); InstructionHandle handle = location.getHandle(); Instruction ins = handle.getInstruction(); // Only consider invoke instructions if (!(ins instanceof InvokeInstruction)) continue; InvokeInstruction inv = (InvokeInstruction) ins; XMethod invokedMethod = XFactory.createXMethod(inv, cpg); String invokedMethodName = invokedMethod.getName(); String argSignature = invokedMethod.getSignature(); argSignature = argSignature.substring(0, argSignature.indexOf(')') + 1); String call = invokedMethodName + argSignature; SignatureParser sigParser = new SignatureParser(inv.getSignature(cpg)); Collection<Info> collection = callMap.get(call); if (!callMap.containsKey(call)) continue; for (Info info : collection) { Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2(); if (DEBUG) System.out.println( "at " + handle.getPosition() + " Checking call to " + info.interfaceForCall + " : " + invokedMethod); try { if (!subtypes2.isSubtype(invokedMethod.getClassDescriptor(), info.interfaceForCall)) continue; } catch (ClassNotFoundException e) { if (info.interfaceForCall.getClassName().equals("java/util/Collection") && invokedMethod.getClassName().equals("com.google.common.collect.Multiset")) { assert true; // we know this is OK without needing to find definition of Multiset } else { AnalysisContext.reportMissingClass(e); continue; } } boolean allMethod; int typeArgument; if (info.typeIndex >= 0) { allMethod = false; typeArgument = info.typeIndex; } else { allMethod = true; typeArgument = -(1 + info.typeIndex); } int pos = info.argumentIndex; int lhsPos; if (inv instanceof INVOKESTATIC) lhsPos = sigParser.getSlotsFromTopOfStackForParameter(0); else lhsPos = sigParser.getTotalArgumentSize(); int stackPos = sigParser.getSlotsFromTopOfStackForParameter(pos); TypeFrame frame = typeDataflow.getFactAtLocation(location); if (!frame.isValid()) { // This basic block is probably dead continue; } Type operandType = frame.getStackValue(stackPos); if (operandType.equals(TopType.instance())) { // unreachable continue; } if (operandType.equals(NullType.instance())) { // ignore continue; } ValueNumberFrame vnFrame = vnDataflow.getFactAtLocation(location); if (!vnFrame.isValid()) { AnalysisContext.logError("Invalid value number frame in " + xmethod); continue; } ValueNumber objectVN = vnFrame.getStackValue(lhsPos); ValueNumber argVN = vnFrame.getStackValue(stackPos); if (objectVN.equals(argVN)) { String bugPattern = "DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES"; int priority = HIGH_PRIORITY; if (invokedMethodName.equals("removeAll")) { bugPattern = "DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION"; priority = NORMAL_PRIORITY; } else if (invokedMethodName.endsWith("All")) { bugPattern = "DMI_VACUOUS_SELF_COLLECTION_CALL"; priority = NORMAL_PRIORITY; } if (invokedMethodName.startsWith("contains")) { InstructionHandle next = handle.getNext(); if (next != null) { Instruction nextIns = next.getInstruction(); if (nextIns instanceof InvokeInstruction) { XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg); if (nextMethod.getName().equals("assertFalse")) continue; } } } accumulator.accumulateBug( new BugInstance(this, bugPattern, priority) .addClassAndMethod(methodGen, sourceFile) .addCalledMethod(methodGen, (InvokeInstruction) ins) .addOptionalAnnotation( ValueNumberSourceInfo.findAnnotationFromValueNumber( method, location, objectVN, vnFrame, "INVOKED_ON")), SourceLineAnnotation.fromVisitedInstruction( classContext, methodGen, sourceFile, handle)); } // Only consider generic... Type objectType = frame.getStackValue(lhsPos); if (!(objectType instanceof GenericObjectType)) continue; GenericObjectType operand = (GenericObjectType) objectType; int expectedTypeParameters = 1; String simpleName = info.interfaceForCall.getSimpleName(); if (simpleName.toLowerCase().endsWith("map") || simpleName.equals("Hashtable")) expectedTypeParameters = 2; else if (simpleName.equals("Table")) expectedTypeParameters = 3; // ... containers if (!operand.hasParameters()) continue; if (operand.getNumParameters() != expectedTypeParameters) continue; ClassDescriptor operandClass = DescriptorFactory.getClassDescriptor(operand); if (!isGenericCollection(operandClass)) continue; if (expectedTypeParameters == 2 && Subtypes2.instanceOf(operandClass, Map.class) && !TypeFrameModelingVisitor.isStraightGenericMap(operandClass)) continue; Type expectedType; if (allMethod) expectedType = operand; else expectedType = operand.getParameterAt(typeArgument); Type actualType = frame.getStackValue(stackPos); Type equalsType = actualType; if (allMethod) { if (!(actualType instanceof GenericObjectType)) { continue; } equalsType = ((GenericObjectType) actualType).getParameterAt(typeArgument); } IncompatibleTypes matchResult = compareTypes(expectedType, actualType, allMethod); boolean parmIsObject = expectedType.getSignature().equals("Ljava/lang/Object;"); boolean selfOperation = !allMethod && operand.equals(actualType) && !parmIsObject; if (!allMethod && !parmIsObject && actualType instanceof GenericObjectType) { GenericObjectType p2 = (GenericObjectType) actualType; List<? extends ReferenceType> parameters = p2.getParameters(); if (parameters != null && parameters.equals(operand.getParameters())) selfOperation = true; } if (!selfOperation && (matchResult == IncompatibleTypes.SEEMS_OK || matchResult.getPriority() == Priorities.IGNORE_PRIORITY)) continue; if (invokedMethodName.startsWith("contains") || invokedMethodName.equals("remove")) { InstructionHandle next = handle.getNext(); if (next != null) { Instruction nextIns = next.getInstruction(); if (nextIns instanceof InvokeInstruction) { XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg); if (nextMethod.getName().equals("assertFalse")) continue; } } } else if (invokedMethodName.equals("get") || invokedMethodName.equals("remove")) { InstructionHandle next = handle.getNext(); if (next != null) { Instruction nextIns = next.getInstruction(); if (nextIns instanceof InvokeInstruction) { XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg); if (nextMethod.getName().equals("assertNull")) continue; } } } boolean noisy = false; if (invokedMethodName.equals("get")) { UnconditionalValueDerefDataflow unconditionalValueDerefDataflow = classContext.getUnconditionalValueDerefDataflow(method); UnconditionalValueDerefSet unconditionalDeref = unconditionalValueDerefDataflow.getFactAtLocation(location); ValueNumberFrame vnAfter = vnDataflow.getFactAfterLocation(location); ValueNumber top = vnAfter.getTopValue(); noisy = unconditionalDeref.getValueNumbersThatAreUnconditionallyDereferenced().contains(top); } // Prepare bug report SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction( classContext, methodGen, sourceFile, handle); // Report a bug that mentions each of the failed arguments in // matches if (expectedType instanceof GenericObjectType) expectedType = ((GenericObjectType) expectedType).getUpperBound(); int priority = matchResult.getPriority(); if (!operandClass.getClassName().startsWith("java/util") && priority == Priorities.HIGH_PRIORITY) priority = Math.max(priority, Priorities.NORMAL_PRIORITY); if (TestCaseDetector.likelyTestCase(xmethod)) priority = Math.max(priority, Priorities.NORMAL_PRIORITY); else if (selfOperation) priority = Priorities.HIGH_PRIORITY; ClassDescriptor expectedClassDescriptor = DescriptorFactory.createClassOrObjectDescriptorFromSignature( expectedType.getSignature()); ClassDescriptor actualClassDescriptor = DescriptorFactory.createClassOrObjectDescriptorFromSignature(equalsType.getSignature()); ClassSummary classSummary = AnalysisContext.currentAnalysisContext().getClassSummary(); Set<XMethod> targets = null; try { targets = Hierarchy2.resolveVirtualMethodCallTargets( actualClassDescriptor, "equals", "(Ljava/lang/Object;)Z", false, false); boolean allOk = targets.size() > 0; for (XMethod m2 : targets) if (!classSummary.mightBeEqualTo(m2.getClassDescriptor(), expectedClassDescriptor)) allOk = false; if (allOk) priority += 2; } catch (ClassNotFoundException e) { AnalysisContext.reportMissingClass(e); } String bugPattern = "GC_UNRELATED_TYPES"; BugInstance bug = new BugInstance(this, bugPattern, priority) .addClassAndMethod(methodGen, sourceFile) .addFoundAndExpectedType(actualType, expectedType) .addCalledMethod(methodGen, (InvokeInstruction) ins) .addOptionalAnnotation( ValueNumberSourceInfo.findAnnotationFromValueNumber( method, location, objectVN, vnFrame, "INVOKED_ON")) .addOptionalAnnotation( ValueNumberSourceInfo.findAnnotationFromValueNumber( method, location, argVN, vnFrame, "ARGUMENT")) .addEqualsMethodUsed(targets); if (noisy) { WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>(); propertySet.addProperty(GeneralWarningProperty.NOISY_BUG); propertySet.decorateBugInstance(bug); } accumulator.accumulateBug(bug, sourceLineAnnotation); } } accumulator.reportAccumulatedBugs(); }
@Override public void visitClassContext(ClassContext classContext) { this.classContext = classContext; classContext.getJavaClass().accept(this); }
public void meetInto( UnconditionalValueDerefSet fact, Edge edge, UnconditionalValueDerefSet result, boolean onlyEdge) { if (isExceptionEdge(edge) && !onlyEdge) { if (DEBUG) { System.out.println("Skipping exception edge"); } return; } ValueNumber knownNonnullOnBranch = null; // Edge transfer function if (isFactValid(fact)) { fact = propagateDerefSetsToMergeInputValues(fact, edge); if (invDataflow != null) { knownNonnullOnBranch = findValueKnownNonnullOnBranch(fact, edge); if (knownNonnullOnBranch != null) { fact = duplicateFact(fact); fact.clearDerefSet(knownNonnullOnBranch); } } } boolean isBackEdge = edge.isBackwardInBytecode(); Set<Integer> loopExitBranches = ClassContext.getLoopExitBranches(method, methodGen); assert loopExitBranches != null; boolean sourceIsTopOfLoop = edge.sourceIsTopOfLoop(loopExitBranches); if (sourceIsTopOfLoop && edge.getType() == EdgeTypes.FALL_THROUGH_EDGE) { isBackEdge = true; } /* if (false && (edge.getType() == EdgeTypes.IFCMP_EDGE || sourceIsTopOfLoop)) { System.out.println("Meet into " + edge); System.out.println(" foo2: " + sourceIsTopOfLoop); System.out.println(" getType: " + edge.getType()); System.out.println(" Backedge according to bytecode: " + isBackEdge); System.out.println(" Fact hashCode: " + System.identityHashCode(result)); System.out.println(" Initial fact: " + result); System.out.println(" Edge fact: " + fact); } */ if (result.isTop() || fact.isBottom()) { // Make result identical to other fact copy(fact, result); if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) { result.resultsFromBackEdge = true; } } else if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) { result.unionWith(fact, vnaDataflow.getAnalysis().getFactory()); result.resultsFromBackEdge = true; if (DEBUG) { System.out.println( "\n Forcing union of " + System.identityHashCode(result) + " due to backedge info"); System.out.println(" result: " + result); } } else if (result.isBottom() || fact.isTop()) { // No change in result fact } else { // Dataflow merge // (intersection of unconditional deref values) if (ASSUME_NONZERO_TRIP_LOOPS && result.resultsFromBackEdge) { result.backEdgeUpdateCount++; if (result.backEdgeUpdateCount < 10) { if (DEBUG) { System.out.println( "\n Union update of " + System.identityHashCode(result) + " due to backedge info"); } result.unionWith(fact, vnaDataflow.getAnalysis().getFactory()); return; } } result.mergeWith(fact, knownNonnullOnBranch, vnaDataflow.getAnalysis().getFactory()); if (DEBUG) { System.out.println(" updated: " + System.identityHashCode(result)); System.out.println(" result: " + result); } } if (DEBUG && isBackEdge && edge.getType() == EdgeTypes.IFCMP_EDGE) { System.out.println(" result: " + result); } }
@Override public void visitClassContext(ClassContext classContext) { classContext.getJavaClass().accept(this); flush(); }
/** * overrides the visitor to report on classes without toStrings that have fields * * @param classContext the context object of the currently parsed class */ @Override public void visitClassContext(ClassContext classContext) { JavaClass cls = classContext.getJavaClass(); if (cls.getPackageName().isEmpty()) { bugReporter.reportBug( new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_PACKAGE.name(), LOW_PRIORITY) .addClass(cls)); } if ((!cls.isAbstract()) && (!cls.isEnum()) && !cls.getClassName().contains("$") && !isTestClass(cls)) { try { boolean clsHasRuntimeAnnotation = classHasRuntimeVisibleAnnotation(cls); HEStatus heStatus = HEStatus.UNKNOWN; checkIDEGeneratedParmNames(cls); for (Field f : cls.getFields()) { if (!f.isStatic() && !f.isSynthetic()) { boolean fieldHasRuntimeAnnotation = fieldHasRuntimeVisibleAnnotation(f); if (!fieldHasRuntimeAnnotation) { /* only report one of these, so as not to flood the report */ if (!hasMethodInHierarchy(cls, "toString", "()Ljava/lang/String;")) { bugReporter.reportBug( new BugInstance( this, BugType.IMC_IMMATURE_CLASS_NO_TOSTRING.name(), LOW_PRIORITY) .addClass(cls)); return; } if (heStatus != HEStatus.NOT_NEEDED) { String fieldSig = f.getSignature(); if (fieldSig.startsWith("L")) { if (!fieldSig.startsWith("Ljava")) { JavaClass fieldClass = Repository.lookupClass(fieldSig.substring(1, fieldSig.length() - 1)); if (!hasMethodInHierarchy(fieldClass, "equals", "(Ljava/lang/Object)Z")) { heStatus = HEStatus.NOT_NEEDED; } } else if (!fieldSig.startsWith("Ljava/lang/") && !fieldSig.startsWith("Ljava/util/")) { heStatus = HEStatus.NOT_NEEDED; } } else if (!fieldSig.startsWith("[")) { heStatus = HEStatus.NEEDED; } } } else { heStatus = HEStatus.NOT_NEEDED; } } } if (!clsHasRuntimeAnnotation && (heStatus == HEStatus.NEEDED)) { if (!hasMethodInHierarchy(cls, "equals", "(Ljava/lang/Object;)Z")) { bugReporter.reportBug( new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_EQUALS.name(), LOW_PRIORITY) .addClass(cls)); } else if (!hasMethodInHierarchy(cls, "hashCode", "()I")) { bugReporter.reportBug( new BugInstance(this, BugType.IMC_IMMATURE_CLASS_NO_HASHCODE.name(), LOW_PRIORITY) .addClass(cls)); } } } catch (ClassNotFoundException cnfe) { bugReporter.reportMissingClass(cnfe); } } }