package soot.jimple.toolkits.infoflow; import soot.*; import java.util.*; import soot.toolkits.graph.*; import soot.toolkits.scalar.*; import soot.jimple.internal.*; import soot.jimple.*; // SimpleMethodInfoFlowAnalysis written by Richard L. Halpert, 2007-02-25 // Constructs a data flow table for the given method. Ignores indirect flow. // These tables conservatively approximate how data flows from parameters, // fields, and globals to parameters, fields, globals, and the return value. // Note that a ref-type parameter (or field or global) might allow access to a // large data structure, but that entire structure will be represented only by // the parameter's one node in the data flow graph. public class SimpleMethodInfoFlowAnalysis extends ForwardFlowAnalysis { SootMethod sm; Value thisLocal; InfoFlowAnalysis dfa; boolean refOnly; MutableDirectedGraph infoFlowGraph; Ref returnRef; FlowSet entrySet; FlowSet emptySet; boolean printMessages; public static int counter = 0; public SimpleMethodInfoFlowAnalysis(UnitGraph g, InfoFlowAnalysis dfa, boolean ignoreNonRefTypeFlow) { this(g, dfa, ignoreNonRefTypeFlow, true); counter++; // Add all of the nodes necessary to ensure that this is a complete data flow graph // Add every parameter of this method for(int i = 0; i < sm.getParameterCount(); i++) { EquivalentValue parameterRefEqVal = InfoFlowAnalysis.getNodeForParameterRef(sm, i); if(!infoFlowGraph.containsNode(parameterRefEqVal)) infoFlowGraph.addNode(parameterRefEqVal); } // Add every field of this class for(Iterator it = sm.getDeclaringClass().getFields().iterator(); it.hasNext(); ) { SootField sf = (SootField) it.next(); EquivalentValue fieldRefEqVal = InfoFlowAnalysis.getNodeForFieldRef(sm, sf); if(!infoFlowGraph.containsNode(fieldRefEqVal)) infoFlowGraph.addNode(fieldRefEqVal); } // Add every field of this class's superclasses SootClass superclass = sm.getDeclaringClass(); if(superclass.hasSuperclass()) superclass = sm.getDeclaringClass().getSuperclass(); while(superclass.hasSuperclass()) // we don't want to process Object { Iterator scFieldsIt = superclass.getFields().iterator(); while(scFieldsIt.hasNext()) { SootField scField = (SootField) scFieldsIt.next(); EquivalentValue fieldRefEqVal = InfoFlowAnalysis.getNodeForFieldRef(sm, scField); if(!infoFlowGraph.containsNode(fieldRefEqVal)) infoFlowGraph.addNode(fieldRefEqVal); } superclass = superclass.getSuperclass(); } // Add thisref of this class EquivalentValue thisRefEqVal = InfoFlowAnalysis.getNodeForThisRef(sm); if(!infoFlowGraph.containsNode(thisRefEqVal)) infoFlowGraph.addNode(thisRefEqVal); // Add returnref of this method EquivalentValue returnRefEqVal = new CachedEquivalentValue(returnRef); if(!infoFlowGraph.containsNode(returnRefEqVal)) infoFlowGraph.addNode(returnRefEqVal); if(printMessages) G.v().out.println("STARTING ANALYSIS FOR " + g.getBody().getMethod() + " -----"); doFlowInsensitiveAnalysis(); if(printMessages) G.v().out.println("ENDING ANALYSIS FOR " + g.getBody().getMethod() + " -----"); } /** A constructor that doesn't run the analysis */ protected SimpleMethodInfoFlowAnalysis(UnitGraph g, InfoFlowAnalysis dfa, boolean ignoreNonRefTypeFlow, boolean dummyDontRunAnalysisYet) { super(g); this.sm = g.getBody().getMethod(); if(sm.isStatic()) this.thisLocal = null; else this.thisLocal = g.getBody().getThisLocal(); this.dfa = dfa; this.refOnly = ignoreNonRefTypeFlow; this.infoFlowGraph = new MemoryEfficientGraph(); this.returnRef = new ParameterRef(g.getBody().getMethod().getReturnType(), -1); // it's a dummy parameter ref this.entrySet = new ArraySparseSet(); this.emptySet = new ArraySparseSet(); printMessages = false; } public void doFlowInsensitiveAnalysis() { FlowSet fs = (FlowSet) newInitialFlow(); boolean flowSetChanged = true; while(flowSetChanged) { int sizebefore = fs.size(); Iterator stmtIt = graph.iterator(); while(stmtIt.hasNext()) { Stmt s = (Stmt) stmtIt.next(); flowThrough(fs, s, fs); } if(fs.size() > sizebefore) flowSetChanged = true; else flowSetChanged = false; } } public MutableDirectedGraph getMethodInfoFlowSummary() { return infoFlowGraph; } protected void merge(Object in1, Object in2, Object out) { FlowSet inSet1 = (FlowSet) in1; FlowSet inSet2 = (FlowSet) in2; FlowSet outSet = (FlowSet) out; inSet1.union(inSet2, outSet); } protected boolean isNonRefType(Type type) { return !(type instanceof RefLikeType); } protected boolean ignoreThisDataType(Type type) { return refOnly && isNonRefType(type); } // Interesting sources are summarized (and possibly printed) public boolean isInterestingSource(Value source) { return (source instanceof Ref); } // Trackable sources are added to the flow set public boolean isTrackableSource(Value source) { return isInterestingSource(source) || (source instanceof Ref); } // Interesting sinks are possibly printed public boolean isInterestingSink(Value sink) { return (sink instanceof Ref); } // Trackable sinks are added to the flow set public boolean isTrackableSink(Value sink) { return isInterestingSink(sink) || (sink instanceof Ref) || (sink instanceof Local); } private ArrayList<Value> getDirectSources(Value v, FlowSet fs) { ArrayList<Value> ret = new ArrayList<Value>(); // of "interesting sources" EquivalentValue vEqVal = new CachedEquivalentValue(v); Iterator fsIt = fs.iterator(); while(fsIt.hasNext()) { Pair pair = (Pair) fsIt.next(); if( pair.getO1().equals(vEqVal) ) ret.add( ((EquivalentValue)pair.getO2()).getValue() ); } return ret; } // For when data flows to a local protected void handleFlowsToValue(Value sink, Value initialSource, FlowSet fs) { if(!isTrackableSink(sink)) return; List<Value> sources = getDirectSources(initialSource, fs); // list of Refs... returns all other sources if(isTrackableSource(initialSource)) sources.add(initialSource); Iterator<Value> sourcesIt = sources.iterator(); while(sourcesIt.hasNext()) { Value source = sourcesIt.next(); EquivalentValue sinkEqVal = new CachedEquivalentValue(sink); EquivalentValue sourceEqVal = new CachedEquivalentValue(source); if(sinkEqVal.equals(sourceEqVal)) continue; Pair pair = new Pair(sinkEqVal, sourceEqVal); if(!fs.contains(pair)) { fs.add(pair); if(isInterestingSource(source) && isInterestingSink(sink)) { if(!infoFlowGraph.containsNode(sinkEqVal)) infoFlowGraph.addNode(sinkEqVal); if(!infoFlowGraph.containsNode(sourceEqVal)) infoFlowGraph.addNode(sourceEqVal); infoFlowGraph.addEdge(sourceEqVal, sinkEqVal); if(printMessages) G.v().out.println(" Found " + source + " flows to " + sink); } } } } // for when data flows to the data structure pointed to by a local protected void handleFlowsToDataStructure(Value base, Value initialSource, FlowSet fs) { List<Value> sinks = getDirectSources(base, fs); if(isTrackableSink(base)) sinks.add(base); List<Value> sources = getDirectSources(initialSource, fs); if(isTrackableSource(initialSource)) sources.add(initialSource); Iterator<Value> sourcesIt = sources.iterator(); while(sourcesIt.hasNext()) { Value source = sourcesIt.next(); EquivalentValue sourceEqVal = new CachedEquivalentValue(source); Iterator<Value> sinksIt = sinks.iterator(); while(sinksIt.hasNext()) { Value sink = sinksIt.next(); if(!isTrackableSink(sink)) continue; EquivalentValue sinkEqVal = new CachedEquivalentValue(sink); if(sinkEqVal.equals(sourceEqVal)) continue; Pair pair = new Pair(sinkEqVal, sourceEqVal); if(!fs.contains(pair)) { fs.add(pair); if(isInterestingSource(source) && isInterestingSink(sink)) { if(!infoFlowGraph.containsNode(sinkEqVal)) infoFlowGraph.addNode(sinkEqVal); if(!infoFlowGraph.containsNode(sourceEqVal)) infoFlowGraph.addNode(sourceEqVal); infoFlowGraph.addEdge(sourceEqVal, sinkEqVal); if(printMessages) G.v().out.println(" Found " + source + " flows to " + sink); } } } } } // handles the invoke expression AND returns a list of the return value's sources // for each node // if the node is a parameter // source = argument <Immediate> // if the node is a static field // source = node <StaticFieldRef> // if the node is a field // source = receiver object <Local> // if the node is the return value // continue // for each sink // if the sink is a parameter // handleFlowsToDataStructure(sink, source, fs) // if the sink is a static field // handleFlowsToValue(sink, source, fs) // if the sink is a field // handleFlowsToDataStructure(receiver object, source, fs) // if the sink is the return value // add node to list of return value sources protected List handleInvokeExpr(InvokeExpr ie, Stmt is, FlowSet fs) { // get the data flow graph MutableDirectedGraph dataFlowGraph = dfa.getInvokeInfoFlowSummary(ie, is, sm); // must return a graph whose nodes are Refs!!! // if( ie.getMethodRef().resolve().getSubSignature().equals(new String("boolean remove(java.lang.Object)")) ) // { // G.v().out.println("*!*!*!*!*!<boolean remove(java.lang.Object)> has FLOW SENSITIVE infoFlowGraph: "); // ClassInfoFlowAnalysis.printDataFlowGraph(infoFlowGraph); // } List returnValueSources = new ArrayList(); Iterator<Object> nodeIt = dataFlowGraph.getNodes().iterator(); while(nodeIt.hasNext()) { EquivalentValue nodeEqVal = (EquivalentValue) nodeIt.next(); if(!(nodeEqVal.getValue() instanceof Ref)) throw new RuntimeException("Illegal node type in data flow graph:" + nodeEqVal.getValue() + " should be an object of type Ref."); Ref node = (Ref) nodeEqVal.getValue(); Value source = null; if(node instanceof ParameterRef) { ParameterRef param = (ParameterRef) node; if(param.getIndex() == -1) continue; source = ie.getArg(param.getIndex()); // Immediate } else if(node instanceof StaticFieldRef) { source = node; // StaticFieldRef } else if(ie instanceof InstanceInvokeExpr && node instanceof InstanceFieldRef) { InstanceInvokeExpr iie = (InstanceInvokeExpr) ie; source = iie.getBase(); // Local } Iterator sinksIt = dataFlowGraph.getSuccsOf(nodeEqVal).iterator(); while(sinksIt.hasNext()) { EquivalentValue sinkEqVal = (EquivalentValue) sinksIt.next(); Ref sink = (Ref) sinkEqVal.getValue(); if(sink instanceof ParameterRef) { ParameterRef param = (ParameterRef) sink; if(param.getIndex() == -1) { returnValueSources.add(source); } else { handleFlowsToDataStructure(ie.getArg(param.getIndex()), source, fs); } } else if(sink instanceof StaticFieldRef) { handleFlowsToValue(sink, source, fs); } else if(ie instanceof InstanceInvokeExpr && sink instanceof InstanceFieldRef) { InstanceInvokeExpr iie = (InstanceInvokeExpr) ie; handleFlowsToDataStructure(iie.getBase(), source, fs); } } } // return the list of return value sources return returnValueSources; } protected void flowThrough(Object inValue, Object unit, Object outValue) { FlowSet in = (FlowSet) inValue; FlowSet out = (FlowSet) outValue; Stmt stmt = (Stmt) unit; if(in != out) // this method is reused for flow insensitive analysis, which uses the same FlowSet for in and out in.copy(out); FlowSet changedFlow = out; // Calculate the minimum subset of the flow set that we need to consider - OBSELETE optimization // FlowSet changedFlow = new ArraySparseSet(); // FlowSet oldFlow = new ArraySparseSet(); // out.copy(oldFlow); // in.union(out, out); // out.difference(oldFlow, changedFlow); /* Iterator changedFlowIt = changedFlow.iterator(); while(changedFlowIt.hasNext()) { Pair pair = (Pair) changedFlowIt.next(); EquivalentValue defEqVal = (EquivalentValue) pair.getO1(); Value def = defEqVal.getValue(); boolean defIsUsed = false; Iterator usesIt = stmt.getUseBoxes().iterator(); while(usesIt.hasNext()) { Value use = ((ValueBox) usesIt.next()).getValue(); if(use.equivTo(def)) defIsUsed = true; } if(!defIsUsed) changedFlow.remove(pair); } */ // Bail out if there's nothing to consider, unless this might be the first run // if(changedFlow.isEmpty() && !oldFlow.equals(emptySet)) // return; if(stmt instanceof IdentityStmt) // assigns an IdentityRef to a Local { IdentityStmt is = (IdentityStmt) stmt; IdentityRef ir = (IdentityRef) is.getRightOp(); if(ir instanceof JCaughtExceptionRef) { // TODO: What the heck do we do with this??? } else if(ir instanceof ParameterRef) { if( !ignoreThisDataType(ir.getType()) ) { // <Local, ParameterRef and sources> handleFlowsToValue(is.getLeftOp(), ir, changedFlow); } } else if(ir instanceof ThisRef) { if( !ignoreThisDataType(ir.getType()) ) { // <Local, ThisRef and sources> handleFlowsToValue(is.getLeftOp(), ir, changedFlow); } } } else if(stmt instanceof ReturnStmt) // assigns an Immediate to the "returnRef" { ReturnStmt rs = (ReturnStmt) stmt; Value rv = rs.getOp(); if(rv instanceof Constant) { // No (interesting) data flow } else if(rv instanceof Local) { if( !ignoreThisDataType(rv.getType()) ) { // <ReturnRef, sources of Local> handleFlowsToValue(returnRef, rv, changedFlow); } } } else if(stmt instanceof AssignStmt) // assigns a Value to a Variable { AssignStmt as = (AssignStmt) stmt; Value lv = as.getLeftOp(); Value rv = as.getRightOp(); Value sink = null; boolean flowsToDataStructure = false; if(lv instanceof Local) // data flows into the Local { sink = lv; } else if(lv instanceof ArrayRef) // data flows into the base's data structure { ArrayRef ar = (ArrayRef) lv; sink = ar.getBase(); flowsToDataStructure = true; } else if(lv instanceof StaticFieldRef) // data flows into the field ref { sink = lv; } else if(lv instanceof InstanceFieldRef) { InstanceFieldRef ifr = (InstanceFieldRef) lv; if( ifr.getBase() == thisLocal ) // data flows into the field ref { sink = lv; } else // data flows into the base's data structure { sink = ifr.getBase(); flowsToDataStructure = true; } } List sources = new ArrayList(); boolean interestingFlow = true; if(rv instanceof Local) { sources.add(rv); interestingFlow = !ignoreThisDataType(rv.getType()); } else if(rv instanceof Constant) { sources.add(rv); interestingFlow = !ignoreThisDataType(rv.getType()); } else if(rv instanceof ArrayRef) // data flows from the base's data structure { ArrayRef ar = (ArrayRef) rv; sources.add(ar.getBase()); interestingFlow = !ignoreThisDataType(ar.getType()); } else if(rv instanceof StaticFieldRef) { sources.add(rv); interestingFlow = !ignoreThisDataType(rv.getType()); } else if(rv instanceof InstanceFieldRef) { InstanceFieldRef ifr = (InstanceFieldRef) rv; if( ifr.getBase() == thisLocal ) // data flows from the field ref { sources.add(rv); interestingFlow = !ignoreThisDataType(rv.getType()); } else // data flows from the base's data structure { sources.add(ifr.getBase()); interestingFlow = !ignoreThisDataType(ifr.getType()); } } else if(rv instanceof AnyNewExpr) { sources.add(rv); interestingFlow = !ignoreThisDataType(rv.getType()); } else if(rv instanceof BinopExpr) { BinopExpr be = (BinopExpr) rv; sources.add(be.getOp1()); sources.add(be.getOp2()); interestingFlow = !ignoreThisDataType(be.getType()); } else if(rv instanceof CastExpr) { CastExpr ce = (CastExpr) rv; sources.add(ce.getOp()); interestingFlow = !ignoreThisDataType(ce.getType()); } else if(rv instanceof InstanceOfExpr) { InstanceOfExpr ioe = (InstanceOfExpr) rv; sources.add(ioe.getOp()); interestingFlow = !ignoreThisDataType(ioe.getType()); } else if(rv instanceof UnopExpr) { UnopExpr ue = (UnopExpr) rv; sources.add(ue.getOp()); interestingFlow = !ignoreThisDataType(ue.getType()); } else if(rv instanceof InvokeExpr) { InvokeExpr ie = (InvokeExpr) rv; sources.addAll(handleInvokeExpr(ie, as, changedFlow)); interestingFlow = !ignoreThisDataType(ie.getType()); } if(interestingFlow) { if(flowsToDataStructure) { Iterator sourcesIt = sources.iterator(); while(sourcesIt.hasNext()) { Value source = (Value) sourcesIt.next(); handleFlowsToDataStructure(sink, source, changedFlow); } } else { Iterator sourcesIt = sources.iterator(); while(sourcesIt.hasNext()) { Value source = (Value) sourcesIt.next(); handleFlowsToValue(sink, source, changedFlow); } } } } else if(stmt.containsInvokeExpr()) // flows data between receiver object, parameters, globals, and return value { handleInvokeExpr(stmt.getInvokeExpr(), stmt, changedFlow); } // changedFlow.union(out, out); - OBSELETE optimization } protected void copy(Object source, Object dest) { FlowSet sourceSet = (FlowSet) source; FlowSet destSet = (FlowSet) dest; sourceSet.copy(destSet); } protected Object entryInitialFlow() { return entrySet.clone(); } protected Object newInitialFlow() { return emptySet.clone(); } public void addToEntryInitialFlow(Value source, Value sink) { EquivalentValue sinkEqVal = new CachedEquivalentValue(sink); EquivalentValue sourceEqVal = new CachedEquivalentValue(source); if(sinkEqVal.equals(sourceEqVal)) return; Pair pair = new Pair(sinkEqVal, sourceEqVal); if(!entrySet.contains(pair)) { entrySet.add(pair); } } public void addToNewInitialFlow(Value source, Value sink) { EquivalentValue sinkEqVal = new CachedEquivalentValue(sink); EquivalentValue sourceEqVal = new CachedEquivalentValue(source); if(sinkEqVal.equals(sourceEqVal)) return; Pair pair = new Pair(sinkEqVal, sourceEqVal); if(!emptySet.contains(pair)) { emptySet.add(pair); } } public Value getThisLocal() { return thisLocal; } }