diff --git a/src/main/java/br/unb/cic/analysis/df/ConfluentTaintedAnalysis.java b/src/main/java/br/unb/cic/analysis/df/ConfluentTaintedAnalysis.java new file mode 100644 index 00000000..86fa5091 --- /dev/null +++ b/src/main/java/br/unb/cic/analysis/df/ConfluentTaintedAnalysis.java @@ -0,0 +1,75 @@ +package br.unb.cic.analysis.df; + +import br.unb.cic.analysis.AbstractMergeConflictDefinition; +import br.unb.cic.analysis.model.Conflict; +import br.unb.cic.analysis.model.ConfluenceConflictReport; +import br.unb.cic.analysis.model.Statement; +import soot.Body; +import soot.Local; +import soot.Unit; +import soot.toolkits.scalar.ArraySparseSet; +import soot.toolkits.scalar.FlowSet; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class ConfluentTaintedAnalysis extends ReachDefinitionAnalysis { + + public ConfluentTaintedAnalysis(Body methodBody, AbstractMergeConflictDefinition definition) { + super(methodBody, definition); + } + + @Override + protected FlowSet gen(Unit u, FlowSet in) { + FlowSet res = new ArraySparseSet<>(); + if(isSourceStatement(u) || isSinkStatement(u)) { + for(Local local: getDefVariables(u)) { + Statement stmt = isSourceStatement(u) ? findSourceStatement(u) : findSinkStatement(u); + res.add(new DataFlowAbstraction(local, stmt)); + } + } + else if (u.getDefBoxes().size() > 0) { + u.getUseBoxes().stream().filter(v -> v.getValue() instanceof Local).forEach(v -> { + Local local = (Local) v.getValue(); + in.forEach(sourceDefs -> { + if(sourceDefs.getLocal().equals(local)){ //if variable in the analyzed stmt is present in IN + u.getDefBoxes().stream() + .filter(def -> def.getValue() instanceof Local) + .forEach(def -> { + res.add(new DataFlowAbstraction((Local)def.getValue(), findStatement(u))); //add variable assigned as the stmt to IN + }); + } + }); + }); + } + return res; + } + + @Override + protected void detectConflict(FlowSet in, Unit u){ + if(isSourceStatement(u) || isSinkStatement(u)){ + return; + } + List sources = new ArrayList<>(); + List sinks = new ArrayList<>(); + + for(Local local: getUseVariables(u)){ + sources.addAll(in.toList().stream() + .filter(element -> element.getStmt().getType().equals(Statement.Type.SOURCE) + && element.getLocal().equals(local)) + .map(item -> item.getStmt()).collect(Collectors.toList())); + sinks.addAll(in.toList().stream() + .filter(element -> element.getStmt().getType().equals(Statement.Type.SINK) + && element.getLocal().equals(local)) + .map(item -> item.getStmt()).collect(Collectors.toList())); + } + + for(Statement source: sources){ + for(Statement sink: sinks){ + Conflict c = new ConfluenceConflictReport(source, sink, findStatement(u)); + Collector.instance().addConflict(c); + } + } + } +} diff --git a/src/main/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysis.java b/src/main/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysis.java index b61b297a..d740244f 100644 --- a/src/main/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysis.java +++ b/src/main/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysis.java @@ -2,7 +2,7 @@ import br.unb.cic.analysis.AbstractMergeConflictDefinition; import br.unb.cic.analysis.model.Conflict; -import br.unb.cic.analysis.model.DoubleSourceConflict; +import br.unb.cic.analysis.model.ConfluenceConflictReport; import br.unb.cic.analysis.model.Statement; import soot.Body; import soot.Local; @@ -56,7 +56,7 @@ protected void detectConflict(FlowSet in, Unit u) { //report the conflicts for(Statement source: sources) { for(Statement sink: sinks) { - Conflict c = new DoubleSourceConflict(source, sink, findStatement(u)); + Conflict c = new ConfluenceConflictReport(source, sink, findStatement(u)); Collector.instance().addConflict(c); } } diff --git a/src/main/java/br/unb/cic/analysis/model/DoubleSourceConflict.java b/src/main/java/br/unb/cic/analysis/model/ConfluenceConflictReport.java similarity index 85% rename from src/main/java/br/unb/cic/analysis/model/DoubleSourceConflict.java rename to src/main/java/br/unb/cic/analysis/model/ConfluenceConflictReport.java index 6c9de669..be7545e2 100644 --- a/src/main/java/br/unb/cic/analysis/model/DoubleSourceConflict.java +++ b/src/main/java/br/unb/cic/analysis/model/ConfluenceConflictReport.java @@ -2,13 +2,13 @@ import java.util.Objects; -public class DoubleSourceConflict extends Conflict { +public class ConfluenceConflictReport extends Conflict { protected String targetClassName; protected String targetMethodName; protected Integer targetLineNumber; - public DoubleSourceConflict(Statement source, Statement sink, Statement target) { + public ConfluenceConflictReport(Statement source, Statement sink, Statement target) { super(source, sink); targetClassName = target.getSootClass().getName(); targetMethodName = target.getSootMethod().getName(); @@ -20,7 +20,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; - DoubleSourceConflict that = (DoubleSourceConflict) o; + ConfluenceConflictReport that = (ConfluenceConflictReport) o; return Objects.equals(targetClassName, that.targetClassName) && Objects.equals(targetMethodName, that.targetMethodName) && Objects.equals(targetLineNumber, that.targetLineNumber); diff --git a/src/test/java/br/unb/cic/analysis/df/TaintedAnalysisConfluentTest.java b/src/test/java/br/unb/cic/analysis/df/ConfluenceWithTransitivityTest.java similarity index 69% rename from src/test/java/br/unb/cic/analysis/df/TaintedAnalysisConfluentTest.java rename to src/test/java/br/unb/cic/analysis/df/ConfluenceWithTransitivityTest.java index 52ca30e4..6adb603b 100644 --- a/src/test/java/br/unb/cic/analysis/df/TaintedAnalysisConfluentTest.java +++ b/src/test/java/br/unb/cic/analysis/df/ConfluenceWithTransitivityTest.java @@ -6,15 +6,16 @@ import org.junit.Before; import org.junit.Test; import soot.*; +import soot.options.Options; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -public class TaintedAnalysisConfluentTest { +public class ConfluenceWithTransitivityTest { - private TaintedAnalysis analysis; + private ConfluentTaintedAnalysis analysis; @Before public void configure() { @@ -26,8 +27,8 @@ public void configure() { protected Map> sourceDefinitions() { Map> res = new HashMap<>(); List lines = new ArrayList<>(); - lines.add(8); - res.put("br.unb.cic.analysis.samples.DoubleSourceSample", lines); + lines.add(9); + res.put("br.unb.cic.analysis.samples.ConfluenceWithTransitivitySample", lines); return res; } @@ -35,8 +36,8 @@ protected Map> sourceDefinitions() { protected Map> sinkDefinitions() { Map> res = new HashMap<>(); List lines = new ArrayList<>(); - lines.add(12); - res.put("br.unb.cic.analysis.samples.DoubleSourceSample", lines); + lines.add(13); + res.put("br.unb.cic.analysis.samples.ConfluenceWithTransitivitySample", lines); return res; } }; @@ -45,17 +46,19 @@ protected Map> sinkDefinitions() { new Transform("jtp.zeroConflict", new BodyTransformer() { @Override protected void internalTransform(Body body, String phaseName, Map options) { - analysis = new TaintedAnalysis(body, definition); + analysis = new ConfluentTaintedAnalysis(body, definition); } })); String cp = "target/test-classes"; - String targetClass = "br.unb.cic.analysis.samples.DoubleSourceSample"; + String targetClass = "br.unb.cic.analysis.samples.ConfluenceWithTransitivitySample"; + + PhaseOptions.v().setPhaseOption("jb", "use-original-names:true"); SootWrapper.builder().withClassPath(cp).addClass(targetClass).build().execute(); } @Test public void testDataFlowAnalysisExpectingOneConflict() { - Assert.assertEquals(0, analysis.getConflicts().size()); + Assert.assertEquals(1, analysis.getConflicts().size()); } } diff --git a/src/test/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysisMethodCallTest.java b/src/test/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysisMethodCallTest.java new file mode 100644 index 00000000..994d9209 --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysisMethodCallTest.java @@ -0,0 +1,60 @@ +package br.unb.cic.analysis.df; + +import br.unb.cic.analysis.AbstractMergeConflictDefinition; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import soot.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SourceSinkConfluenceAnalysisMethodCallTest { + + private SourceSinkConfluenceAnalysis analysis; + + @Before + public void configure() { + G.reset(); + Collector.instance().clear(); + + AbstractMergeConflictDefinition definition = new AbstractMergeConflictDefinition() { + @Override + protected Map> sourceDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(8); + res.put("br.unb.cic.analysis.samples.SourceSinkMethodCallSample", lines); + return res; + } + + @Override + protected Map> sinkDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(10); + res.put("br.unb.cic.analysis.samples.SourceSinkMethodCallSample", lines); + return res; + } + }; + + PackManager.v().getPack("jtp").add( + new Transform("jtp.zeroConflict", new BodyTransformer() { + @Override + protected void internalTransform(Body body, String phaseName, Map options) { + analysis = new SourceSinkConfluenceAnalysis(body, definition); + } + })); + String cp = "target/test-classes"; + String targetClass = "br.unb.cic.analysis.samples.SourceSinkMethodCallSample"; + + Main.main(new String[] {"-w", "-allow-phantom-refs", "-f", "J", "-keep-line-number", "-cp", cp, targetClass}); + } + + @Test + public void testDataFlowAnalysisExpectingOneConflict() { + Assert.assertEquals(1, analysis.getConflicts().size()); + } +} diff --git a/src/test/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysisVariableAttributionTest.java b/src/test/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysisVariableAttributionTest.java new file mode 100644 index 00000000..85f7a8ea --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/df/SourceSinkConfluenceAnalysisVariableAttributionTest.java @@ -0,0 +1,63 @@ +package br.unb.cic.analysis.df; + +import br.unb.cic.analysis.AbstractMergeConflictDefinition; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import soot.*; +import soot.options.Options; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SourceSinkConfluenceAnalysisVariableAttributionTest { + + private SourceSinkConfluenceAnalysis analysis2; + + @Before + public void configure() { + G.reset(); + Collector.instance().clear(); + + AbstractMergeConflictDefinition definition = new AbstractMergeConflictDefinition() { + @Override + protected Map> sourceDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(18); + res.put("br.unb.cic.analysis.samples.SourceSinkVariableAttributionSample", lines); + return res; + } + + @Override + protected Map> sinkDefinitions() { + Map> res = new HashMap<>(); + List lines = new ArrayList<>(); + lines.add(20); + res.put("br.unb.cic.analysis.samples.SourceSinkVariableAttributionSample", lines); + return res; + } + }; + + PackManager.v().getPack("jtp").add( + new Transform("jtp.zeroConflict", new BodyTransformer() { + @Override + protected void internalTransform(Body body, String phaseName, Map options) { + analysis2 = new SourceSinkConfluenceAnalysis(body, definition); + } + })); + String cp = "target/test-classes"; + String targetClass = "br.unb.cic.analysis.samples.SourceSinkVariableAttributionSample"; + + Options.v().setPhaseOption("jb", "optimize:false"); + + Main.main(new String[] {"-w", "-allow-phantom-refs", "-f", "J", "-keep-line-number", "-cp", cp, targetClass}); + } + + @Test + public void testDataFlowAnalysisExpectingOneConflict() { + Assert.assertEquals(1, analysis2.getConflicts().size()); + } +} diff --git a/src/test/java/br/unb/cic/analysis/samples/ConfluenceWithTransitivitySample.java b/src/test/java/br/unb/cic/analysis/samples/ConfluenceWithTransitivitySample.java new file mode 100644 index 00000000..b04a53e1 --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/samples/ConfluenceWithTransitivitySample.java @@ -0,0 +1,21 @@ +package br.unb.cic.analysis.samples; + +public class ConfluenceWithTransitivitySample { + public void foo(){ + int x = 1; + int y = 2; + int z = 3; + + x = 10; //left + + z = x+1; + + y++; //right + + //addThese(z, y); + System.out.println(z+y); + } + /*private int addThese(int a1, int a2){ + return a1+a2; + }*/ +} diff --git a/src/test/java/br/unb/cic/analysis/samples/SourceSinkMethodCallSample.java b/src/test/java/br/unb/cic/analysis/samples/SourceSinkMethodCallSample.java new file mode 100644 index 00000000..a54be25d --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/samples/SourceSinkMethodCallSample.java @@ -0,0 +1,17 @@ +package br.unb.cic.analysis.samples; + +public class SourceSinkMethodCallSample { + public void foo(){ + int x = 10; + int y = 20; + + x = one(); //right + + y = two(); //left + + System.out.println(x+y); + } + private int one(){return 1;} + private int two(){return 2;} + +} diff --git a/src/test/java/br/unb/cic/analysis/samples/SourceSinkVariableAttributionSample.java b/src/test/java/br/unb/cic/analysis/samples/SourceSinkVariableAttributionSample.java new file mode 100644 index 00000000..d98decb6 --- /dev/null +++ b/src/test/java/br/unb/cic/analysis/samples/SourceSinkVariableAttributionSample.java @@ -0,0 +1,28 @@ +package br.unb.cic.analysis.samples; + +/** + * with the current analysis setup Soot, + * when optimizing the code, ends up erasing + * variable references that have a constant + * attributed to them, preventing the conflict + * detection by the analyzer if such attribution + * happens to be a line of interest(source or sink). + * **/ + +public class SourceSinkVariableAttributionSample { + public void foo() { + int x = 0; + int y = 0; + int z = 10; + + x = 10; //left + + y = z+2; //right + + addThese(x, y); //Confluence Line + + } + private int addThese(int a0, int a1){ + return a0 + a1; + } +}