/** Checks the specific method for consistency. */ public static void checkMgen(MethodGen mgen) { if (skip_checks) return; try { mgen.toString(); mgen.getLineNumberTable(mgen.getConstantPool()); InstructionList ilist = mgen.getInstructionList(); if (ilist == null || ilist.getStart() == null) return; CodeExceptionGen[] exceptionHandlers = mgen.getExceptionHandlers(); for (CodeExceptionGen gen : exceptionHandlers) { assert ilist.contains(gen.getStartPC()) : "exception handler " + gen + " has been forgotten in " + mgen.getClassName() + "." + mgen.getName(); } MethodGen nmg = new MethodGen(mgen.getMethod(), mgen.getClassName(), mgen.getConstantPool()); nmg.getLineNumberTable(mgen.getConstantPool()); } catch (Throwable t) { System.out.printf("failure in method %s.%s\n", mgen.getClassName(), mgen.getName()); t.printStackTrace(); throw new Error(t); } }
/** * Adds a prefix to the original method. To make it callable only from within the framework * itself. * * @param mg the MethodGen * @param method the current method * @param methodSequence the methods sequence number * @param uuid the definition UUID * @return the modified method */ private Method addPrefixToMethod( final MethodGen mg, final Method method, final int methodSequence, final String uuid) { // change the method access flags (should always be set to protected) int accessFlags = mg.getAccessFlags(); if ((accessFlags & Constants.ACC_PROTECTED) == 0) { // set the protected flag accessFlags |= Constants.ACC_PROTECTED; } if ((accessFlags & Constants.ACC_PRIVATE) != 0) { // clear the private flag accessFlags &= ~Constants.ACC_PRIVATE; } if ((accessFlags & Constants.ACC_PUBLIC) != 0) { // clear the public flag accessFlags &= ~Constants.ACC_PUBLIC; } mg.setName(getPrefixedMethodName(method, methodSequence, mg.getClassName())); mg.setAccessFlags(accessFlags); mg.setMaxStack(); mg.setMaxLocals(); return mg.getMethod(); }
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()); } }
@Override public void initEntryFact(LockSet result) { result.clear(); result.setDefaultLockCount(0); if (isSynchronized && !isStatic) { ValueNumber thisValue = vna.getThisValue(); result.setLockCount(thisValue.getNumber(), 1); } else if (isSynchronized && isStatic) { ValueNumber thisValue = vna.getClassObjectValue(methodGen.getClassName()); result.setLockCount(thisValue.getNumber(), 1); } }
private void lockOp(LockSet fact, int lockNumber, int delta) { int value = fact.getLockCount(lockNumber); if (value < 0) { return; } value += delta; if (value < 0) { value = LockSet.BOTTOM; } if (DEBUG) { System.out.println( "Setting " + lockNumber + " to " + value + " in " + methodGen.getClassName() + "." + methodGen.getName()); } fact.setLockCount(lockNumber, value); }
/** * Sets the locals to 'this' and each of the arguments. Any other locals are removed. An * instruction list with at least one instruction must exist. */ public static void setup_init_locals(MethodGen mg) { // Get the parameter types and names. Type[] arg_types = mg.getArgumentTypes(); String[] arg_names = mg.getArgumentNames(); // Remove any existing locals mg.setMaxLocals(0); mg.removeLocalVariables(); // Add a local for the instance variable (this) if (!mg.isStatic()) mg.addLocalVariable("this", new ObjectType(mg.getClassName()), null, null); // Add a local for each parameter for (int ii = 0; ii < arg_names.length; ii++) { mg.addLocalVariable(arg_names[ii], arg_types[ii], null, null); } // Reset the current number of locals so that when other locals // are added they get added at the correct offset mg.setMaxLocals(); return; }
/** * Constructor. * * @param rdfs the reverse depth-first-search (for the block order) * @param cfg the CFG for the method * @param methodGen the MethodGen for the method * @param vnaDataflow * @param assertionMethods AssertionMethods for the analyzed class */ public UnconditionalValueDerefAnalysis( ReverseDepthFirstSearch rdfs, DepthFirstSearch dfs, CFG cfg, Method method, MethodGen methodGen, ValueNumberDataflow vnaDataflow, AssertionMethods assertionMethods) { super(rdfs, dfs); this.cfg = cfg; this.methodGen = methodGen; this.method = method; this.vnaDataflow = vnaDataflow; this.assertionMethods = assertionMethods; if (DEBUG) { System.out.println( "UnconditionalValueDerefAnalysis analysis " + methodGen.getClassName() + "." + methodGen.getName() + " : " + methodGen.getSignature()); } }
private static String getFullMethodName(MethodGen methodGen) { String methodNameWithSignature = methodGen.getName() + methodGen.getSignature(); String slashedClassName = methodGen.getClassName().replace('.', '/'); return slashedClassName + "." + methodNameWithSignature; }
/** * Transforms invoke instructions that match the specified list for this class to call the * specified static call instead. */ private InstructionList xform_inst(MethodGen mg, Instruction inst) { switch (inst.getOpcode()) { case Constants.INVOKESTATIC: { InstructionList il = new InstructionList(); INVOKESTATIC is = (INVOKESTATIC) inst; String cname = is.getClassName(pgen); String mname = is.getMethodName(pgen); Type[] args = is.getArgumentTypes(pgen); MethodDef orig = new MethodDef(cname + "." + mname, args); MethodInfo call = method_map.get(orig); if (call != null) { call.cnt++; String classname = call.method_class; String methodname = mname; debug_map.log( "%s.%s: Replacing method %s.%s (%s) with %s.%s%n", mg.getClassName(), mg.getName(), cname, mname, UtilMDE.join(args, ", "), classname, methodname); il.append( ifact.createInvoke( classname, methodname, is.getReturnType(pgen), args, Constants.INVOKESTATIC)); } return (il); } case Constants.INVOKEVIRTUAL: { InstructionList il = new InstructionList(); INVOKEVIRTUAL iv = (INVOKEVIRTUAL) inst; String cname = iv.getClassName(pgen); String mname = iv.getMethodName(pgen); Type[] args = iv.getArgumentTypes(pgen); Type instance_type = iv.getReferenceType(pgen); Type[] new_args = BCELUtil.insert_type(instance_type, args); MethodDef orig = new MethodDef(cname + "." + mname, args); if (debug_class) System.out.printf("looking for %s in map %s%n", orig, method_map); MethodInfo call = method_map.get(orig); if (call != null) { call.cnt++; String classname = call.method_class; String methodname = mname; debug_map.log( "Replacing method %s.%s (%s) with %s.%s%n", cname, mname, ArraysMDE.toString(args), classname, methodname); il.append( ifact.createInvoke( classname, methodname, iv.getReturnType(pgen), new_args, Constants.INVOKESTATIC)); } return (il); } default: return (null); } }
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(); }