All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.toolkits.infoflow.SimpleMethodInfoFlowAnalysis Maven / Gradle / Ivy

There is a newer version: 1.12.0
Show newest version
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 getDirectSources(Value v, FlowSet fs)
	{
		ArrayList ret = new ArrayList(); // 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 sources = getDirectSources(initialSource, fs); // list of Refs... returns all other sources
		if(isTrackableSource(initialSource))
			sources.add(initialSource);
		Iterator 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 sinks = getDirectSources(base, fs);
		if(isTrackableSink(base))
			sinks.add(base);
		List sources = getDirectSources(initialSource, fs);
		if(isTrackableSource(initialSource))
			sources.add(initialSource);
		Iterator sourcesIt = sources.iterator();
		while(sourcesIt.hasNext())
		{
			Value source = sourcesIt.next();
			EquivalentValue sourceEqVal = new CachedEquivalentValue(source);
			Iterator 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 
			// if the node is a static field
				// source = node 
			// if the node is a field
				// source = receiver object 
			// 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("*!*!*!*!*! has FLOW SENSITIVE infoFlowGraph: ");
//			ClassInfoFlowAnalysis.printDataFlowGraph(infoFlowGraph);
//		}
		
		List returnValueSources = new ArrayList();
		
		Iterator 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()) )
				{
					// 
					handleFlowsToValue(is.getLeftOp(), ir, changedFlow);
				}
			}
			else if(ir instanceof ThisRef)
			{
				if( !ignoreThisDataType(ir.getType()) )
				{
					// 
					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()) )
				{
					// 
					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;
	}
}