/** * */ package soottocfg.soot.memory_model; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.base.Verify; import soot.Local; import soot.Scene; import soot.SootClass; import soot.SootField; import soot.SootMethod; import soot.Unit; import soot.Value; import soot.jimple.FieldRef; import soot.jimple.InstanceFieldRef; import soot.jimple.JimpleBody; import soot.jimple.SpecialInvokeExpr; import soot.jimple.StaticFieldRef; import soot.jimple.Stmt; //import soot.jimple.spark.geom.geomPA.GeomPointsTo; //import soot.options.SparkOptions; import soot.toolkits.graph.CompleteUnitGraph; import soot.toolkits.graph.UnitGraph; import soottocfg.cfg.SourceLocation; import soottocfg.cfg.expression.BinaryExpression; import soottocfg.cfg.expression.BinaryExpression.BinaryOperator; import soottocfg.cfg.expression.Expression; import soottocfg.cfg.expression.IdentifierExpression; import soottocfg.cfg.expression.TupleAccessExpression; import soottocfg.cfg.method.Method; import soottocfg.cfg.statement.AssignStatement; import soottocfg.cfg.statement.AssumeStatement; import soottocfg.cfg.statement.CallStatement; import soottocfg.cfg.statement.PullStatement; import soottocfg.cfg.statement.PushStatement; import soottocfg.cfg.type.ReferenceType; import soottocfg.cfg.type.Type; import soottocfg.cfg.variable.ClassVariable; import soottocfg.cfg.variable.Variable; import soottocfg.soot.util.MethodInfo; import soottocfg.soot.util.SootTranslationHelpers; /** * @author schaef * @author rodykers * */ public class NewMemoryModel extends BasicMemoryModel { private Map<Variable, Map<String, Variable>> fieldToLocalMap = new HashMap<Variable, Map<String, Variable>>(); // This one sticks around between method calls... private Map<Variable, SootField> localToFieldMap = new HashMap<Variable, SootField>(); // private static Variable staticFieldContainerVariable; // private static List<SootField> usedStaticFields; public static final String GlobalsClassName = "$Global_"; // private static final String globalsClassVarName = // "JayhornGlobalsClassVar"; protected static void resetGlobalState() { // NewMemoryModel.staticFieldContainerVariable = null; // NewMemoryModel.usedStaticFields = null; } public NewMemoryModel() { NewMemoryModel.resetGlobalState(); // load points to analysis // setGeomPointsToAnalysis(); } public void clearFieldToLocalMap() { // TODO: this is only here because we know it's only called once per // method: fieldToLocalMap.clear(); } private boolean pullAt(Unit u, FieldRef fr) { SootMethod m = SootTranslationHelpers.v().getCurrentMethod(); if (fr instanceof InstanceFieldRef) { InstanceFieldRef ifr = (InstanceFieldRef) fr; // in constructor never pull 'this' if (m.isConstructor() && ifr.getBase().equals(m.getActiveBody().getThisLocal())) return false; } else if (fr instanceof StaticFieldRef) { // in static initializer never pull static field if (m.isStaticInitializer()) return false; } return true; } /** * Helper method to create a push of all field locals. * This method should only be used at the very end of a constructor. */ private void pushAllFields() { SootMethod m = SootTranslationHelpers.v().getCurrentMethod(); if (!m.isConstructor() || m.isStatic() || !m.isConcrete()) { return; } SourceLocation loc = this.statementSwitch.getCurrentLoc(); Variable thisLocal = this.statementSwitch.getMethodInfo().lookupLocalVariable(m.getActiveBody().getThisLocal()); ClassVariable classVar = ((ReferenceType) thisLocal.getType()).getClassVariable(); List<Variable> vars = new LinkedList<Variable>(); for (SootField sf : SootTranslationHelpers.findNonStaticFieldsRecursively(m.getDeclaringClass())) { vars.add(lookupFieldLocal(thisLocal, sf)); } List<Expression> packedVars = new LinkedList<Expression>(); for (int i = 0; i < vars.size(); i++) { packedVars.add(new IdentifierExpression(loc, vars.get(i))); } this.statementSwitch .push(new PushStatement(loc, classVar, new IdentifierExpression(loc, thisLocal), packedVars)); } private boolean pushAt(Unit u, FieldRef fr) { SootMethod m = SootTranslationHelpers.v().getCurrentMethod(); UnitGraph graph = new CompleteUnitGraph(m.getActiveBody()); if (fr instanceof InstanceFieldRef) { InstanceFieldRef ifr = (InstanceFieldRef) fr; // in constructor only push 'this' after the last access if (m.isConstructor() && ifr.getBase().equals(m.getActiveBody().getThisLocal())) { // check if there is any path from 'u' to the tail(s) that does // not contain an write to 'this' List<Unit> tails = graph.getTails(); List<Unit> todo = new LinkedList<Unit>(); Set<Unit> done = new HashSet<Unit>(); todo.add(u); boolean foundPathWithoutAccess = false; while (!todo.isEmpty()) { Unit unit = todo.remove(0); done.add(unit); // if it contains another write to 'this', stop exploring // this path Stmt s = (Stmt) unit; if (s.containsFieldRef()) { FieldRef fr2 = s.getFieldRef(); if (fr2 instanceof InstanceFieldRef) { InstanceFieldRef ifr2 = (InstanceFieldRef) fr2; if (ifr2.getBase().equals(m.getActiveBody().getThisLocal()) && !u.equals(unit)) { continue; } } } else if (s.containsInvokeExpr() && s.getInvokeExpr() instanceof SpecialInvokeExpr) { // also stop exploring if there is a call to the // super class constructor. SpecialInvokeExpr ivk = (SpecialInvokeExpr) s.getInvokeExpr(); if (ivk.getMethod().isConstructor() && ivk.getBase().equals(m.getActiveBody().getThisLocal())) { // super class constructor. continue; } } if (tails.contains(unit)) { if (!s.containsInvokeExpr()) { foundPathWithoutAccess = true; // at the end of this // path } } else { for (Unit next : graph.getSuccsOf(unit)) { if (!todo.contains(next) && !done.contains(next)) { todo.add(next); } } // todo.addAll(graph.getSuccsOf(unit)); } } return foundPathWithoutAccess; } } else if (fr instanceof StaticFieldRef) { // in static initializer only push at the end if (m.isStaticInitializer()) { // check if there is any path from 'u' to the tail(s) that does // not contain an access to 'this' List<Unit> tails = graph.getTails(); List<Unit> todo = new LinkedList<Unit>(); todo.add(u); boolean foundPathWithoutAccess = false; while (!todo.isEmpty()) { Unit unit = todo.remove(0); // if it contains another access to a static field, stop // exploring this path Stmt s = (Stmt) unit; if (s.containsFieldRef()) { FieldRef fr2 = s.getFieldRef(); if (fr2 instanceof StaticFieldRef && !u.equals(unit)) continue; } if (tails.contains(unit)) foundPathWithoutAccess = true; // at the end of this // path else todo.addAll(graph.getSuccsOf(unit)); } return foundPathWithoutAccess; } } return true; } @Override public void mkHeapWriteStatement(Unit u, FieldRef fieldRef, Value rhs) { SourceLocation loc = SootTranslationHelpers.v().getSourceLocation(u); rhs.apply(valueSwitch); Expression value = valueSwitch.popExpression(); ClassVariable classVar; IdentifierExpression base; List<Variable> fieldLocals = new LinkedList<Variable>(); if (fieldRef instanceof InstanceFieldRef) { InstanceFieldRef ifr = (InstanceFieldRef) fieldRef; ifr.getBase().apply(valueSwitch); base = (IdentifierExpression) valueSwitch.popExpression(); // classVar = lookupClassVariable( // SootTranslationHelpers.v().getClassConstant(fieldRef.getField().getDeclaringClass().getType())); classVar = ((ReferenceType) base.getType()).getClassVariable(); // for (SootField sf : // findFieldsRecursively(fieldRef.getField().getDeclaringClass())) { for (SootField sf : SootTranslationHelpers.findFieldsRecursivelyForRef(ifr.getBase())) { if (!sf.isStatic()) { fieldLocals.add(lookupFieldLocal(base.getVariable(), sf)); } } } else if (fieldRef instanceof StaticFieldRef) { /* * For static fields, we use the artificial global class * that holds all static fields as base. */ Variable glob = getStaticFieldContainerVariable(fieldRef.getField().getDeclaringClass()); classVar = ((ReferenceType) glob.getType()).getClassVariable(); base = new IdentifierExpression(loc, glob); for (SootField sf : staticFieldsPerClass.get(glob)) { fieldLocals.add(lookupFieldLocal(glob, sf)); } } else { throw new RuntimeException("not implemented"); } Variable fieldVar = lookupFieldLocal(fieldRef); // TODO Verify.verify(fieldLocals.contains(fieldVar), fieldLocals.toString() + "\ndoesn't contain " + fieldVar); // Variable[] vars = classVar.getAssociatedFields(); Variable[] vars = fieldLocals.toArray(new Variable[fieldLocals.size()]); // ------------- pull --------------- if (pullAt(u, fieldRef)) { List<IdentifierExpression> unpackedVars = new LinkedList<IdentifierExpression>(); for (int i = 0; i < vars.length; i++) { unpackedVars.add(new IdentifierExpression(this.statementSwitch.getCurrentLoc(), vars[i])); } this.statementSwitch.push(new PullStatement(loc, classVar, base, unpackedVars)); } // ------------------------------------ this.statementSwitch.push(new AssignStatement(loc, new IdentifierExpression(this.statementSwitch.getCurrentLoc(), fieldVar), value)); // ------------- push ----------------- if (pushAt(u, fieldRef)) { List<Expression> packedVars = new LinkedList<Expression>(); for (int i = 0; i < vars.length; i++) { packedVars.add(new IdentifierExpression(this.statementSwitch.getCurrentLoc(), vars[i])); } this.statementSwitch.push(new PushStatement(loc, classVar, base, packedVars)); } // ------------------------------------ } @Override public void mkHeapReadStatement(Unit u, FieldRef fieldRef, Value lhs) { SourceLocation loc = SootTranslationHelpers.v().getSourceLocation(u); // Variable fieldVar = lookupField(fieldRef.getField()); lhs.apply(valueSwitch); IdentifierExpression left = (IdentifierExpression) valueSwitch.popExpression(); ClassVariable classVar; IdentifierExpression base; List<Variable> fieldLocals = new LinkedList<Variable>(); if (fieldRef instanceof InstanceFieldRef) { InstanceFieldRef ifr = (InstanceFieldRef) fieldRef; ifr.getBase().apply(valueSwitch); base = (IdentifierExpression) valueSwitch.popExpression(); // classVar = lookupClassVariable( // SootTranslationHelpers.v().getClassConstant(fieldRef.getField().getDeclaringClass().getType())); classVar = ((ReferenceType) base.getType()).getClassVariable(); for (SootField sf : SootTranslationHelpers.findFieldsRecursivelyForRef(ifr.getBase())) { if (!sf.isStatic()) { fieldLocals.add(lookupFieldLocal(base.getVariable(), sf)); } } } else if (fieldRef instanceof StaticFieldRef) { /* * For static fields, we use the artificial global class * that holds all static fields as base. */ Variable glob = getStaticFieldContainerVariable(fieldRef.getField().getDeclaringClass()); classVar = ((ReferenceType) glob.getType()).getClassVariable(); base = new IdentifierExpression(loc, glob); for (SootField sf : staticFieldsPerClass.get(glob)) { fieldLocals.add(lookupFieldLocal(glob, sf)); } } else { throw new RuntimeException("not implemented"); } SootField field = fieldRef.getField(); if (SootTranslationHelpers.v().isWrittenOnce(field)) { if (!field.isStatic()) { TupleAccessExpression tae = new TupleAccessExpression(loc, base.getVariable(), field.getName()); this.statementSwitch.push(new AssignStatement(loc, left, tae)); //New, needs testing! /* We don't need to do this for writing these variables * because we assume they are constant anyway. */ return; } else { //TODO: } } Variable fieldVar = lookupFieldLocal(fieldRef); // TODO Verify.verify(fieldLocals.contains(fieldVar)); // Variable[] vars = classVar.getAssociatedFields(); Variable[] vars = fieldLocals.toArray(new Variable[fieldLocals.size()]); // ------------- pull --------------- if (pullAt(u, fieldRef)) { List<IdentifierExpression> unpackedVars = new LinkedList<IdentifierExpression>(); for (int i = 0; i < vars.length; i++) { unpackedVars.add(new IdentifierExpression(this.statementSwitch.getCurrentLoc(), vars[i])); } this.statementSwitch.push(new PullStatement(loc, classVar, base, unpackedVars)); } // ------------------------------------ this.statementSwitch.push(new AssignStatement(loc, left, new IdentifierExpression(this.statementSwitch.getCurrentLoc(), fieldVar))); // ------------- push ----------------- // if (pushAt(u, fieldRef)) { // List<Expression> packedVars = new LinkedList<Expression>(); // for (int i = 0; i < vars.length; i++) { // packedVars.add(new // IdentifierExpression(this.statementSwitch.getCurrentLoc(), vars[i])); // } // this.statementSwitch.push(new PushStatement(loc, classVar, base, // packedVars)); // } // ------------------------------------ } Map<SootClass, Variable> staticClassGlobals = new LinkedHashMap<SootClass, Variable>(); Map<Variable, List<SootField>> staticFieldsPerClass = new HashMap<Variable, List<SootField>>(); protected Variable getStaticFieldContainerVariable(SootClass sootClass) { if (!staticClassGlobals.containsKey(sootClass)) { ClassVariable classVar = new ClassVariable("$StaticFields_" + sootClass.getName(), new LinkedList<ClassVariable>()); List<SootField> usedFields = new LinkedList<SootField>(); List<Variable> fieldVars = new LinkedList<Variable>(); for (SootClass sc : new LinkedList<SootClass>(Scene.v().getClasses())) { if (sc.resolvingLevel() >= SootClass.BODIES) { for (SootMethod sm : sc.getMethods()) { try { for (Unit u : sm.retrieveActiveBody().getUnits()) { Stmt st = (Stmt) u; if (st.containsFieldRef()) { SootField sf = st.getFieldRef().getField(); if (sf.isStatic() && !usedFields.contains(sf) && sf.getDeclaringClass().equals(sootClass)) { if (!sf.equals(SootTranslationHelpers.v().getExceptionGlobal())) { fieldVars.add( new Variable(sf.getDeclaringClass().getName() + "." + sf.getName(), this.lookupType(sf.getType()))); usedFields.add(sf); } } } } } catch (Exception e) { } } } } classVar.addFields(fieldVars); SootTranslationHelpers.v().getProgram().addClassVariable(classVar); Variable var = new Variable(GlobalsClassName + sootClass.getName(), new ReferenceType(classVar), true, true); staticClassGlobals.put(sootClass, var); staticFieldsPerClass.put(var, usedFields); this.program.getGlobalsMap().put(sootClass.getName(), var); } return staticClassGlobals.get(sootClass); // if (staticFieldContainerVariable == null) { // usedStaticFields = new LinkedList<SootField>(); // ClassVariable classVar = new ClassVariable(globalsClassVarName, new // LinkedList<ClassVariable>()); // Set<Variable> fields = new LinkedHashSet<Variable>(); // for (SootClass sc : new // LinkedList<SootClass>(Scene.v().getClasses())) { // if (sc.resolvingLevel() >= SootClass.BODIES) { // for (SootMethod sm : sc.getMethods()) { // try { // for (Unit u : sm.retrieveActiveBody().getUnits()) { // Stmt st = (Stmt) u; // if (st.containsFieldRef()) { // SootField sf = st.getFieldRef().getField(); // if (sf.isStatic() && !usedStaticFields.contains(sf)) { // // if (!sf.equals(SootTranslationHelpers.v().getExceptionGlobal())) { // // TODO hack to exclude the // // exception field. // fields.add(new Variable(sf.getDeclaringClass().getName() + "." + // sf.getName(), this.lookupType(sf.getType()))); // usedStaticFields.add(sf); // // } // } // } // } // } catch (Exception e) { // // } // } // } // } // // List<Variable> fieldList = new LinkedList<Variable>(); // fieldList.addAll(fields); // classVar.addFields(fieldList); // SootTranslationHelpers.v().getProgram().addClassVariable(classVar); // staticFieldContainerVariable = new Variable(globalsClassName, new // ReferenceType(classVar), true, true); // } // return staticFieldContainerVariable; } @Override public void mkConstructorCall(Unit u, SootMethod constructor, List<Expression> args) { SourceLocation loc = SootTranslationHelpers.v().getSourceLocation(u); Method method = SootTranslationHelpers.v().lookupOrCreateMethod(constructor); List<Expression> receiver = new LinkedList<Expression>(); receiver.add(this.statementSwitch.getMethodInfo().getExceptionVariable()); SootClass declClass = constructor.getDeclaringClass(); SootClass currentClass = SootTranslationHelpers.v().getCurrentMethod().getDeclaringClass(); if (currentClass.hasSuperclass() && declClass.equals(currentClass.getSuperclass())) { /* * If this is a call to the super class constructor, use the return * values to assign all field locals. */ JimpleBody jb = (JimpleBody) SootTranslationHelpers.v().getCurrentMethod().getActiveBody(); Variable thisLocal = this.statementSwitch.getMethodInfo().lookupLocalVariable(jb.getThisLocal()); for (SootField sf : SootTranslationHelpers.findNonStaticFieldsRecursively(declClass)) { receiver.add(new IdentifierExpression(loc, lookupFieldLocal(thisLocal, sf))); } verifyArgLength(u, method, receiver); CallStatement stmt = new CallStatement(loc, method, args, receiver); this.statementSwitch.push(stmt); // TODO: this is a hack at should be handeled properly. // TODO: @Rody, instead of pushing after the consturctor call, // we could just use the field locals and treat the constructor // call as a pull. pushAllFields(); } else { /* * If this is not a superclass constructor, just fill the args up * with dummy variables. */ ClassVariable cv = SootTranslationHelpers.v().getClassVariable(declClass); IdentifierExpression baseExpr = (IdentifierExpression) args.get(0); List<Variable> finalFields = Arrays.asList(cv.getFinalFields()); List<AssumeStatement> assumeTupleVals = new LinkedList<AssumeStatement>(); // ignore the first outparam (for the exception) because we added // that already. int i = 0; for (SootField sf : SootTranslationHelpers.findNonStaticFieldsRecursively(declClass)) { Variable fieldLocal = lookupFieldLocal(baseExpr.getVariable(), sf); receiver.add(new IdentifierExpression(loc, fieldLocal)); Variable fieldVar = cv.getAssociatedFields()[i]; if (finalFields.contains(fieldVar)) { TupleAccessExpression tae = new TupleAccessExpression(loc, baseExpr.getVariable(), fieldVar.getName()); Expression exp = new BinaryExpression(loc, BinaryOperator.Eq, tae, new IdentifierExpression(loc, fieldLocal)); assumeTupleVals.add(new AssumeStatement(loc, exp)); } i++; } verifyArgLength(u, method, receiver); CallStatement stmt = new CallStatement(loc, method, args, receiver); this.statementSwitch.push(stmt); for (AssumeStatement s : assumeTupleVals) { // System.err.println("sfgdfgd\t" + s); //TODO: debug if this is reachable this.statementSwitch.push(s); } } } private void verifyArgLength(Unit u, Method method, List<Expression> receiver) { if (method.getReturnType().size() != receiver.size()) { StringBuilder sb = new StringBuilder(); sb.append("["); for (Type t : method.getReturnType()) { sb.append(", "); sb.append(t.toString()); } sb.append("]"); sb.append(" != "); sb.append("["); for (Expression e : receiver) { sb.append(", "); sb.append(e.toString()); } sb.append("]"); Verify.verify(false, method.getMethodName() + " -> " + sb.toString()); } } @Override public void mkCopy(Local lhs, Local rhs) { // System.out.println("Copying " + rhs + " into " + lhs); lhs.apply(valueSwitch); IdentifierExpression base = (IdentifierExpression) valueSwitch.popExpression(); for (Map.Entry<Variable, Map<String, Variable>> e : fieldToLocalMap.entrySet()) { if (e.getKey().getName().equals(rhs.getName())) { fieldToLocalMap.put(base.getVariable(), e.getValue()); return; } } } private Map<String, Variable> getMapForVar(Variable baseVar) { if (!fieldToLocalMap.containsKey(baseVar)) { fieldToLocalMap.put(baseVar, new HashMap<String, Variable>()); } return fieldToLocalMap.get(baseVar); } private Variable lookupFieldLocal(Variable baseVar, SootField sf) { Map<String, Variable> f2l = getMapForVar(baseVar); if (!f2l.containsKey(sf.getDeclaration())) { /* * for this references in constructors * we return the out params as field locals. * Unless it is a static field, then we treat it * like any other field. */ if (this.statementSwitch.getMethod().isConstructor()) { JimpleBody jb = (JimpleBody) this.statementSwitch.getMethod().getActiveBody(); Variable thisVar = statementSwitch.getMethodInfo().lookupLocalVariable(jb.getThisLocal()); if (baseVar.equals(thisVar)) { int i = 1; for (SootField cfield : SootTranslationHelpers .findNonStaticFieldsRecursively(sf.getDeclaringClass())) { if (cfield.equals(sf)) { Variable outVar = statementSwitch.getMethodInfo().getOutVariable(i); if (!f2l.containsKey(sf.getDeclaration())) { f2l.put(sf.getDeclaration(), outVar); } localToFieldMap.put(outVar, sf); break; } i++; } Verify.verify(f2l.containsKey(sf.getDeclaration())); return f2l.get(sf.getDeclaration()); } } MethodInfo currentMethodInfo = this.statementSwitch.getMethodInfo(); soottocfg.cfg.type.Type tp = this.lookupType(sf.getType()); String name = baseVar.getName() + "_" + sf.getName() + "_" + sf.getNumber(); if (sf.isStatic()) { name = sf.getDeclaringClass().getName() + "_" + sf.getName(); } boolean makeConst = SootTranslationHelpers.v().isWrittenOnce(sf); // boolean makeConst = sf.isFinal(); Variable l = currentMethodInfo.createFreshLocal(name, tp, makeConst, false); f2l.put(sf.getDeclaration(), l); // also add it to the localToFieldMap localToFieldMap.put(l, sf); } return f2l.get(sf.getDeclaration()); } private Variable lookupFieldLocal(FieldRef fieldRef) { Variable baseVar = null; if (fieldRef instanceof InstanceFieldRef) { InstanceFieldRef ifr = (InstanceFieldRef) fieldRef; ifr.getBase().apply(valueSwitch); IdentifierExpression base = (IdentifierExpression) valueSwitch.popExpression(); baseVar = base.getVariable(); } else if (fieldRef instanceof StaticFieldRef) { baseVar = getStaticFieldContainerVariable(fieldRef.getField().getDeclaringClass()); } else { throw new RuntimeException("not implemented"); } return lookupFieldLocal(baseVar, fieldRef.getField()); } public SootField lookupField(Variable local) { return localToFieldMap.get(local); } // private void setGeomPointsToAnalysis() { // HashMap<String, String> opt = new HashMap<String, String>(); // opt.put("enabled", "true"); // opt.put("verbose", "true"); // opt.put("ignore-types", "false"); // opt.put("force-gc", "false"); // // opt.put("pre-jimplify","false"); // // opt.put("vta","false"); // // opt.put("rta","false"); // // opt.put("field-based","false"); // // opt.put("types-for-sites","false"); // // opt.put("merge-stringbuffer","true"); // // opt.put("string-constants","false"); // // opt.put("simulate-natives","true"); // // opt.put("simple-edges-bidirectional","false"); // // opt.put("on-fly-cg","true"); // // opt.put("simplify-offline","false"); // // opt.put("simplify-sccs","false"); // // opt.put("ignore-types-for-sccs","false"); // // opt.put("propagator","worklist"); // opt.put("set-impl", "double"); // opt.put("double-set-old", "hybrid"); // opt.put("double-set-new", "hybrid"); // // opt.put("dump-html","false"); // // opt.put("dump-pag","false"); // // opt.put("dump-solution","false"); // // opt.put("topo-sort","false"); // // opt.put("dump-types","true"); // // opt.put("class-method-var","true"); // // opt.put("dump-answer","false"); // // opt.put("add-tags","false"); // // opt.put("set-mass","false"); // // // SparkTransformer.v().transform("",opt); // SparkOptions so = new SparkOptions(opt); // GeomPointsTo gpt = new GeomPointsTo(so); // Scene.v().setPointsToAnalysis(gpt); // } }