diff --git a/.gitignore b/.gitignore index 93fff4ccf..f68a0a678 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ .idea/ stava.iml .DS_Store + +out diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..dda89494c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Launch Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "Launch Main", + "request": "launch", + "mainClass": "Main", + "projectName": "stava_862dc55b" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..0b75109cc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "java.project.referencedLibraries": [ + "lib/**/*.jar", + "soot/sootclasses-trunk-jar-with-dependencies.jar" + ] +} \ No newline at end of file diff --git a/examples/Case1.java b/examples/Case1.java new file mode 100644 index 000000000..54a6df892 --- /dev/null +++ b/examples/Case1.java @@ -0,0 +1,28 @@ +package examples; + +public class Case1 { + public static void main(String[] args) { + Case1 obj = new Case1(); + obj.m1(); + } + + // Consider Both A and B are in their respective function stack + public void m1() { + Node A = new Node(); // A is in lower function call stackframe + m2(A); + } + + public void m2(Node A) { + Node B = new Node(); // B is in higher function call stackframe + // Thus B's lifetime is less than A + m3(A, B); + } + + public void m3(Node A, Node B) { + A.f = B; // Object pointed to by B must escape as B's lifetime is less than A's + // lifetime Else, A.f will become undefined, and will not point to any + // object once m2's function stack is poped out. + } + + // Object pointed to by B should be Heapified! +} diff --git a/examples/Case2.java b/examples/Case2.java new file mode 100644 index 000000000..85ba64f6c --- /dev/null +++ b/examples/Case2.java @@ -0,0 +1,27 @@ +package examples; + +public class Case2 { + public static void main(String[] args) { + Case1 obj = new Case1(); + obj.m1(); + } + + // Consider Both A and B are in their respective function stack + public void m1() { + Node B = new Node(); // B is in lower function call stackframe + m2(B); + } + + public void m2(Node B) { + Node A = new Node(); // A is in higher function call stackframe + // Thus B's lifetime is more than A + m3(A, B); + } + + public void m3(Node A, Node B) { + A.f = B; // Object pointed to by B need not escape as B's lifetime is more than A's + // lifetime. So, if B will not be popped before A in anycase. + } + + // Object pointed to by B need not be Heapified! +} diff --git a/examples/Case3.java b/examples/Case3.java new file mode 100644 index 000000000..75ea26770 --- /dev/null +++ b/examples/Case3.java @@ -0,0 +1,22 @@ +package examples; + +public class Case3 { + public static void main(String[] args) { + Case1 obj = new Case1(); + obj.m1(); + } + + // Consider Both A and B are in their respective function stack + public void m2() { + Node A = new Node(); + Node B = new Node(); // A and B are in the same stack frame + m3(A, B); + } + + public void m3(Node A, Node B) { + A.f = B; // Object pointed to by B need not escape as B's lifetime is equal to A's + // lifetime. So, A and B will be popped out at the same time. + } + + // Object pointed to by B need not be Heapified! +} diff --git a/examples/Challenge1.java b/examples/Challenge1.java new file mode 100644 index 000000000..235e5b8e7 --- /dev/null +++ b/examples/Challenge1.java @@ -0,0 +1,30 @@ +package examples; + +public class Challenge1 { + public static void main(String[] args) { + Challenge1 obj = new Challenge1(); + obj.m1(); + } + + public void m1() { + int x = 3; + Node A = new Node(); + Node B = new Node(); + + m2(A, B, x); + } + + public void m2(Node p1, Node p2, int x) { + if (x == 0) return; + + m3(p1, p2, x - 1); + p1.f = p2; + } + + public void m3(Node p1, Node p2, int x) { + Node C = new Node(); + Node D = new Node(); + + m2(C, D, x); + } +} diff --git a/examples/Node.java b/examples/Node.java new file mode 100644 index 000000000..402112453 --- /dev/null +++ b/examples/Node.java @@ -0,0 +1,5 @@ +package examples; + +public class Node { + public Node f; +} diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 67ec6a063..dace39cdb 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -3,7 +3,7 @@ # Set the paths according to your installation. All paths must be full paths. # Arguments: 1) name of benchmark # Installed path of Java 8 JDK -java_install_path="/usr/lib/jvm/java-8-oracle/" +java_install_path="/usr/lib/jvm/java-8-openjdk-amd64/" # java_install_path="" # java_install_path="/wd/users/t17041/benchmarks/jdk-11.0.8/" # Path to the directory containing all benchmarks. The subdirectories here must diff --git a/scripts/run.sh b/scripts/run.sh index 318dd0e82..32f771271 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -3,8 +3,14 @@ # Sample script to be used to run the project on non-benchmark code. # Set the paths according to your installation. All paths must be full paths. # Instructions: ./run.sh ClassName + +if [ -z "$1" ]; then + echo "No TestCase Number Provided"; + exit +fi + # Installed path of Java 8 JDK -java_install_path="/usr/lib/jvm/java-8-oracle/" +java_install_path="/usr/lib/jvm/java-8-openjdk-amd64/" # The soot jar to be used. soot_path=`realpath ../soot/sootclasses-trunk-jar-with-dependencies.jar` @@ -13,10 +19,10 @@ soot_path=`realpath ../soot/sootclasses-trunk-jar-with-dependencies.jar` stava_path=`realpath ..` # The directory to be analysed. -test_path=`realpath ../tests/test20/` +test_path=`realpath ../tests/test$1/` # The directory inside which stava will output the results. -output_path=`realpath ../out/testcase/` +output_path=`realpath ../out/testcase$1/` java_compiler="${java_install_path}/bin/javac" java_vm="${java_install_path}/bin/java" diff --git a/src/analyser/MethodsLinkingAnalyser.java b/src/analyser/MethodsLinkingAnalyser.java new file mode 100644 index 000000000..10065b649 --- /dev/null +++ b/src/analyser/MethodsLinkingAnalyser.java @@ -0,0 +1,232 @@ +package analyser; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import data.GlobalAnalysisData; +import handlers.JAssignStmt.JAssignStmtHandler; +import handlers.JAssignStmt.StoreStmt; +import soot.*; +import main.CHATransform; +import ptg.ObjectNode; +import ptg.ObjectType; +import ptg.PointsToGraph; +import soot.jimple.Constant; +import soot.jimple.InstanceInvokeExpr; +import soot.jimple.InvokeExpr; +import soot.jimple.internal.JAssignStmt; +import soot.jimple.internal.JInvokeStmt; +import soot.jimple.internal.JStaticInvokeExpr; +import soot.jimple.toolkits.callgraph.CallGraph; +import soot.jimple.toolkits.callgraph.Edge; +import utils.AnalysisError; + +public class MethodsLinkingAnalyser extends SceneTransformer { + public static ConcurrentHashMap> nativeLocals = new ConcurrentHashMap<>(); + + public static ArrayList whitelistedNatives = new ArrayList<>( + Arrays.asList("")); + + public static ArrayList blacklistedNatives = new ArrayList<>( + Arrays.asList("", + "", + "")); + + @Override + protected void internalTransform(String arg0, Map arg1) { + // Get the call graph, and start processing from the main method + SootClass mainClass = Scene.v().getMainClass(); + SootMethod methodCaller = mainClass.getMethodByName("main"); + processMethod(methodCaller); + } + + private void processMethod(SootMethod methodCaller) { + if (methodCaller.isJavaLibraryMethod()) { + return; + } + + if (GlobalAnalysisData.methodsProcessed.contains(methodCaller)) { + // Method already computed, we have the results + return; + } + + if (GlobalAnalysisData.methodsProcessing.contains(methodCaller)) { + // Recursive loop, we for now use the intraprocedural results for recursion + // cases, a less precise answer + return; + } + + GlobalAnalysisData.methodsProcessing.add(methodCaller); + + System.out.println("PRIYAM Method Name: " + methodCaller.getBytecodeSignature() + ":" + methodCaller.getName()); + Body body = methodCaller.getActiveBody(); + PatchingChain units = body.getUnits(); + + for (Unit u : units) { + // System.out.println("PRIYAM unit: " + u); + if (u instanceof JInvokeStmt) { + JInvokeStmt stmt = (JInvokeStmt) u; + InvokeExpr expr = stmt.getInvokeExpr(); + handleInvokeExpr(methodCaller, u, expr); + } else if (u instanceof JAssignStmt) { + handleAssignExpr(methodCaller, u); + } + } + + GlobalAnalysisData.methodsProcessing.remove(methodCaller); + GlobalAnalysisData.methodsProcessed.add(methodCaller); + } + + private void handleAssignExpr(SootMethod methodCaller, Unit u) { + JAssignStmt stmt = (JAssignStmt) u; + Value lhs = stmt.getLeftOp(); + Value rhs = stmt.getRightOp(); + if (lhs.getType() instanceof PrimType) { + if (rhs instanceof InvokeExpr) { + handleInvokeExpr(methodCaller, u, (InvokeExpr) rhs); + } + return; + } else { + JAssignStmtHandler.handle(methodCaller, u, GlobalAnalysisData.ptgs.get(methodCaller), + GlobalAnalysisData.summaries.get(methodCaller)); + } + } + + private void handleInvokeExpr(SootMethod methodCaller, Unit u, InvokeExpr expr) { + PointsToGraph ptg = GlobalAnalysisData.ptgs.get(methodCaller); + + if (ptg == null) { + // If no points to graph, no need to process further + // System.out.println("PTG: " + ptg); + System.out.println("PRIYAM handleExpr PTG null, returning for: " + u); + return; + } + + CallGraph cg = Scene.v().getCallGraph(); + + // System.out.println("PRIYAM handlExpr: " + u); + Iterator iedges = cg.edgesOutOf(u); + List args = expr.getArgs(); + + // System.out.println("PRIYAM handlExpr" + u); + + List edges = new ArrayList<>(); + if (!iedges.hasNext()) { + iedges = CHATransform.getSpark().edgesOutOf(u); + } + + while (iedges.hasNext()) { + edges.add(iedges.next()); + } + + if (edges.size() == 0) { + // System.out.println("Empty edges: " + expr + ", function incoming edges: " + + // cg.edgesInto(methodCaller).hasNext() + + // " Method: " + methodCaller.getBytecodeSignature()); + edges.add(new Edge(methodCaller, u, expr.getMethod(), Kind.SPECIAL)); + } else { + // System.out.println("Edges for u: " + u + ": " + edges); + } + + if (expr instanceof InstanceInvokeExpr) { + // ASK - No need to handle instance invoke expr? + } else if (expr instanceof JStaticInvokeExpr) { + } else { + System.err.println("Unidentified invoke expr: " + expr.toString()); + throw new IllegalArgumentException(expr.toString()); + } + + for (Edge edge : edges) { + /* + * 1. + * We traverse and find the caller callie relationship + * between ObjectNodes + */ + SootMethod method = edge.tgt(); + int paramCount = method.getParameterCount(); + + // Recursion to first process the method + // if not already processed + if (!GlobalAnalysisData.methodsProcessed.contains(method)) { + processMethod(method); + } + + Map> paramMapping = new HashMap>(); + for (int i = 0; i < paramCount; i++) { + ObjectNode obj = new ObjectNode(i, ObjectType.parameter); + // ConditionalValue cv = new ConditionalValue(method, obj, true); + + if (edge.kind() == Kind.REFL_INVOKE) + paramMapping.put(obj, ptg.vars.get((Local) args.get(1))); + else if (edge.kind() == Kind.REFL_CONSTR_NEWINSTANCE) + paramMapping.put(obj, ptg.vars.get((Local) args.get(0))); + else { + Value arg = args.get(i); + if (arg.getType() instanceof RefType || arg.getType() instanceof ArrayType) + if (!(arg instanceof Constant)) { // Notice the not(!) + // ptg.addParametricEdge((Local) args.get(i), cv); + // System.out.println("args get i: " + args.get(i)); + paramMapping.put(obj, ptg.vars.get((Local) args.get(i))); + } + } + } + + // System.out.println("PRIYAM PARAMS MAPPING: " + paramMapping); + + /* + * 2. + * Now, loop in the callie method's ptg to find if there + * exists any relationship/node between the params + * If exists, add the realtion for corresponding values in + * paramsMapping also + */ + PointsToGraph calliePTG = GlobalAnalysisData.ptgs.get(method); + // System.out.println("PRIYAM METHOD: " + method); + // System.out.println("PRIYAM PTGS: " + GlobalAnalysisData.ptgs); + // System.out.println("PRIYAM calliePTG: " + calliePTG); + // If ptg gives error, ensure StaticAnalysis has been done + + for (int i = 0; i < paramCount; i++) { + ObjectNode obj = new ObjectNode(i, ObjectType.parameter); + Map> pointingTo = calliePTG.fields.get(obj); + + if (pointingTo == null) { + continue; + } + + for (Map.Entry> entry : pointingTo.entrySet()) { + for (ObjectNode fieldObj : entry.getValue()) { + System.out.println("There exists an edge from: " + obj + " to " + fieldObj + " by " + + entry.getKey() + " when calling from " + methodCaller + " to " + method); + if (fieldObj.type != ObjectType.parameter) { + continue; + } + + if (!paramMapping.containsKey(obj) || !paramMapping.containsKey(fieldObj)) { + // If paramsMapping does not have the object, it can happen if null is passed + continue; + } + + // System.out.println("Param Mapping for obj: " + paramMapping.get(obj)); + // System.out.println("Param Mapping for fieldObj: " + + // paramMapping.get(fieldObj)); + + // Find paramsMapping for obj + // Find paramsMapping for fieldObj + // Add an edge from objs to fieldObjs + for (ObjectNode objInCaller : paramMapping.get(obj)) { + for (ObjectNode fieldObjInCaller : paramMapping.get(fieldObj)) { + System.out.println("There should exists an edge from: " + objInCaller + " to " + + fieldObjInCaller + " by " + entry.getKey()); + ptg.WEAK_makeField(objInCaller, entry.getKey(), fieldObjInCaller); + // System.out.println("AFTER MAKE FIELD" + ptg); + } + } + } + } + } + + // System.out.println("AFTER PRIYAM PTGS: " + GlobalAnalysisData.ptgs + "\n"); + } + } +} diff --git a/src/analyser/StaticAnalyser.java b/src/analyser/StaticAnalyser.java index d07487aea..b87bf762c 100644 --- a/src/analyser/StaticAnalyser.java +++ b/src/analyser/StaticAnalyser.java @@ -22,13 +22,12 @@ import java.util.regex.*; +import data.GlobalAnalysisData; + public class StaticAnalyser extends BodyTransformer { private boolean allNonEscaping; - public static Map ptgs; - public static Map> summaries; - public static LinkedHashMap analysis; - public static List noBCIMethods; + String[] ignoreFuncs = { // "", // "", @@ -71,10 +70,6 @@ public class StaticAnalyser extends BodyTransformer { public StaticAnalyser() { super(); - analysis = new LinkedHashMap<>(); - ptgs = new ConcurrentHashMap<>(); - summaries = new ConcurrentHashMap<>(); - noBCIMethods = new ArrayList<>(); allNonEscaping = false; } @@ -93,7 +88,18 @@ protected void internalTransform(Body body, String phasename, Map")){ // System.out.println("Skipping this method"); // return; @@ -222,9 +228,9 @@ protected void internalTransform(Body body, String phasename, Map 10000) // return; } catch (IllegalBCIException e) { - noBCIMethods.add(body.getMethod()); + GlobalAnalysisData.noBCIMethods.add(body.getMethod()); setParamsAsEscaping(body.getMethod(),summary); - summaries.put(body.getMethod(), summary); + GlobalAnalysisData.summaries.put(body.getMethod(), summary); String s = "->*** Error at: " + u.toString() + " of " + body.getMethod().getBytecodeSignature(); System.err.println(s); System.err.println(e); @@ -290,13 +296,13 @@ protected void internalTransform(Body body, String phasename, Map elem = iterator.next(); while (iterator.hasNext()) elem = iterator.next(); PointsToGraph ptg = elem.getValue().getOut(); - ptgs.put(body.getMethod(), ptg); + GlobalAnalysisData.ptgs.put(body.getMethod(), ptg); if (allNonEscaping) { markAsNonEscaping(summary); markAsEscaping(JInvokeStmtHandler.nativeLocals.get(body.getMethod()), summary, ptg); } - summaries.put(body.getMethod(), summary); - System.out.println("Method Name: "+ body.getMethod().getBytecodeSignature() + ":"+body.getMethod().getName()); + GlobalAnalysisData.summaries.put(body.getMethod(), summary); + System.out.println("Static Method Name: "+ body.getMethod().getBytecodeSignature() + ":"+body.getMethod().getName()); } private void markAsEscaping(List nativeList, Map summary, PointsToGraph ptg) { @@ -332,6 +338,10 @@ private void setParamsAsEscaping(SootMethod m, Map sum public void apply(SootMethod m, Unit u, PointsToGraph ptg, Map summary) { // System.err.println(u+" "+u.getClass().getName()); + + // System.out.println("PRIYAM soot unit " + u); + // System.out.println("PRIYAM soot ptg " + ptg); + if (u instanceof JAssignStmt) { JAssignStmtHandler.handle(m, u, ptg, summary); } else if (u instanceof JIdentityStmt) { @@ -355,7 +365,7 @@ public void apply(SootMethod m, Unit u, PointsToGraph ptg, Map entry : analysis.entrySet()) { + for (Map.Entry entry : GlobalAnalysisData.analysis.entrySet()) { System.out.println("Class: " + entry.getKey().getMethod().getDeclaringClass()); System.out.println("Method: " + entry.getKey().getMethod().getName()); System.out.println("Analysis:\n" + entry.getValue()); diff --git a/src/config/StoreEscape.java b/src/config/StoreEscape.java index b1185bd80..8d595cbd2 100644 --- a/src/config/StoreEscape.java +++ b/src/config/StoreEscape.java @@ -1,7 +1,7 @@ package config; public class StoreEscape { - public static boolean MarkStoreEscaping = true; + public static boolean MarkStoreEscaping = false; public static boolean ReduceParamDependence = true; public static boolean MarkParamReturnEscaping = false; } diff --git a/src/data/GlobalAnalysisData.java b/src/data/GlobalAnalysisData.java new file mode 100644 index 000000000..327c36f53 --- /dev/null +++ b/src/data/GlobalAnalysisData.java @@ -0,0 +1,29 @@ +package data; + +import es.EscapeStatus; +import ptg.*; +import soot.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class GlobalAnalysisData { + public static Map ptgs; + public static Map> summaries; + public static Map> stackOrders; + public static LinkedHashMap analysis; + public static List noBCIMethods; + + // If the methods have been processed interprocedurally + public static Set methodsProcessed; + public static Set methodsProcessing; + + public GlobalAnalysisData() { + analysis = new LinkedHashMap<>(); + ptgs = new ConcurrentHashMap<>(); + summaries = new ConcurrentHashMap<>(); + stackOrders = new ConcurrentHashMap<>(); + noBCIMethods = new ArrayList<>(); + methodsProcessed = new HashSet(); + methodsProcessing = new HashSet(); + } +} diff --git a/src/main/GetSootArgs.java b/src/main/GetSootArgs.java index adb992a9b..a1e13877c 100644 --- a/src/main/GetSootArgs.java +++ b/src/main/GetSootArgs.java @@ -213,6 +213,7 @@ private String[] normal(String[] args){ "-p","cg.spark","on", "-p","cg","all-reachable", "-keep-offset", + "-f","J", // "-soot-classpath", cp, //"-prepend-classpath", "-keep-line-number", "-main-class", args[3], diff --git a/src/main/Main.java b/src/main/Main.java index c1f7c7328..95bb9012b 100644 --- a/src/main/Main.java +++ b/src/main/Main.java @@ -1,7 +1,9 @@ package main; +import analyser.MethodsLinkingAnalyser; import analyser.StaticAnalyser; import config.StoreEscape; +import data.GlobalAnalysisData; import es.*; import ptg.ObjectNode; import ptg.PointsToGraph; @@ -27,7 +29,6 @@ import java.util.Map; import java.util.*; import java.io.*; -import java.lang.*; import static utils.KillCallerOnly.kill; @@ -50,7 +51,7 @@ static void setStoreEscapeOptions(String[] args) { } } public static void main(String[] args) { - + // Generating soot args GetSootArgs g = new GetSootArgs(); String[] sootArgs = g.get(args); setStoreEscapeOptions(args); @@ -58,22 +59,19 @@ public static void main(String[] args) { System.out.println("Unable to generate args for soot!"); return; } + + GlobalAnalysisData globalAnlaysisData = new GlobalAnalysisData(); StaticAnalyser staticAnalyser = new StaticAnalyser(); CHATransform prepass = new CHATransform(); + MethodsLinkingAnalyser methodsLinkingAnalyser = new MethodsLinkingAnalyser(); PackManager.v().getPack("wjap").add(new Transform("wjap.pre", prepass)); PackManager.v().getPack("jtp").add(new Transform("jtp.sample", staticAnalyser)); + long analysis_start = System.currentTimeMillis(); Options.v().parse(sootArgs); Scene.v().loadNecessaryClasses(); Scene.v().loadDynamicClasses(); List entryPoints = Scene.v().getEntryPoints(); - // SootClass sc = Scene.v().loadClassAndSupport("java.lang.CharacterData"); - // System.out.println(sc.getMethods()); - // Scene.v().forceResolve(sc.getName(), SootClass.BODIES); - // SootMethod tobeAdded = sc.getMethodByName("toUpperCaseEx"); - // System.out.println("Method: "+tobeAdded); - // // SootMethod tobeAdded = Scene.v().getMethod(""); - // entryPoints.add(tobeAdded); Chain appClasses = Scene.v().getClasses(); Iterator appClassItertator = appClasses.iterator(); @@ -92,75 +90,133 @@ public static void main(String[] args) { // System.out.println("SuperClass: "+aclass.getSuperclass()); } } - // aclass = Scene.v().loadClassAndSupport(aclass.getName()); - // aclass = Scene.v().forceResolve(aclass.getName(), SootClass.BODIES); - // // if (aclass.getName().contains("spec.validity.Digests")) { - // // System.out.println("Aclass spec: "+aclass.getName()+" : "+aclass.getMethodByName("crunch_jars")); - // // } - // System.out.println("Aclass: "+aclass.getName()+ " phantom: "+aclass.isPhantomClass()+" app: "+aclass.isApplicationClass()+" Concrete: "+ - // aclass.isConcrete()+" : " + aclass.getMethods()); - // // System.out.println(aclass.getMethods()); - // entryPoints.addAll(aclass.getMethods()); } - // System.out.println(entryPoints); - // if (true) - // return; - Scene.v().setEntryPoints(entryPoints); + // Scene.v().setEntryPoints(entryPoints); + // System.out.println("All Packs" + PackManager.v().allPacks()); PackManager.v().runPacks(); - // soot.Main.main(sootArgs); + long analysis_end = System.currentTimeMillis(); System.out.println("Static Analysis is done!"); System.out.println("Time Taken:"+(analysis_end-analysis_start)/1000F); - + System.out.println("BEFORE InterProcedural Linking"); + printAllInfo(GlobalAnalysisData.ptgs, GlobalAnalysisData.summaries, GlobalAnalysisData.stackOrders, args[4]); + + // // Problem - not processing methods + + // First, we add edges created due to callie function during method call + analysis_start = System.currentTimeMillis(); + // How to use spark here? + // StackOrderAnalyser stackOrderAnalyser = new StackOrderAnalyser(); + // PackManager.v().getPack("jtp").add(new Transform("jtp.order", stackOrderAnalyser)); + // Scene.v().setEntryPoints(entryPoints); + + PackManager.v().getPack("wjtp").add(new Transform("wjtp.lfc", methodsLinkingAnalyser)); + PackManager.v().runPacks(); + + // Now we are going to find the stack ordering of the non escaping functions + CreateStackOrdering(); + analysis_end = System.currentTimeMillis(); + System.out.println("InterProcedural Linking is done!"); + System.out.println("Time Taken:"+(analysis_end-analysis_start)/1000F); + boolean useNewResolver = true; long res_start = System.currentTimeMillis(); - // printSummary(staticAnalyser.summaries); - // System.err.println(staticAnalyser.ptgs); - printAllInfo(StaticAnalyser.ptgs, staticAnalyser.summaries, args[4]); - // if (true) - // return; - // printCFG(); + System.out.println("BEFORE Resolver"); + printAllInfo(GlobalAnalysisData.ptgs, GlobalAnalysisData.summaries, GlobalAnalysisData.stackOrders, args[4]); if(useNewResolver) { - ReworkedResolver sr = new ReworkedResolver(staticAnalyser.summaries, - staticAnalyser.ptgs, - staticAnalyser.noBCIMethods); + ReworkedResolver sr = new ReworkedResolver( + GlobalAnalysisData.summaries, + GlobalAnalysisData.ptgs, + GlobalAnalysisData.noBCIMethods); long res_end = System.currentTimeMillis(); System.out.println("Resolution is done"); System.out.println("Time Taken in phase 1:"+(analysis_end-analysis_start)/1000F); System.out.println("Time Taken in phase 2:"+(res_end-res_start)/1000F); - - // System.out.println(staticAnalyser.summaries.size()+ " "+staticAnalyser.ptgs.size()); - - HashMap> resolved = (HashMap) kill(sr.solvedSummaries); - - printAllInfo(StaticAnalyser.ptgs, resolved, args[4]); - - saveStats(sr.existingSummaries, resolved, args[4], staticAnalyser.ptgs); - - printResForJVM(sr.solvedSummaries, args[2], args[4]); + System.out.println("AFTER Resolver"); + printAllInfo(GlobalAnalysisData.ptgs, GlobalAnalysisData.summaries, GlobalAnalysisData.stackOrders, args[4]); + printResForJVM(sr.solvedSummaries, GlobalAnalysisData.stackOrders, args[2], args[4]); } else { SummaryResolver sr = new SummaryResolver(); - sr.resolve(staticAnalyser.summaries, staticAnalyser.ptgs); + sr.resolve(GlobalAnalysisData.summaries, GlobalAnalysisData.ptgs); long res_end = System.currentTimeMillis(); System.out.println("Resolution is done"); System.out.println("Time Taken:"+(res_end-res_start)/1000F); - // System.out.println(staticAnalyser.summaries.size()+ " "+staticAnalyser.ptgs.size()); + // System.out.println(GlobalAnalysisData.summaries.size()+ " "+GlobalAnalysisData.ptgs.size()); HashMap> resolved = (HashMap) kill(sr.solvedSummaries); - printAllInfo(StaticAnalyser.ptgs, staticAnalyser.summaries, args[4]); + // printAllInfo(GlobalAnalysisData.ptgs, GlobalAnalysisData.summaries, args[4]); - printAllInfo(StaticAnalyser.ptgs, resolved, args[4]); + // printAllInfo(GlobalAnalysisData.ptgs, resolved, args[4]); - saveStats(sr.existingSummaries, resolved, args[4], staticAnalyser.ptgs); + // saveStats(sr.existingSummaries, resolved, args[4], GlobalAnalysisData.ptgs); - printResForJVM(sr.solvedSummaries, args[2], args[4]); + // printResForJVM(sr.solvedSummaries, args[2], args[4]); + } + } + + /** + * Performs dfs and finds the topological order + * @param node - Starting node of the dfs + * @param ptg - Points to graph + * @param visited - A visited array to have an idea of dfs + * @param topoOrder - The final result of the dfs - Topological Order + */ + static void topologicalSortDfs( + ObjectNode node, + PointsToGraph ptg, + HashSet visited, + ArrayList topoOrder) { + visited.add(node); + + Map> objectNodesMap = ptg.fields.get(node); + if (objectNodesMap != null) { + for (SootField sootField : objectNodesMap.keySet()) { + for (ObjectNode nextObject : objectNodesMap.get(sootField)) { + if (!visited.contains(nextObject)) { + topologicalSortDfs(nextObject, ptg, visited, topoOrder); + } + } + } + } + + topoOrder.add(node); + } + + /** + * Create Stack ordering for each ptg in the Static Analyser + */ + static void CreateStackOrdering() { + // We assumed that the PTGs exist in the GlobalAnalysisData + + System.out.println("PRIYAM - Starting topological sorting"); + for(SootMethod method : GlobalAnalysisData.ptgs.keySet()) { + PointsToGraph ptg = GlobalAnalysisData.ptgs.get(method); + + // TODO - First check if it is a DAG + // TO ASK - Peform a check before or combine with the + // topological dfs + // Perform topological sort for the ptg + + HashSet visited = new HashSet(); + ArrayList topoOrder = new ArrayList(); + + for(Set objectNodeSet : ptg.vars.values()) { + for (ObjectNode object : objectNodeSet) { + if (!visited.contains(object)) { + // System.out.println("PRIYAM object" + object); + topologicalSortDfs(object, ptg, visited, topoOrder); + } + } + } + + GlobalAnalysisData.stackOrders.put(method, topoOrder); } } @@ -215,8 +271,11 @@ public int compare(EscapeState a, EscapeState b) } } - private static void printAllInfo(Map ptgs, - Map> summaries, String opDir) { + private static void printAllInfo( + Map ptgs, + Map> summaries, + Map> stackOrders, + String opDir) { Path p_opDir = Paths.get(opDir); for (Map.Entry entry : ptgs.entrySet()) { @@ -229,7 +288,15 @@ private static void printAllInfo(Map ptgs, output.append("PTG:\n"); output.append(ptg.toString()); output.append("\nSummary\n"); - output.append(summaries.get(method).toString() + "\n"); + output.append(summaries.get(method).toString()); + + if (stackOrders.containsKey(method)) { + output.append("\nStackOrder\n"); + output.append(stackOrders.get(method).toString() + "\n\n"); + } else { + output.append("\nNo stack Ordering exists for : " + method + "\n\n"); + } + try { Files.write(p_opFile, output.toString().getBytes(StandardCharsets.UTF_8), Files.exists(p_opFile) ? StandardOpenOption.APPEND : StandardOpenOption.CREATE); @@ -240,6 +307,7 @@ private static void printAllInfo(Map ptgs, } } } + static String transformFuncSignature(String inputString) { StringBuilder finalString = new StringBuilder(); for(int i=1;i> summaries, String ipDir, String opDir) { + + static void printResForJVM( + Map> summaries, + Map> stackOrders, + String ipDir, + String opDir) { // Open File Path p_ipDir = Paths.get(ipDir); Path p_opDir = Paths.get(opDir); @@ -266,9 +339,10 @@ static void printResForJVM(Map> su HashMap summary = entry.getValue(); sb.append(transformFuncSignature(method.getBytecodeSignature())); sb.append(" "); - sb.append(GetListOfNoEscapeObjects.get(summary)); + sb.append(GetListOfNoEscapeObjects.get(summary, stackOrders.get(method))); sb.append("\n"); } + try { System.out.println("Trying to write to:" + p_opFile); Files.write(p_opFile, sb.toString().getBytes(StandardCharsets.UTF_8), diff --git a/src/stackOrdering/StackOrderer.java b/src/stackOrdering/StackOrderer.java new file mode 100644 index 000000000..15e40829a --- /dev/null +++ b/src/stackOrdering/StackOrderer.java @@ -0,0 +1,5 @@ +package stackOrdering; + +public class StackOrderer { + +} diff --git a/src/utils/GetListOfNoEscapeObjects.java b/src/utils/GetListOfNoEscapeObjects.java index 021f268d0..46ce44a13 100644 --- a/src/utils/GetListOfNoEscapeObjects.java +++ b/src/utils/GetListOfNoEscapeObjects.java @@ -10,16 +10,18 @@ import java.util.Map; public class GetListOfNoEscapeObjects { - public static String get(HashMap summary) { + public static String get( + HashMap summary, + ArrayList stackOrder) { ArrayList arr = new ArrayList<>(); - for (Map.Entry entry : summary.entrySet()) { - ObjectNode obj = entry.getKey(); - if(obj.type != ObjectType.internal) + for (ObjectNode obj : stackOrder) { + if (obj.type != ObjectType.internal) continue; - EscapeStatus es = entry.getValue(); - if (es.containsNoEscape()) arr.add(obj.ref); + EscapeStatus es = summary.get(obj); + if (es.containsNoEscape()) + arr.add(obj.ref); } - Collections.sort(arr); + String _ret = arr.toString(); return _ret; } diff --git a/tests/test1/Main.java b/tests/test1/Main.java index 64b3938fa..6ca52a26a 100644 --- a/tests/test1/Main.java +++ b/tests/test1/Main.java @@ -5,8 +5,7 @@ public static void main(String[] args) { Node C = new Node(); Node D = new Node(); A.n = B; - B.n = C; - C.n = D; - D.n = B; + B.n = D; + D.n = C; } } diff --git a/tests/test23/Main.java b/tests/test23/Main.java new file mode 100644 index 000000000..9cf2b64ff --- /dev/null +++ b/tests/test23/Main.java @@ -0,0 +1,13 @@ +public class Main { + public static void main(String[] args) { + Node A = new Node(); + Node B = new Node(); + Node C = new Node(); + Node D = new Node(); + Node E = new Node(); + A.n = B; + B.n = D; + B.m = E; + D.n = C; + } +} diff --git a/tests/test23/Node.java b/tests/test23/Node.java new file mode 100644 index 000000000..391eba7af --- /dev/null +++ b/tests/test23/Node.java @@ -0,0 +1,4 @@ +public class Node { + public Node n; + public Node m; +} diff --git a/tests/test24/Main.java b/tests/test24/Main.java new file mode 100644 index 000000000..88678ebb1 --- /dev/null +++ b/tests/test24/Main.java @@ -0,0 +1,23 @@ +public class Main { + public static void main(String[] args) { + foo(null, null); + } + + static void foo(Node node1, Node node2) { + Node A = new Node(); + Node B = new Node(); + Node C = new Node(); + Node D = new Node(); + Node E = new Node(); + bar(A, B); + bar(A, C); + bar(D, E); + bar(node1, node2); + + // see next test28 + } + + static void bar(Node n1, Node n2) { + n1.n = n2; + } +} diff --git a/tests/test24/Node.java b/tests/test24/Node.java new file mode 100644 index 000000000..391eba7af --- /dev/null +++ b/tests/test24/Node.java @@ -0,0 +1,4 @@ +public class Node { + public Node n; + public Node m; +} diff --git a/tests/test25/Main.java b/tests/test25/Main.java new file mode 100644 index 000000000..e9b503f51 --- /dev/null +++ b/tests/test25/Main.java @@ -0,0 +1,26 @@ +public class Main { + public static void main(String[] args) { + } + + static void foo(Node node1, Node node2) { + Node A = new Node(); + Node B = new Node(); + Node C = new Node(); + Node D = new Node(); + Node E = new Node(); + bar(A, B); + bar(A, C); + bar(D, E); + bar(node1, node2); + } + + static void bar(Node n1, Node n2) { + Node F = new Node(); + n1.n = F; // we need to heapify F (address check) + F.n = n2; // no heapfication + + // Assumption 2 + // Theory -- If two objects are directly connected + // Then only we need stack order, else, we will not be needing any stack order + } +} diff --git a/tests/test25/Node.java b/tests/test25/Node.java new file mode 100644 index 000000000..391eba7af --- /dev/null +++ b/tests/test25/Node.java @@ -0,0 +1,4 @@ +public class Node { + public Node n; + public Node m; +} diff --git a/tests/test26/Main.java b/tests/test26/Main.java new file mode 100644 index 000000000..39b7ecb7d --- /dev/null +++ b/tests/test26/Main.java @@ -0,0 +1,19 @@ +public class Main { + public static void main(String[] args) { + Node x = new Node(); + Node y = new Node(); + foo(x, y); // There should come a linking for x and y from baz + } + + static void foo(Node node1, Node node2) { + bar(node1, node2); + } + + static void bar(Node n1, Node n2) { + baz(n1, n2); + } + + static void baz(Node p, Node q) { + p.n = q; // Linking the objects here + } +} diff --git a/tests/test26/Node.java b/tests/test26/Node.java new file mode 100644 index 000000000..391eba7af --- /dev/null +++ b/tests/test26/Node.java @@ -0,0 +1,4 @@ +public class Node { + public Node n; + public Node m; +} diff --git a/tests/test27/Main.java b/tests/test27/Main.java new file mode 100644 index 000000000..60829bb79 --- /dev/null +++ b/tests/test27/Main.java @@ -0,0 +1,32 @@ +public class Main { + public static void main(String[] args) { + // A recursive and multiple params test + } + + static void foo(Node node1, Node node2) { + Node A = new Node(); + Node B = new Node(); + Node C = new Node(); + Node D = new Node(); + Node E = new Node(); + Node F = new Node(); + Node G = new Node(); + + Main obj = new Main(); + obj.bar(A, B, C, D, E, F, G); + } + + void bar(Node n1, Node n2, Node n3, Node n4, Node n5, Node n6, Node n7) { + if (n7 == null) { + return; + } + + n1.n = n3; + n4.n = n5; + n5.n = n6; + n6.n = n2; + n2.n = n3; + + bar(n2, n3, n4, n5, n6, n7, null); + } +} diff --git a/tests/test27/Node.java b/tests/test27/Node.java new file mode 100644 index 000000000..391eba7af --- /dev/null +++ b/tests/test27/Node.java @@ -0,0 +1,4 @@ +public class Node { + public Node n; + public Node m; +} diff --git a/tests/test28/Main.java b/tests/test28/Main.java new file mode 100644 index 000000000..10ade0f0b --- /dev/null +++ b/tests/test28/Main.java @@ -0,0 +1,34 @@ +public class Main { + public static void main(String[] args) { + foo(null, null); + } + + static void foo(Node node1, Node node2) { + Node A = new Node(); + Node B = new Node(); + Node C = new Node(); + Node D = new Node(); + Node E = new Node(); + bar(A, B); + // A.n = B; + + // if I make channges in staticAnalyser? + + Node x = A.n; + + // r10=[, ] + // Assumption 1 + // we do replacement of assignment + // statements after the whole pass + // How to consider for if else branch? + // For different branches? + + bar(x, C); + bar(D, E); + bar(node1, node2); + } + + static void bar(Node n1, Node n2) { + n1.n = n2; + } +} diff --git a/tests/test28/Node.java b/tests/test28/Node.java new file mode 100644 index 000000000..391eba7af --- /dev/null +++ b/tests/test28/Node.java @@ -0,0 +1,4 @@ +public class Node { + public Node n; + public Node m; +}