package soottocfg.soot.util; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.base.Verify; import soottocfg.cfg.Program; import soottocfg.cfg.expression.Expression; import soottocfg.cfg.expression.literal.NullLiteral; import soottocfg.cfg.method.CfgBlock; import soottocfg.cfg.method.Method; import soottocfg.cfg.statement.AssignStatement; import soottocfg.cfg.statement.CallStatement; import soottocfg.cfg.statement.NewStatement; import soottocfg.cfg.statement.Statement; import soottocfg.cfg.type.ReferenceType; import soottocfg.cfg.variable.Variable; import soottocfg.soot.memory_model.NewMemoryModel; import soottocfg.soot.transformers.ArrayTransformer; /** * @author rodykers */ public class FlowBasedPointsToAnalysis { private int nextAliasClass = 0; private Map<Variable,Set<Integer>> pointsTo = new HashMap<Variable,Set<Integer>>(); public void run(Program program) { // first add allocation site / alias class to all constructor calls for (Method m : program.getMethods()) { for (CfgBlock b : m.vertexSet()) { for (Statement s : b.getStatements()) { if (s instanceof NewStatement) { NewStatement ns = (NewStatement) s; Set<Integer> pt = getPointsToSet(ns.getLeft()); pt.add(nextAliasClass++); } } } } // then propagate point to sets until we reach a fixpoint // InterProceduralPullPushOrdering ordering = new InterProceduralPullPushOrdering(program.getEntryPoint()); int changes; do { changes = 0; // go over all assignments and method calls and update points to sets for (Method m : program.getMethods()) { for (CfgBlock b : m.vertexSet()) { for (Statement s : b.getStatements()) { if (s instanceof AssignStatement) { AssignStatement as = (AssignStatement) s; if (refType(as.getLeft()) && refType(as.getRight()) && !(as.getRight() instanceof NullLiteral)) { Variable left = variableFromExpression(as.getLeft()); Variable right = variableFromExpression(as.getRight()); changes += rightIntoLeft(right, left); } } else if (s instanceof CallStatement) { CallStatement cs = (CallStatement) s; Method target = cs.getCallTarget(); List<Variable> params = target.getInParams(); List<Expression> args = cs.getArguments(); Verify.verify(params.size()==args.size()); for (int i = 0; i < params.size(); i++) { Variable left = params.get(i); if (refType(left) && refType(args.get(i)) && !(args.get(i) instanceof NullLiteral)) { Variable right = variableFromExpression(args.get(i)); changes += rightIntoLeft(right, left); } } List<Variable> rets = target.getOutParams(); List<Expression> rec = cs.getReceiver(); Verify.verify(rec.size()==1 || rets.size()==rec.size(), "In "+m.getMethodName()+ " for "+ cs+": "+rets.size()+"!="+rec.size()); for (int i = 1; i < rec.size(); i++) { if (refType(rec.get(i)) && refType(rets.get(i-1))) { Variable left = variableFromExpression(rec.get(i)); Variable right = rets.get(i-1); changes += rightIntoLeft(right, left); } } // } else if (s instanceof PullStatement) { // PullStatement pull = (PullStatement) s; // Variable left = variableFromExpression(pull.getObject()); // Set<PushStatement> pushes = ordering.getPushsInfluencing(pull); // for (PushStatement push : pushes) { // Variable right = variableFromExpression(push.getObject()); // changes += rightIntoLeft(right, left); // } } } } } } while (changes > 0); } private int rightIntoLeft(Variable right, Variable left) { int changes = 0; if (refType(left) && refType(right)) { ReferenceType rtleft = (ReferenceType) left.getType(); ReferenceType rtright = (ReferenceType) right.getType(); Set<Integer> ptleft = getPointsToSet(left); Set<Integer> ptright = getPointsToSet(right); if (!ptleft.containsAll(ptright)){ for (Integer allocSite : ptright) { // only consider well typed assignments if (!ptleft.contains(allocSite) && rtleft.getClassVariable().superclassOf(rtright.getClassVariable())) { ptleft.add(allocSite); changes++; } } } } return changes; } public boolean mustAlias(Expression ref1, Expression ref2) { ReferenceType rt1 = getReferenceType(ref1); ReferenceType rt2 = getReferenceType(ref2); if (rt1.getClassVariable()==null || rt2.getClassVariable()==null) { // System.err.println("Class var not set: " + ref1 + " and " + ref2); return false; } if (!rt1.getClassVariable().subclassOf(rt2.getClassVariable()) && !rt1.getClassVariable().superclassOf(rt2.getClassVariable())) return false; if (!(ref1 instanceof NullLiteral || ref2 instanceof NullLiteral)) { Variable v1 = variableFromExpression(ref1); Variable v2 = variableFromExpression(ref2); if (v1.equals(v2)) return true; } Set<Integer> pt1 = getPointsToSet(ref1); Set<Integer> pt2 = getPointsToSet(ref2); return pt1.size()==1 && pt2.size()==1 && pt1.containsAll(pt2); } public boolean mayAlias(Expression ref1, Expression ref2) { if (ref1 instanceof NullLiteral || ref2 instanceof NullLiteral) return false; ReferenceType rt1 = getReferenceType(ref1); ReferenceType rt2 = getReferenceType(ref2); if (rt1.getClassVariable()==null || rt2.getClassVariable()==null) { // System.err.println("Class var not set: " + ref1 + " and " + ref2); return false; } if (!rt1.getClassVariable().subclassOf(rt2.getClassVariable()) && !rt1.getClassVariable().superclassOf(rt2.getClassVariable())) return false; Set<Integer> pt1 = getPointsToSet(ref1); Set<Integer> pt2 = getPointsToSet(ref2); // If we did not collect points to info, err on the safe side if (pt1.isEmpty() || pt2.isEmpty()) return true; return !(Collections.disjoint(pt1,pt2)); } private Set<Integer> getPointsToSet(Variable v) { if (!this.pointsTo.containsKey(v)) this.pointsTo.put(v, new HashSet<Integer>()); // bit of a hack to get this to work with the Jayhorn classes if (v.getType().toString().startsWith(NewMemoryModel.GlobalsClassName) || v.getType().toString().startsWith(ArrayTransformer.arrayTypeName)) { Set<Integer> pointsto = new HashSet<Integer>(); int ptid = -v.getType().hashCode(); pointsto.add(ptid); return pointsto; } return this.pointsTo.get(v); } private Set<Integer> getPointsToSet(Expression e) { if (e instanceof NullLiteral) { return new HashSet<Integer>(); } return getPointsToSet(variableFromExpression(e)); } private Variable variableFromExpression(Expression e) { Verify.verify(e.getUseVariables().size()==1, "Called variableFromExpression on expression that does not contain exactly 1 variable: " + e); return e.getUseVariables().iterator().next(); } private boolean refType(Expression e1){ return (e1.getType() instanceof ReferenceType); } private boolean refType(Variable e1){ return (e1.getType() instanceof ReferenceType); } private ReferenceType getReferenceType(Expression e) { Verify.verify(refType(e), "Called aliasing method on non-reference type"); return (ReferenceType) e.getType(); } }