@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); } }
public LockAnalysis(MethodGen methodGen, ValueNumberDataflow vnaDataflow, DepthFirstSearch dfs) { super(dfs); this.methodGen = methodGen; this.vnaDataflow = vnaDataflow; this.vna = vnaDataflow.getAnalysis(); this.isSynchronized = methodGen.isSynchronized(); this.isStatic = methodGen.isStatic(); if (DEBUG) { System.out.println( "Analyzing Locks in " + methodGen.getClassName() + "." + methodGen.getName()); } }
/** * Find out if any VNs in the source block contribute to unconditionally dereferenced VNs in the * target block. If so, the VN in the source block is also unconditionally dereferenced, and we * must propagate the target VN's dereferences. * * @param fact a dataflow value * @param edge edge to check for merge input values * @return possibly-modified dataflow value */ private UnconditionalValueDerefSet propagateDerefSetsToMergeInputValues( UnconditionalValueDerefSet fact, Edge edge) { ValueNumberFrame blockValueNumberFrame = vnaDataflow.getResultFact(edge.getSource()); ValueNumberFrame targetValueNumberFrame = vnaDataflow.getStartFact(edge.getTarget()); UnconditionalValueDerefSet originalFact = fact; fact = duplicateFact(fact); if (blockValueNumberFrame.isValid() && targetValueNumberFrame.isValid()) { int slots = 0; if (targetValueNumberFrame.getNumSlots() == blockValueNumberFrame.getNumSlots()) { slots = targetValueNumberFrame.getNumSlots(); } else if (targetValueNumberFrame.getNumLocals() == blockValueNumberFrame.getNumLocals()) { slots = targetValueNumberFrame.getNumLocals(); } if (slots > 0) { if (DEBUG) { System.out.println("** Valid VNA frames for " + edge); System.out.println("** Block : " + blockValueNumberFrame); System.out.println("** Target: " + targetValueNumberFrame); } for (int i = 0; i < slots; i++) { ValueNumber blockVN = blockValueNumberFrame.getValue(i); ValueNumber targetVN = targetValueNumberFrame.getValue(i); if (blockVN.equals(targetVN)) { continue; } fact.clearDerefSet(blockVN); if (originalFact.isUnconditionallyDereferenced(targetVN)) { fact.setDerefSet(blockVN, originalFact.getUnconditionalDerefLocationSet(targetVN)); } } // for all slots for (ValueNumber blockVN : blockValueNumberFrame.valueNumbersForLoads()) { AvailableLoad load = blockValueNumberFrame.getLoad(blockVN); if (load == null) { continue; } ValueNumber[] targetVNs = targetValueNumberFrame.getAvailableLoad(load); if (targetVNs != null) { for (ValueNumber targetVN : targetVNs) { if (targetVN.hasFlag(ValueNumber.PHI_NODE) && fact.isUnconditionallyDereferenced(targetVN) && !fact.isUnconditionallyDereferenced(blockVN)) { // Block VN is also dereferenced // unconditionally. AvailableLoad targetLoad = targetValueNumberFrame.getLoad(targetVN); if (!load.equals(targetLoad)) { continue; } if (DEBUG) { System.out.println( "** Copy vn derefs for " + load + " from " + targetVN + " --> " + blockVN); System.out.println( "** block phi for " + System.identityHashCode(blockValueNumberFrame) + " is " + blockValueNumberFrame.phiNodeForLoads); System.out.println( "** target phi for " + System.identityHashCode(targetValueNumberFrame) + " is " + targetValueNumberFrame.phiNodeForLoads); } fact.setDerefSet(blockVN, fact.getUnconditionalDerefLocationSet(targetVN)); } } } } } } if (DEBUG) { System.out.println("Target VNF: " + targetValueNumberFrame); System.out.println("Block VNF: " + blockValueNumberFrame); System.out.println("fact: " + fact); } fact.cleanDerefSet(null, blockValueNumberFrame); return fact; }
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 UnconditionalValueDerefSet createFact() { return new UnconditionalValueDerefSet(vnaDataflow.getAnalysis().getNumValuesAllocated()); }
@Override public void transferInstruction( InstructionHandle handle, BasicBlock basicBlock, UnconditionalValueDerefSet fact) throws DataflowAnalysisException { Instruction instruction = handle.getInstruction(); if (fact.isTop()) { return; } Location location = new Location(handle, basicBlock); // If this is a call to an assertion method, // change the dataflow value to be TOP. // We don't want to report future derefs that would // be guaranteed only if the assertion methods // returns normally. // TODO: at some point, evaluate whether we should revisit this if (isAssertion(handle) // || handle.getInstruction() instanceof ATHROW ) { if (DEBUG) { System.out.println("MAKING BOTTOM0 AT: " + location); } fact.clear(); return; } // Get value number frame ValueNumberFrame vnaFrame = vnaDataflow.getFactAtLocation(location); if (!vnaFrame.isValid()) { if (DEBUG) { System.out.println("MAKING TOP1 AT: " + location); } // Probably dead code. // Assume this location can't be reached. makeFactTop(fact); return; } if (isNullCheck(handle, methodGen.getConstantPool())) { handleNullCheck(location, vnaFrame, fact); } // Check for calls to a method that unconditionally dereferences // a parameter. Mark any such arguments as derefs. if (CHECK_CALLS && instruction instanceof InvokeInstruction) { checkUnconditionalDerefDatabase(location, vnaFrame, fact); } // If this is a method call instruction, // check to see if any of the parameters are @NonNull, // and treat them as dereferences. if (CHECK_ANNOTATIONS && instruction instanceof InvokeInstruction) { checkNonNullParams(location, vnaFrame, fact); } if (CHECK_ANNOTATIONS && instruction instanceof ARETURN) { XMethod thisMethod = XFactory.createXMethod(methodGen); checkNonNullReturnValue(thisMethod, location, vnaFrame, fact); } if (CHECK_ANNOTATIONS && (instruction instanceof PUTFIELD || instruction instanceof PUTSTATIC)) { checkNonNullPutField(location, vnaFrame, fact); } // Check to see if an instance value is dereferenced here checkInstance(location, vnaFrame, fact); /* if (false) { fact.cleanDerefSet(location, vnaFrame); }*/ if (DEBUG && fact.isTop()) { System.out.println("MAKING TOP2 At: " + location); } }
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(); }