@Override public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, LockSet fact) throws DataflowAnalysisException { Instruction ins = handle.getInstruction(); short opcode = ins.getOpcode(); if (opcode == Constants.MONITORENTER || opcode == Constants.MONITOREXIT) { ValueNumberFrame frame = vnaDataflow.getFactAtLocation(new Location(handle, basicBlock)); modifyLock(frame, fact, opcode == Constants.MONITORENTER ? 1 : -1); } else if (opcode == Constants.INVOKEVIRTUAL || opcode == Constants.INVOKEINTERFACE) { InvokeInstruction inv = (InvokeInstruction) ins; String name = inv.getMethodName(methodGen.getConstantPool()); String sig = inv.getSignature(methodGen.getConstantPool()); ValueNumberFrame frame = vnaDataflow.getFactAtLocation(new Location(handle, basicBlock)); if ("()V".equals(sig) && ("lock".equals(name) || "lockInterruptibly".equals(name))) { modifyLock(frame, fact, 1); } else if ("()V".equals(sig) && ("unlock".equals(name))) { modifyLock(frame, fact, -1); } } else if ((ins instanceof ReturnInstruction) && isSynchronized && !isStatic) { lockOp(fact, vna.getThisValue().getNumber(), -1); } }
/** Method invocation. */ public void visitInvokeInstruction(InvokeInstruction i) { Type[] argTypes = i.getArgumentTypes(cp); for (int j = 0; j < argTypes.length; j++) cv.registerCoupling(argTypes[j]); cv.registerCoupling(i.getReturnType(cp)); /* Measuring decision: measure overloaded methods separately */ cv.registerMethodInvocation(i.getClassName(cp), i.getMethodName(cp), argTypes); }
/** * @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); } }
public boolean isStreamClose( BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg, ResourceValueFrame frame, RepositoryLookupFailureCallback lookupFailureCallback) { if (!mightCloseStream(basicBlock, handle, cpg)) return false; Instruction ins = handle.getInstruction(); if ((ins instanceof INVOKEVIRTUAL) || (ins instanceof INVOKEINTERFACE)) { // Does this instruction close the stream? InvokeInstruction inv = (InvokeInstruction) ins; if (!frame.isValid() || !getInstanceValue(frame, inv, cpg).isInstance()) return false; // It's a close if the invoked class is any subtype of the stream // base class. // (Basically, we may not see the exact original stream class, // even though it's the same instance.) try { String classClosed = inv.getClassName(cpg); return Hierarchy.isSubtype(classClosed, streamBase) || Hierarchy.isSubtype(streamBase, classClosed); } catch (ClassNotFoundException e) { lookupFailureCallback.reportMissingClass(e); return false; } } return false; }
private TaintMethodSummary getMethodSummary(InvokeInstruction obj) { String methodNameWithSig = obj.getMethodName(cpg) + obj.getSignature(cpg); String fullMethodName = getSlashedClassName(obj) + "." + methodNameWithSig; TaintMethodSummary methodSummary = methodSummaries.get(fullMethodName); if (methodSummary == null && TO_STRING_METHOD.equals(methodNameWithSig)) { methodSummary = TaintMethodSummary.getDefaultToStringSummary(); } return methodSummary; }
/** * Look up the method referenced by given InvokeInstruction. This method does <em>not</em> look * for implementations in super or subclasses according to the virtual dispatch rules. * * @param inv the InvokeInstruction * @param cpg the ConstantPoolGen used by the class the InvokeInstruction belongs to * @param chooser JavaClassAndMethodChooser to use to pick the method from among the candidates * @return the JavaClassAndMethod, or null if no such method is defined in the class */ public static JavaClassAndMethod findExactMethod( InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser chooser) throws ClassNotFoundException { String className = inv.getClassName(cpg); String methodName = inv.getName(cpg); String methodSig = inv.getSignature(cpg); JavaClass jclass = Repository.lookupClass(className); return findMethod(jclass, methodName, methodSig, chooser); }
/** * Determine if given Instruction is a monitor wait. * * @param ins the Instruction * @param cpg the ConstantPoolGen for the Instruction * @return true if the instruction is a monitor wait, false if not */ public static boolean isMonitorNotify(Instruction ins, ConstantPoolGen cpg) { if (!(ins instanceof InvokeInstruction)) return false; if (ins.getOpcode() == Constants.INVOKESTATIC) return false; InvokeInstruction inv = (InvokeInstruction) ins; String methodName = inv.getMethodName(cpg); String methodSig = inv.getSignature(cpg); return isMonitorNotify(methodName, methodSig); }
/** * Get a MethodDescriptor describing the method called by given InvokeInstruction. * * @param inv the InvokeInstruction * @param cpg ConstantPoolGen of class containing instruction * @return MethodDescriptor describing the called method */ public static MethodDescriptor getCalledMethodDescriptor( InvokeInstruction inv, ConstantPoolGen cpg) { String calledClassName = inv.getClassName(cpg).replace('.', '/'); String calledMethodName = inv.getMethodName(cpg); String calledMethodSig = inv.getSignature(cpg); boolean isStatic = inv.getOpcode() == Constants.INVOKESTATIC; return DescriptorFactory.instance() .getMethodDescriptor(calledClassName, calledMethodName, calledMethodSig, isStatic); }
/** * Resolve possible method call targets. This works for both static and instance method calls. * * @param invokeInstruction the InvokeInstruction * @param typeFrame the TypeFrame containing the types of stack values * @param cpg the ConstantPoolGen * @return Set of methods which might be called * @throws DataflowAnalysisException * @throws ClassNotFoundException */ public static Set<JavaClassAndMethod> resolveMethodCallTargets( InvokeInstruction invokeInstruction, TypeFrame typeFrame, ConstantPoolGen cpg) throws DataflowAnalysisException, ClassNotFoundException { short opcode = invokeInstruction.getOpcode(); if (opcode == Constants.INVOKESTATIC) { HashSet<JavaClassAndMethod> result = new HashSet<JavaClassAndMethod>(); JavaClassAndMethod targetMethod = findInvocationLeastUpperBound(invokeInstruction, cpg, CONCRETE_METHOD); if (targetMethod != null) { result.add(targetMethod); } return result; } if (!typeFrame.isValid()) { return new HashSet<JavaClassAndMethod>(); } Type receiverType; boolean receiverTypeIsExact; if (opcode == Constants.INVOKESPECIAL) { // invokespecial instructions are dispatched to EXACTLY // the class specified by the instruction receiverType = ObjectTypeFactory.getInstance(invokeInstruction.getClassName(cpg)); receiverTypeIsExact = false; // Doesn't actually matter } else { // For invokevirtual and invokeinterface instructions, we have // virtual dispatch. By taking the receiver type (which may be a // subtype of the class specified by the instruction), // we may get a more precise set of call targets. int instanceStackLocation = typeFrame.getInstanceStackLocation(invokeInstruction, cpg); receiverType = typeFrame.getStackValue(instanceStackLocation); if (!(receiverType instanceof ReferenceType)) { return new HashSet<JavaClassAndMethod>(); } receiverTypeIsExact = typeFrame.isExact(instanceStackLocation); } if (DEBUG_METHOD_LOOKUP) { System.out.println( "[receiver type is " + receiverType + ", " + (receiverTypeIsExact ? "exact]" : " not exact]")); } return resolveMethodCallTargets( (ReferenceType) receiverType, invokeInstruction, cpg, receiverTypeIsExact); }
public static boolean mightCloseStream( BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg) { Instruction ins = handle.getInstruction(); if ((ins instanceof INVOKEVIRTUAL) || (ins instanceof INVOKEINTERFACE)) { // Does this instruction close the stream? InvokeInstruction inv = (InvokeInstruction) ins; // It's a close if the invoked class is any subtype of the stream // base class. // (Basically, we may not see the exact original stream class, // even though it's the same instance.) return inv.getName(cpg).equals("close") && inv.getSignature(cpg).equals("()V"); } return false; }
public static @CheckForNull JavaClassAndMethod findInvocationLeastUpperBound( InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser methodChooser) throws ClassNotFoundException { if (DEBUG_METHOD_LOOKUP) { System.out.println( "Find prototype method for " + SignatureConverter.convertMethodSignature(inv, cpg)); } short opcode = inv.getOpcode(); if (opcode == Constants.INVOKESTATIC) { if (methodChooser == INSTANCE_METHOD) return null; } else { if (methodChooser == STATIC_METHOD) return null; } // Find the method if (opcode == Constants.INVOKESPECIAL) { // Non-virtual dispatch return findExactMethod(inv, cpg, methodChooser); } else { String className = inv.getClassName(cpg); String methodName = inv.getName(cpg); String methodSig = inv.getSignature(cpg); if (DEBUG_METHOD_LOOKUP) { System.out.println("[Class name is " + className + "]"); System.out.println("[Method name is " + methodName + "]"); System.out.println("[Method signature is " + methodSig + "]"); } if (className.startsWith("[")) { // Java 1.5 allows array classes to appear as the class name className = "java.lang.Object"; } JavaClass jClass = Repository.lookupClass(className); return findInvocationLeastUpperBound( jClass, methodName, methodSig, methodChooser, opcode == Constants.INVOKEINTERFACE); } }
private boolean matchMethod( InvokeInstruction inv, ConstantPoolGen cpg, String className, String methodName) { return inv.getClassName(cpg).equals(className) && inv.getName(cpg).equals(methodName); }
private void checkForPossibleObligationTransfer( InvokeInstruction inv, InstructionHandle handle) throws ClassNotFoundException { // // We will assume that a method invocation might transfer // an obligation from one type to another if // 1. either // - it's a constructor where the constructed // type and exactly one param type // are obligation types, or // - it's a method where the return type and // exactly one param type are obligation types // 2. at least one instance of the resource "consumed" // by the transfer exists at the point of the transfer. // E.g., if we see a transfer of InputStream->Reader, // there must be an instance of InputStream at // the transfer point. // if (DEBUG_FP) { System.out.println("Checking " + handle + " as possible obligation transfer...:"); } // Find the State which is a prefix of the error state // at the location of this (possible) transfer. State transferState = getTransferState(handle); if (transferState == null) { if (DEBUG_FP) { System.out.println("No transfer state???"); } return; } String methodName = inv.getMethodName(cpg); Type producedType = methodName.equals("<init>") ? inv.getReferenceType(cpg) : inv.getReturnType(cpg); if (DEBUG_FP && !(producedType instanceof ObjectType)) { System.out.println("Produced type " + producedType + " not an ObjectType"); } if (producedType instanceof ObjectType) { Obligation produced = database.getFactory().getObligationByType((ObjectType) producedType); if (DEBUG_FP && produced == null) { System.out.println("Produced type " + producedType + " not an obligation type"); } if (produced != null) { XMethod calledMethod = XFactory.createXMethod(inv, cpg); Obligation[] params = database.getFactory().getParameterObligationTypes(calledMethod); for (int i = 0; i < params.length; i++) { Obligation consumed = params[i]; if (DEBUG_FP && consumed == null) { System.out.println("Param " + i + " not an obligation type"); } if (DEBUG_FP && consumed != null && consumed.equals(produced)) { System.out.println("Consumed type is the same as produced type"); } if (consumed != null && !consumed.equals(produced)) { // See if an instance of the consumed obligation // type // exists here. if (transferState.getObligationSet().getCount(consumed.getId()) > 0) { transferList.add(new PossibleObligationTransfer(consumed, produced)); if (DEBUG_FP) { System.out.println( "===> Possible transfer of " + consumed + " to " + produced + " at " + handle); } } else if (DEBUG_FP) { System.out.println( handle + " not a transfer " + "of " + consumed + "->" + produced + " because no instances of " + consumed); System.out.println("I see " + transferState.getObligationSet()); } } } } } }
private ResourceValue getInstanceValue( ResourceValueFrame frame, InvokeInstruction inv, ConstantPoolGen cpg) { int numConsumed = inv.consumeStack(cpg); if (numConsumed == Constants.UNPREDICTABLE) throw new IllegalStateException(); return frame.getValue(frame.getNumSlots() - numConsumed); }
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 static String getFullMethodName(ConstantPoolGen cpg, InvokeInstruction invoke) { String dottedClassName = invoke.getReferenceType(cpg).toString(); StringBuilder builder = new StringBuilder(ClassName.toSlashedClassName(dottedClassName)); builder.append(".").append(invoke.getMethodName(cpg)).append(invoke.getSignature(cpg)); return builder.toString(); }
public static Set<ValueNumber> checkUnconditionalDerefDatabase( Location location, ValueNumberFrame vnaFrame, ConstantPoolGen constantPool, @CheckForNull IsNullValueFrame invFrame, TypeDataflow typeDataflow) throws DataflowAnalysisException { if (invFrame != null && !invFrame.isValid()) { return Collections.emptySet(); } InvokeInstruction inv = (InvokeInstruction) location.getHandle().getInstruction(); SignatureParser sigParser = new SignatureParser(inv.getSignature(constantPool)); int numParams = sigParser.getNumParameters(); if (numParams == 0 || !sigParser.hasReferenceParameters()) { return Collections.emptySet(); } ParameterNullnessPropertyDatabase database = AnalysisContext.currentAnalysisContext().getUnconditionalDerefParamDatabase(); if (database == null) { if (DEBUG_CHECK_CALLS) { System.out.println("no database!"); } return Collections.emptySet(); } TypeFrame typeFrame = typeDataflow.getFactAtLocation(location); if (!typeFrame.isValid()) { if (DEBUG_CHECK_CALLS) { System.out.println("invalid type frame!"); } return Collections.emptySet(); } try { Set<XMethod> targetSet = Hierarchy2.resolveMethodCallTargets(inv, typeFrame, constantPool); if (targetSet.isEmpty()) { return Collections.emptySet(); } if (DEBUG_CHECK_CALLS) { System.out.println("target set size: " + targetSet.size()); } // Compute the intersection of all properties ParameterProperty derefParamSet = null; for (XMethod target : targetSet) { if (target.isStub()) { continue; } if (DEBUG_CHECK_CALLS) { System.out.print("Checking: " + target + ": "); } ParameterProperty targetDerefParamSet = database.getProperty(target.getMethodDescriptor()); if (targetDerefParamSet == null) { // Hmm...no information for this target. // assume it doesn't dereference anything if (DEBUG_CHECK_CALLS) { System.out.println("==> no information, assume no guaranteed dereferences"); } return Collections.emptySet(); } if (DEBUG_CHECK_CALLS) { System.out.println("==> " + targetDerefParamSet); } if (derefParamSet == null) { derefParamSet = new ParameterProperty(); derefParamSet.copyFrom(targetDerefParamSet); } else { derefParamSet.intersectWith(targetDerefParamSet); } } if (derefParamSet == null || derefParamSet.isEmpty()) { if (DEBUG) { System.out.println("** Nothing"); } return Collections.emptySet(); } if (DEBUG_CHECK_CALLS) { System.out.println( "** Summary of call @ " + location.getHandle().getPosition() + ": " + derefParamSet); } HashSet<ValueNumber> requiredToBeNonnull = new HashSet<ValueNumber>(); for (int i = 0; i < numParams; i++) { if (!derefParamSet.hasProperty(i)) { continue; } int argSlot = vnaFrame.getStackLocation(sigParser.getSlotsFromTopOfStackForParameter(i)); if (invFrame != null && !reportDereference(invFrame, argSlot)) { continue; } if (DEBUG_CHECK_CALLS) { System.out.println( " dereference @ " + location.getHandle().getPosition() + " of parameter " + i); } requiredToBeNonnull.add(vnaFrame.getValue(argSlot)); } return requiredToBeNonnull; } catch (ClassNotFoundException e) { AnalysisContext.reportMissingClass(e); } return Collections.emptySet(); }
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(); }
/** * Resolve possible instance method call targets. * * @param receiverType type of the receiver object * @param invokeInstruction the InvokeInstruction * @param cpg the ConstantPoolGen * @param receiverTypeIsExact if true, the receiver type is known exactly, which should allow a * precise result * @return Set of methods which might be called * @throws ClassNotFoundException */ public static Set<JavaClassAndMethod> resolveMethodCallTargets( ReferenceType receiverType, InvokeInstruction invokeInstruction, ConstantPoolGen cpg, boolean receiverTypeIsExact) throws ClassNotFoundException { HashSet<JavaClassAndMethod> result = new HashSet<JavaClassAndMethod>(); if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC) throw new IllegalArgumentException(); String methodName = invokeInstruction.getName(cpg); String methodSig = invokeInstruction.getSignature(cpg); // Array method calls aren't virtual. // They should just resolve to Object methods. if (receiverType instanceof ArrayType) { JavaClass javaLangObject = AnalysisContext.currentAnalysisContext().lookupClass("java.lang.Object"); JavaClassAndMethod classAndMethod = findMethod(javaLangObject, methodName, methodSig, INSTANCE_METHOD); if (classAndMethod != null) result.add(classAndMethod); return result; } if (receiverType instanceof NullType) { return Collections.<JavaClassAndMethod>emptySet(); } AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext(); // Get the receiver class. String receiverClassName = ((ObjectType) receiverType).getClassName(); JavaClass receiverClass = analysisContext.lookupClass(receiverClassName); ClassDescriptor receiverDesc = DescriptorFactory.createClassDescriptorFromDottedClassName(receiverClassName); // Figure out the upper bound for the method. // This is what will be called if this is not a virtual call site. JavaClassAndMethod upperBound = findMethod(receiverClass, methodName, methodSig, CONCRETE_METHOD); if (upperBound == null) { upperBound = findInvocationLeastUpperBound( receiverClass, methodName, methodSig, CONCRETE_METHOD, false); } if (upperBound != null) { if (DEBUG_METHOD_LOOKUP) { System.out.println( "Adding upper bound: " + SignatureConverter.convertMethodSignature( upperBound.getJavaClass(), upperBound.getMethod())); } result.add(upperBound); } // Is this a virtual call site? boolean virtualCall = (invokeInstruction.getOpcode() == Constants.INVOKEVIRTUAL || invokeInstruction.getOpcode() == Constants.INVOKEINTERFACE) && (upperBound == null || !upperBound.getJavaClass().isFinal() && !upperBound.getMethod().isFinal()) && !receiverTypeIsExact; if (virtualCall) { if (!receiverClassName.equals("java.lang.Object")) { // This is a true virtual call: assume that any concrete // subtype method may be called. Set<ClassDescriptor> subTypeSet = analysisContext.getSubtypes2().getSubtypes(receiverDesc); for (ClassDescriptor subtype : subTypeSet) { XMethod concreteSubtypeMethod = findMethod(subtype, methodName, methodSig, false); if (concreteSubtypeMethod != null && (concreteSubtypeMethod.getAccessFlags() & Constants.ACC_ABSTRACT) == 0) { result.add(new JavaClassAndMethod(concreteSubtypeMethod)); } } if (false && subTypeSet.size() > 500) new RuntimeException( receiverClassName + " has " + subTypeSet.size() + " subclasses, " + result.size() + " of which implement " + methodName + methodSig + " " + invokeInstruction) .printStackTrace(System.out); } } return result; }