diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/AbstractPointer.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/AbstractPointer.java index 1d406345c..3b16185e5 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/cs/element/AbstractPointer.java +++ b/src/main/java/pascal/taie/analysis/pta/core/cs/element/AbstractPointer.java @@ -66,13 +66,6 @@ public void setPointsToSet(PointsToSet pointsToSet) { this.pointsToSet = pointsToSet; } - @Override - public void removeObjsIf(Predicate filter) { - if (pointsToSet != null) { - pointsToSet.removeIf(filter); - } - } - @Override public void addFilter(Predicate filter) { if (filters.isEmpty()) { diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/Pointer.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/Pointer.java index 9c2e5d368..6c0597a0c 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/cs/element/Pointer.java +++ b/src/main/java/pascal/taie/analysis/pta/core/cs/element/Pointer.java @@ -57,11 +57,6 @@ public interface Pointer extends Indexable { */ void setPointsToSet(PointsToSet pointsToSet); - /** - * Removes objects pointed to by this pointer if they satisfy the filter. - */ - void removeObjsIf(Predicate filter); - /** * Adds filter to filter out objects pointed to by this pointer. */ @@ -104,6 +99,10 @@ public interface Pointer extends Indexable { /** * Removes out edges of this pointer if they satisfy the filter. + *

+ * Note: This method should not be used during pointer analysis iterations, + * as it can break the monotonicity of the analysis. + *

*/ void removeEdgesIf(Predicate filter); diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/CompositePlugin.java b/src/main/java/pascal/taie/analysis/pta/plugin/CompositePlugin.java index 2b203cff3..657c2de62 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/CompositePlugin.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/CompositePlugin.java @@ -91,9 +91,10 @@ private void addPlugin(Plugin plugin, List plugins, } public void clearPlugins() { - Stream.of(allPlugins, onNewPointsToSetPlugins, onNewCallEdgePlugins, - onNewMethodPlugins, onNewStmtPlugins, onNewCSMethodPlugins, - onUnresolvedCallPlugins).forEach(List::clear); + Stream.of(allPlugins, + onNewPointsToSetPlugins, onNewCallEdgePlugins, onNewMethodPlugins, + onNewStmtPlugins, onNewCSMethodPlugins, onUnresolvedCallPlugins + ).forEach(List::clear); } @Override diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/taint/SinkHandler.java b/src/main/java/pascal/taie/analysis/pta/plugin/taint/SinkHandler.java index 0509754d9..3951ac7b2 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/taint/SinkHandler.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/taint/SinkHandler.java @@ -118,5 +118,4 @@ private Set collectTaintFlows( .map(sourcePoint -> new TaintFlow(sourcePoint, sinkPoint)) .collect(Collectors.toSet()); } - } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java b/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java index 6d42ab195..e393b7388 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/taint/TaintAnalysis.java @@ -25,18 +25,22 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import pascal.taie.World; +import pascal.taie.analysis.graph.callgraph.CallGraph; import pascal.taie.analysis.pta.core.cs.context.Context; +import pascal.taie.analysis.pta.core.cs.element.CSCallSite; import pascal.taie.analysis.pta.core.cs.element.CSManager; +import pascal.taie.analysis.pta.core.cs.element.CSMethod; import pascal.taie.analysis.pta.core.cs.element.CSVar; import pascal.taie.analysis.pta.core.solver.Solver; import pascal.taie.analysis.pta.plugin.CompositePlugin; +import pascal.taie.analysis.pta.pts.PointsToSet; import pascal.taie.config.AnalysisOptions; import pascal.taie.ir.IR; import pascal.taie.language.classes.JMethod; import pascal.taie.util.Timer; +import javax.annotation.Nullable; import java.io.File; -import java.util.Scanner; import java.util.Set; public class TaintAnalysis extends CompositePlugin { @@ -55,11 +59,13 @@ public class TaintAnalysis extends CompositePlugin { public void setSolver(Solver solver) { this.solver = solver; AnalysisOptions options = solver.getOptions(); - isInteractive = options.getBoolean("taint-interactive"); + isInteractive = options.getBoolean("taint-interactive-mode"); initialize(); } private void initialize() { + // reset sub-plugins + clearPlugins(); TaintManager manager = new TaintManager(solver.getHeapModel()); TaintConfig config = TaintConfig.loadConfig( solver.getOptions().getString("taint-config"), @@ -70,62 +76,87 @@ private void initialize() { addPlugin(new SourceHandler(context), new TransferHandler(context), new SanitizerHandler(context)); - } - - /** - * Add comment - */ - private boolean reInitialize() { - clearPlugins(); - initialize(); + // clear all taint objects and taint edges CSManager csManager = solver.getCSManager(); - TaintManager taintManager = context.manager(); csManager.pointers().forEach(p -> { - p.removeObjsIf(csObj -> taintManager.isTaint(csObj.getObject())); + PointsToSet pts = p.getPointsToSet(); + if (pts != null) { + pts.removeIf(csObj -> manager.isTaint(csObj.getObject())); + } p.removeEdgesIf(TaintTransferEdge.class::isInstance); }); - solver.getCallGraph().reachableMethods().forEach(csMethod -> { - JMethod method = csMethod.getMethod(); - Context ctxt = csMethod.getContext(); - IR ir = csMethod.getMethod().getIR(); - if (context.config().callSiteMode()) { - ir.forEach(stmt -> onNewStmt(stmt, method)); - } - csMethod.getEdges().forEach(this::onNewCallEdge); - this.onNewCSMethod(csMethod); - ir.getParams().forEach(param -> { - CSVar csParam = csManager.getCSVar(ctxt, param); - onNewPointsToSet(csParam, csParam.getPointsToSet()); + // trigger the creation of taint objects + CallGraph cg = solver.getCallGraph(); + if (cg != null) { + cg.reachableMethods().forEach(csMethod -> { + JMethod method = csMethod.getMethod(); + Context ctxt = csMethod.getContext(); + IR ir = csMethod.getMethod().getIR(); + if (context.config().callSiteMode()) { + ir.forEach(stmt -> onNewStmt(stmt, method)); + } + csMethod.getEdges().forEach(this::onNewCallEdge); + this.onNewCSMethod(csMethod); + ir.getParams().forEach(param -> { + CSVar csParam = csManager.getCSVar(ctxt, param); + onNewPointsToSet(csParam, csParam.getPointsToSet()); + }); }); - }); - return !taintManager.getTaintObjs().isEmpty(); + } } @Override public void onPhaseFinish() { reportTaintFlows(); if (isInteractive) { - Scanner scan = new Scanner(System.in); while (true) { - logger.info("Change your taint config, and input 'r' to continue, 'e' to exit:"); - if (!scan.hasNextLine()) { + System.out.println("Taint Analysis is in interactive mode," + + " you can change your taint config and run the analysis again.\n" + + "Enter 'r' to run, 'e' to exit: "); + String input = nextLineFromConsole(); + if (input == null) { break; } - String input = scan.nextLine().strip(); + input = input.strip(); + System.out.println("You have entered: '" + input + "'"); if ("r".equals(input)) { - if (reInitialize()) { + initialize(); + if (!context.manager().getTaintObjs().isEmpty()) { break; } } else if ("e".equals(input)) { isInteractive = false; break; - } else { - logger.error("Invalid input"); } } } } + /** + * A utility method for reading one line from the console using {@code System.in}. + * This method does not use buffering to ensure it does not read more than necessary. + *
+ * + * @return one line line read from the console, + * or {@code null} if no line is available + */ + @Nullable + private static String nextLineFromConsole() { + StringBuilder sb = new StringBuilder(); + try { + int c; + while ((c = System.in.read()) != -1) { + if (c == '\r' || c == '\n') { + break; + } + sb.append((char) c); + } + } catch (Exception e) { + logger.error("Error reading from console", e); + } + return sb.toString(); + } + private void reportTaintFlows() { Set taintFlows = new SinkHandler(context).collectTaintFlows(); logger.info("Detected {} taint flow(s):", taintFlows.size()); @@ -137,5 +168,4 @@ private void reportTaintFlows() { new File(World.get().getOptions().getOutputDir(), TAINT_FLOW_GRAPH_FILE)), "TFGDumper"); } - } diff --git a/src/main/java/pascal/taie/analysis/pta/pts/PointsToSet.java b/src/main/java/pascal/taie/analysis/pta/pts/PointsToSet.java index 6277658a8..ff75a5474 100644 --- a/src/main/java/pascal/taie/analysis/pta/pts/PointsToSet.java +++ b/src/main/java/pascal/taie/analysis/pta/pts/PointsToSet.java @@ -60,6 +60,10 @@ public interface PointsToSet extends Iterable, Copyable { /** * Removes objects from this set if they satisfy the filter. + *

+ * Note: This method should be used with caution during pointer analysis iterations, + * as it can break the monotonicity of the analysis. + *

*/ void removeIf(Predicate filter); diff --git a/src/main/resources/tai-e-analyses.yml b/src/main/resources/tai-e-analyses.yml index b7cfcc540..0a29ae263 100644 --- a/src/main/resources/tai-e-analyses.yml +++ b/src/main/resources/tai-e-analyses.yml @@ -28,7 +28,7 @@ reflection-log: null # path to reflection log, required when reflection option is log taint-config: null # path to config file of taint analysis, # when this file is given, taint analysis will be enabled - taint-interactive: false # whether enable interactive taint analysis + taint-interactive-mode: false # whether enable interactive taint analysis plugins: [ ] # | [ pluginClass, ... ] time-limit: -1 # set time limit (in seconds) for pointer analysis, # -1 means no time limit diff --git a/src/test/java/pascal/taie/analysis/pta/TaintTest.java b/src/test/java/pascal/taie/analysis/pta/TaintTest.java index 32d5cbda8..3b4129af1 100644 --- a/src/test/java/pascal/taie/analysis/pta/TaintTest.java +++ b/src/test/java/pascal/taie/analysis/pta/TaintTest.java @@ -79,8 +79,8 @@ void test(String mainClass, String... opts) { void testInteractiveTaintAnalysis() { InputStream originalSystemIn = System.in; try { - InputStream testIn = new ByteArrayInputStream("r\ne\n".getBytes()); - System.setIn(testIn); + String simulatedInput = "r\nr\ne"; + System.setIn(new ByteArrayInputStream(simulatedInput.getBytes())); Main.main( "-pp", "-cp", "src/test/resources/pta/taint", @@ -89,7 +89,7 @@ void testInteractiveTaintAnalysis() { + "implicit-entries:false;" + "only-app:true;" + "distinguish-string-constants:all;" - + "interactive-taint-analysis:true;" + + "taint-interactive-mode:true;" + TAINT_CONFIG ); } finally {