From f3b2d04a83164b53a81ea858b7d3906d5391d365 Mon Sep 17 00:00:00 2001 From: prophe <1172782111@qq.com> Date: Mon, 12 Jan 2026 14:40:35 +0800 Subject: [PATCH 1/2] update support for cut-shortcut. --- .../analysis/graph/flowgraph/FlowKind.java | 6 + .../taie/analysis/pta/PointerAnalysis.java | 53 +- .../pta/core/cs/element/AbstractPointer.java | 14 + .../pta/core/cs/element/CSManager.java | 6 + .../pta/core/cs/element/HostPointer.java | 30 + .../core/cs/element/MapBasedCSManager.java | 11 + .../analysis/pta/core/cs/element/Pointer.java | 7 + .../pta/core/solver/CutShortcutSolver.java | 310 ++++++++ .../pta/core/solver/DefaultSolver.java | 34 +- .../pta/core/solver/PointerFlowGraph.java | 9 +- .../analysis/pta/core/solver/WorkList.java | 51 +- .../analysis/pta/plugin/CompositePlugin.java | 4 + .../taie/analysis/pta/plugin/Plugin.java | 9 + .../cutshortcut/ReflectiveEdgeProperty.java | 44 ++ .../plugin/cutshortcut/SpecialVariables.java | 50 ++ .../container/ClassAndTypeClassifier.java | 80 +++ .../container/ContainerAccessHandler.java | 678 ++++++++++++++++++ .../container/ContainerConfig.java | 428 +++++++++++ .../plugin/cutshortcut/container/Host.java | 84 +++ .../container/HostMap/DelegateHostSet.java | 66 ++ .../container/HostMap/HostList.java | 61 ++ .../container/HostMap/HostSet.java | 33 + .../container/HostMap/HostSetFactory.java | 22 + .../container/HostMap/HybridBitHostSet.java | 21 + .../container/MakeDefaultContainerConfig.java | 148 ++++ .../cutshortcut/container/Parameter.java | 10 + .../cutshortcut/field/AbstractLoadField.java | 36 + .../cutshortcut/field/AbstractStoreField.java | 27 + .../cutshortcut/field/FieldAccessHandler.java | 339 +++++++++ .../cutshortcut/field/GetStatement.java | 10 + .../cutshortcut/field/ParameterIndex.java | 69 ++ .../cutshortcut/field/SetStatement.java | 23 + .../localflow/LocalFlowHandler.java | 206 ++++++ .../localflow/ParameterIndexOrNewObj.java | 16 + .../reflection/ReflectiveActionModel.java | 27 +- .../plugin/reflection/ReflectiveCallEdge.java | 5 +- src/main/java/pascal/taie/ir/exp/Var.java | 42 ++ .../java/pascal/taie/ir/stmt/FieldStmt.java | 2 +- 38 files changed, 3016 insertions(+), 55 deletions(-) create mode 100644 src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java create mode 100644 src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/GetStatement.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/ParameterIndexOrNewObj.java diff --git a/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java b/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java index 42ffd8e88..47a450de3 100644 --- a/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java +++ b/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java @@ -39,5 +39,11 @@ public enum FlowKind { PARAMETER_PASSING, RETURN, + // local flow in cut-shortcut + ARG_TO_HOST, HOST_TO_RESULT, SUBSET, CORRELATION, ARRAYCOPY, ID, + VIRTUAL_ARRAY, + VIRTUAL_ARG, + SET, GET, NON_RELAY_GET, + OTHER, } diff --git a/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java b/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java index 7b293b951..4cd7e173d 100644 --- a/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java +++ b/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java @@ -30,6 +30,7 @@ import pascal.taie.analysis.pta.core.cs.selector.ContextSelectorFactory; import pascal.taie.analysis.pta.core.heap.AllocationSiteBasedModel; import pascal.taie.analysis.pta.core.heap.HeapModel; +import pascal.taie.analysis.pta.core.solver.CutShortcutSolver; import pascal.taie.analysis.pta.core.solver.DefaultSolver; import pascal.taie.analysis.pta.core.solver.Solver; import pascal.taie.analysis.pta.plugin.AnalysisTimer; @@ -40,6 +41,10 @@ import pascal.taie.analysis.pta.plugin.ReferenceHandler; import pascal.taie.analysis.pta.plugin.ResultProcessor; import pascal.taie.analysis.pta.plugin.ThreadHandler; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.ContainerAccessHandler; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.MakeDefaultContainerConfig; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.FieldAccessHandler; +import pascal.taie.analysis.pta.plugin.cutshortcut.localflow.LocalFlowHandler; import pascal.taie.analysis.pta.plugin.exception.ExceptionAnalysis; import pascal.taie.analysis.pta.plugin.invokedynamic.InvokeDynamicAnalysis; import pascal.taie.analysis.pta.plugin.invokedynamic.Java9StringConcatHandler; @@ -75,52 +80,59 @@ public PointerAnalysisResult analyze() { HeapModel heapModel = new AllocationSiteBasedModel(options); ContextSelector selector = null; String advanced = options.getString("advanced"); + String solverType = options.has("solver") ? options.getString("solver"): "default"; // which solver to use String cs = options.getString("cs"); if (advanced != null) { - if (advanced.equals("collection")) { + if (advanced.equals("collection")) selector = ContextSelectorFactory.makeSelectiveSelector(cs, new CollectionMethods(World.get().getClassHierarchy()).get()); - } else { + else { // run context-insensitive analysis as pre-analysis - PointerAnalysisResult preResult = runAnalysis(heapModel, - ContextSelectorFactory.makeCISelector()); - if (advanced.startsWith("scaler")) { + PointerAnalysisResult preResult = runAnalysis(heapModel, ContextSelectorFactory.makeCISelector(), solverType); + if (advanced.startsWith("scaler")) selector = Monitor.runAndCount(() -> ContextSelectorFactory .makeGuidedSelector(Scaler.run(preResult, advanced)), "Scaler", Level.INFO); - } else if (advanced.startsWith("zipper")) { + else if (advanced.startsWith("zipper")) selector = Monitor.runAndCount(() -> ContextSelectorFactory .makeSelectiveSelector(cs, Zipper.run(preResult, advanced)), "Zipper", Level.INFO); - } else if (advanced.equals("mahjong")) { + else if (advanced.equals("mahjong")) heapModel = Monitor.runAndCount(() -> Mahjong.run(preResult, options), "Mahjong", Level.INFO); - } else { + else throw new IllegalArgumentException( "Illegal advanced analysis argument: " + advanced); - } + } } if (selector == null) { selector = ContextSelectorFactory.makePlainSelector(cs); } - return runAnalysis(heapModel, selector); + return runAnalysis(heapModel, selector, solverType); } private PointerAnalysisResult runAnalysis(HeapModel heapModel, - ContextSelector selector) { + ContextSelector selector, + String solverType) { AnalysisOptions options = getOptions(); - Solver solver = new DefaultSolver(options, - heapModel, selector, new MapBasedCSManager()); + Solver solver = null; + if (solverType.equals("default")) + solver = new DefaultSolver(options, heapModel, selector, new MapBasedCSManager()); + // cut-shortcut + else if (solverType.equals("csc")) + solver = new CutShortcutSolver(options, heapModel, selector, new MapBasedCSManager()); + else + throw new IllegalArgumentException("Illegal solver type: " + solverType); // The initialization of some Plugins may read the fields in solver, // e.g., contextSelector or csManager, thus we initialize Plugins - // after setting all other fields of solver. - setPlugin(solver, options); + // after setting all other fields of solver.setPlugin(solver, options); + setPlugin(solver, options, solverType); solver.solve(); return solver.getResult(); } - private static void setPlugin(Solver solver, AnalysisOptions options) { + private static void setPlugin(Solver solver, AnalysisOptions options, String solverType) { CompositePlugin plugin = new CompositePlugin(); // add builtin plugins // To record elapsed time precisely, AnalysisTimer should be added at first. @@ -132,6 +144,15 @@ private static void setPlugin(Solver solver, AnalysisOptions options) { new NativeModeller(), new ExceptionAnalysis() ); + if (solverType.equals("csc")) { + MakeDefaultContainerConfig.make(); + plugin.addPlugin( + new LocalFlowHandler(), + new FieldAccessHandler(), + new ContainerAccessHandler() + ); + } + int javaVersion = World.get().getOptions().getJavaVersion(); if (javaVersion < 9) { // current reference handler doesn't support Java 9+ 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 3b16185e5..ec2c50201 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 @@ -45,6 +45,8 @@ abstract class AbstractPointer implements Pointer { private final ArrayList outEdges = new ArrayList<>(4); + private final ArrayList inEdges = new ArrayList<>(4); + private Set> filters = Set.of(); protected AbstractPointer(int index) { @@ -95,6 +97,7 @@ public PointerFlowEdge addEdge(PointerFlowEdge edge) { assert edge.source() == this; if (successors.add(edge.target())) { outEdges.add(edge); + edge.target().addInEdge(edge); return edge; } else if (edge.kind() == FlowKind.OTHER) { for (PointerFlowEdge outEdge : outEdges) { @@ -103,11 +106,17 @@ public PointerFlowEdge addEdge(PointerFlowEdge edge) { } } outEdges.add(edge); + edge.target().addInEdge(edge); return edge; } return null; } + public void addInEdge(PointerFlowEdge edge) { + assert edge.target() == this; + inEdges.add(edge); + } + @Override public void removeEdgesIf(Predicate filter) { outEdges.removeIf(filter); @@ -118,6 +127,11 @@ public Set getOutEdges() { return Collections.unmodifiableSet(new ArraySet<>(outEdges, true)); } + @Override + public Set getInEdges() { + return Collections.unmodifiableSet(new ArraySet<>(inEdges, true)); + } + @Override public int getOutDegree() { return outEdges.size(); diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java index e02141ced..9392387ef 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java +++ b/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java @@ -24,6 +24,7 @@ import pascal.taie.analysis.pta.core.cs.context.Context; import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.Invoke; import pascal.taie.language.classes.JField; @@ -124,4 +125,9 @@ public interface CSManager { * The indexer is useful for creating efficient points-to sets. */ Indexer getObjectIndexer(); + + /** + * @return the HostPointer for given host and category used in Cut-Shortcut. + */ + HostPointer getHostPointer(Host host, String category); } diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java new file mode 100644 index 000000000..e09be0655 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java @@ -0,0 +1,30 @@ +package pascal.taie.analysis.pta.core.cs.element; + +import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; +import pascal.taie.language.type.Type; + +public class HostPointer extends AbstractPointer { + private final Host host; + + private final String category; + + public HostPointer(Host host, String category, int index) { + super(index); + this.host = host; + this.category = category; + } + + public Host getHost() { + return host; + } + + @Override + public Type getType() { + return host.getType(); + } + + @Override + public String toString() { + return host.toString(); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java index b8e304fa7..6de293261 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java +++ b/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java @@ -25,6 +25,7 @@ import pascal.taie.World; import pascal.taie.analysis.pta.core.cs.context.Context; import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.Invoke; import pascal.taie.language.classes.ClassNames; @@ -93,6 +94,9 @@ public Collection getCSVarsOf(Var var) { return ptrManager.getCSVarsOf(var); } + @Override + public HostPointer getHostPointer(Host host, String category) { return ptrManager.getHostPointer(host, category); } + @Override public Collection getStaticFields() { return ptrManager.getStaticFields(); @@ -156,6 +160,8 @@ private static class PointerManager { private final Map arrayIndexes = Maps.newMap(); + private final TwoKeyMap hostPointers = Maps.newTwoKeyMap(); + /** * Counter for assigning unique indexes to Pointers. */ @@ -181,6 +187,11 @@ private ArrayIndex getArrayIndex(CSObj array) { a -> new ArrayIndex(a, counter++)); } + private HostPointer getHostPointer(Host host, String category) { + return hostPointers.computeIfAbsent(host, category, + (h, c) -> new HostPointer(h, c, counter++)); + } + private Collection getVars() { return vars.keySet(); } 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 4b0275149..cff3c7586 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 @@ -97,6 +97,8 @@ public interface Pointer extends Indexable { */ PointerFlowEdge addEdge(PointerFlowEdge edge); + void addInEdge(PointerFlowEdge edge); + /** * Removes out edges of this pointer if they satisfy the filter. *

@@ -112,6 +114,11 @@ public interface Pointer extends Indexable { */ Set getOutEdges(); + /** + * @return out edges of this pointer in pointer flow graph. + */ + Set getInEdges(); + /** * @return out degree of this pointer in pointer flow graph. */ diff --git a/src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java b/src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java new file mode 100644 index 000000000..27ac8419a --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java @@ -0,0 +1,310 @@ +package pascal.taie.analysis.pta.core.solver; + +import pascal.taie.analysis.graph.callgraph.CallKind; +import pascal.taie.analysis.graph.callgraph.Edge; +import pascal.taie.analysis.graph.flowgraph.FlowKind; +import pascal.taie.analysis.pta.core.cs.context.Context; +import pascal.taie.analysis.pta.core.cs.element.*; +import pascal.taie.analysis.pta.core.cs.selector.ContextSelector; +import pascal.taie.analysis.pta.core.heap.HeapModel; +import pascal.taie.analysis.pta.plugin.CompositePlugin; +import pascal.taie.analysis.pta.plugin.Plugin; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.ContainerAccessHandler; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.ContainerConfig; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSet; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSetFactory; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.FieldAccessHandler; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; +import pascal.taie.analysis.pta.plugin.reflection.ReflectiveCallEdge; +import pascal.taie.analysis.pta.pts.PointsToSet; +import pascal.taie.config.AnalysisOptions; +import pascal.taie.ir.exp.Exp; +import pascal.taie.ir.exp.InvokeExp; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.proginfo.FieldRef; +import pascal.taie.ir.stmt.Invoke; +import pascal.taie.ir.stmt.LoadField; +import pascal.taie.ir.stmt.StoreField; +import pascal.taie.language.classes.JClass; +import pascal.taie.language.classes.JField; +import pascal.taie.language.classes.JMethod; +import pascal.taie.language.type.NullType; +import pascal.taie.language.type.ReferenceType; +import pascal.taie.language.type.Type; +import pascal.taie.util.collection.Sets; + +import java.util.Set; + +import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.setVirtualArg; +import static pascal.taie.analysis.pta.plugin.cutshortcut.SpecialVariables.isNonRelay; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.isHashtableClass; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.isVectorClass; + +public class CutShortcutSolver extends DefaultSolver { + private ContainerConfig containerConfig; + + private HostSetFactory hostSetFactory; + + private FieldAccessHandler fieldAccessHandler = null; + + private ContainerAccessHandler containerAccessHandler = null; + + private final Set specialHandledReturnVars = Sets.newSet(); + + private final Set ignoredStoreFields = Sets.newSet(); + + private final Set recoveredCallSites = Sets.newSet(); + + private final Set selectedMethods = Sets.newSet(); + + public CutShortcutSolver(AnalysisOptions options, HeapModel heapModel, + ContextSelector contextSelector, CSManager csManager) { + super(options, heapModel, contextSelector, csManager); + } + + @Override + public void setPlugin(Plugin plugin) { + super.setPlugin(plugin); + assert plugin instanceof CompositePlugin; + CompositePlugin compositePlugin = (CompositePlugin) plugin; + for (Plugin p : compositePlugin.getAllPlugins()) { + if (p instanceof FieldAccessHandler fah) + fieldAccessHandler = fah; + else if (p instanceof ContainerAccessHandler cah) + containerAccessHandler = cah; + } + assert fieldAccessHandler != null; + assert containerAccessHandler != null; + } + + // ---------- solver logic starts ---------- + /** + * Initializes pointer analysis. + */ + @Override + protected void initialize() { + containerConfig = ContainerConfig.config; + hostSetFactory = new HostSetFactory(containerConfig.getHostIndexer()); + super.initialize(); + } + + /** + * Processes worklist entries until the worklist is empty. + */ + @Override + protected void analyze() { + while (!workList.isEmpty()) { + WorkList.Entry entry = workList.pollEntry(); + if (entry instanceof WorkList.PointerEntry pEntry) { + Pointer p = pEntry.pointer(); + PointsToSet pts = pEntry.pointsToSet(); + PointsToSet diff = propagate(p, pts); + if (!diff.isEmpty() && p instanceof CSVar v) { + processInstanceStore(v, diff); + processInstanceLoad(v, diff); + processArrayStore(v, diff); + processArrayLoad(v, diff); + processCall(v, diff); + plugin.onNewPointsToSet(v, diff); + } + } + else if (entry instanceof WorkList.CallEdgeEntry eEntry) + processCallEdge(eEntry.edge()); + else if (entry instanceof WorkList.SetStmtEntry sEntry) + fieldAccessHandler.onNewSetStatement(sEntry.method(), sEntry.fieldRef(), sEntry.baseIndex(), sEntry.rhsIndex()); + else if (entry instanceof WorkList.GetStmtEntry gEntry) + fieldAccessHandler.onNewGetStatement(gEntry.method(), gEntry.lhsIndex(), gEntry.baseIndex(), gEntry.fieldRef()); + else if (entry instanceof WorkList.HostEntry hEntry) { + Pointer p = hEntry.pointer(); + HostSet diff = processHostEntry(hEntry); + if (p instanceof CSVar csVar && !diff.isEmpty()) + containerAccessHandler.onNewHostEntry(csVar, hEntry.kind(), diff); + } + } + plugin.onFinish(); + } + + String[] stopSigns = new String[]{"iterator(", "entrySet()", "keySet()", "values()", "Entry(", "Iterator("}; + + public boolean needPropagateHost(Pointer source, FlowKind kind) { + if (kind == FlowKind.RETURN) { + CSVar csSource = (CSVar) source; + Var sourceVar = csSource.getVar(); + JClass container = sourceVar.getMethod().getDeclaringClass(); + String methodString = sourceVar.getMethod().toString(); + if (containerConfig.isRealHostClass(container)) { + for (String stopSign : stopSigns) { + if (methodString.contains(stopSign)) + return false; + } + if (isHashtableClass(container) && (methodString.contains("elements()") || methodString.contains("keys()"))) + return false; + return !isVectorClass(container) || !methodString.contains("elements()"); + } + return true; + } + return true; + } + + /** + * @return true if the type of given expression is concerned in + * pointer analysis, otherwise false. + */ + public static boolean isConcerned(Exp exp) { + Type type = exp.getType(); + return type instanceof ReferenceType && !(type instanceof NullType); + } + + public void addIgnoredStoreField(StoreField set) { // 需要跳过的StoreField,位于最内层(你应该知道最内层的含义)的set方法 + ignoredStoreFields.add(set); + } + + private HostSet processHostEntry(WorkList.HostEntry entry) { + Pointer pointer = entry.pointer(); + HostSet hostSet = entry.hostSet(); + HostList.Kind kind = entry.kind(); + HostSet diff = containerAccessHandler.getHostListOf(pointer).addAllDiff(kind, hostSet); + if (!diff.isEmpty()) { + pointerFlowGraph.getOutEdgesOf(pointer).forEach(edge -> { + if (needPropagateHost(edge.source(), edge.kind())) { + Pointer target = edge.target(); + workList.addHostEntry(target, kind, diff); + } + }); + } + return diff; + } + + private void processInstanceStore(CSVar baseVar, PointsToSet pts) { + Context context = baseVar.getContext(); + Var var = baseVar.getVar(); + for (StoreField store : var.getStoreFields()) { + // for StoreFields that are recognized as a setStatement, we skip the process + if (ignoredStoreFields.contains(store)) + continue; + Var fromVar = store.getRValue(); + if (isConcerned(fromVar)) { + CSVar from = getCSManager().getCSVar(context, fromVar); + pts.forEach(baseObj -> { + JField field = store.getFieldRef().resolve(); + InstanceField instField = getCSManager().getInstanceField( + baseObj, field); + addPFGEdge(from, instField, FlowKind.INSTANCE_STORE, field.getType() + ); + }); + } + } + } + + private void processInstanceLoad(CSVar baseVar, PointsToSet pts) { + Context context = baseVar.getContext(); + Var var = baseVar.getVar(); + for (LoadField load : var.getLoadFields()) { + Var toVar = load.getLValue(); + JField field = load.getFieldRef().resolveNullable(); + if (isConcerned(toVar) && field != null) { + CSVar to = getCSManager().getCSVar(context, toVar); + pts.forEach(baseObj -> { + InstanceField instField = getCSManager().getInstanceField( + baseObj, field); + addPFGEdge(instField, to, //toVar.getType(), + isNonRelay(load) ? FlowKind.NON_RELAY_GET : FlowKind.INSTANCE_LOAD); + }); + } + } + } + + public void processCallEdge(Edge edge) { + if (callGraph.addEdge(edge)) { + if (edge instanceof ReflectiveCallEdge reflEdge) + setVirtualArg(reflEdge); + // process new call edge + CSMethod csCallee = edge.getCallee(); + addCSMethod(csCallee); + if (edge.getKind() != CallKind.OTHER && !isIgnored(csCallee.getMethod())) { + Context callerCtx = edge.getCallSite().getContext(); + Invoke callSite = edge.getCallSite().getCallSite(); + Context calleeCtx = csCallee.getContext(); + JMethod callee = csCallee.getMethod(); + InvokeExp invokeExp = callSite.getInvokeExp(); + // pass arguments to parameters + for (int i = 0; i < invokeExp.getArgCount(); ++i) { + Var arg = invokeExp.getArg(i); + if (isConcerned(arg)) { + Var param = callee.getIR().getParam(i); + CSVar argVar = getCSManager().getCSVar(callerCtx, arg); + CSVar paramVar = getCSManager().getCSVar(calleeCtx, param); + addPFGEdge(argVar, paramVar, FlowKind.PARAMETER_PASSING); + } + } + // pass results to LHS variable + if (!ContainerAccessHandler.CutReturnEdge(callSite, callee) || recoveredCallSites.contains(callSite)) { + Var lhs = callSite.getResult(); + if (lhs != null && isConcerned(lhs)) { + CSVar csLHS = getCSManager().getCSVar(callerCtx, lhs); + for (Var ret : callee.getIR().getReturnVars()) { + if (isConcerned(ret) && !specialHandledReturnVars.contains(ret)) { + CSVar csRet = getCSManager().getCSVar(calleeCtx, ret); + addPFGEdge(csRet, csLHS, FlowKind.RETURN); + } + } + } + } + } + plugin.onNewCallEdge(edge); + } + } + + public boolean addRecoveredCallSite(Invoke callSite) { + return recoveredCallSites.add(callSite); + } + + public boolean isRecoveredCallSite(Invoke callSite) { + return recoveredCallSites.contains(callSite); + } + + public void addPFGEdge(Pointer source, Pointer target, FlowKind kind, Set transfers) { + PointerFlowEdge edge = new PointerFlowEdge(kind, source, target); + transfers.forEach(edge::addTransfer); + if (pointerFlowGraph.addEdge(edge) != null) { + PointsToSet sourceSet = getPointsToSetOf(source); + PointsToSet targetSet = makePointsToSet(); + transfers.forEach(transfer -> { + if (edge.addTransfer(transfer)) { + PointsToSet transferSet = transfer.apply(edge, sourceSet); + targetSet.addAll(transferSet); + } + }); + if (!targetSet.isEmpty()) + addPointsTo(target, targetSet); + plugin.onNewPFGEdge(edge); + } + } + + public void addSetStmtEntry(JMethod method, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) { + workList.addSetStmtEntry(method, fieldRef, baseIndex, rhsIndex); + } + + public void addGetStmtEntry(JMethod method, int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { + workList.addGetStmtEntry(method, lhsIndex, baseIndex, fieldRef); + } + + public void addHostEntry(Pointer pointer, HostList.Kind kind, HostSet hostSet) { + workList.addHostEntry(pointer, kind, hostSet); + } + + public void addSpecialHandledRetVar(Var ret) { + specialHandledReturnVars.add(ret); + } + + public void addSelectedMethod(JMethod method) { selectedMethods.add(method); } + + public Set getInvolvedMethods() { + return selectedMethods; + } + + public HostSet getEmptyHostSet() { + return hostSetFactory.make(); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java b/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java index f0313dce3..4ac8b88a2 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java +++ b/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java @@ -141,27 +141,27 @@ public class DefaultSolver implements Solver { */ private volatile boolean isTimeout; - private Plugin plugin; + protected Plugin plugin; - private WorkList workList; + protected WorkList workList; - private CSCallGraph callGraph; + protected CSCallGraph callGraph; - private PointerFlowGraph pointerFlowGraph; + protected PointerFlowGraph pointerFlowGraph; - private Set reachableMethods; + protected Set reachableMethods; /** * Set of classes that have been initialized. */ - private Set initializedClasses; + protected Set initializedClasses; /** * Set of methods to be intercepted and ignored. */ - private Set ignoredMethods; + protected Set ignoredMethods; - private StmtProcessor stmtProcessor; + protected StmtProcessor stmtProcessor; private PointerAnalysisResult result; @@ -174,6 +174,7 @@ public DefaultSolver(AnalysisOptions options, HeapModel heapModel, this.csManager = csManager; hierarchy = World.get().getClassHierarchy(); typeSystem = World.get().getTypeSystem(); + callGraph = new CSCallGraph(csManager); ptsFactory = new PointsToSetFactory(csManager.getObjectIndexer()); propTypes = new PropagateTypes( (List) options.get("propagate-types"), @@ -251,8 +252,7 @@ public void solve() { /** * Initializes pointer analysis. */ - private void initialize() { - callGraph = new CSCallGraph(csManager); + protected void initialize() { pointerFlowGraph = new PointerFlowGraph(csManager); workList = new WorkList(); reachableMethods = Sets.newSet(); @@ -304,7 +304,7 @@ private void stop() { /** * Processes work list entries until the work list is empty. */ - private void analyze() { + protected void analyze() { while (!workList.isEmpty() && !isTimeout) { // phase starts while (!workList.isEmpty() && !isTimeout) { @@ -340,7 +340,7 @@ private void analyze() { * Propagates pointsToSet to pt(pointer) and its PFG successors, * returns the difference set of pointsToSet and pt(pointer). */ - private PointsToSet propagate(Pointer pointer, PointsToSet pointsToSet) { + protected PointsToSet propagate(Pointer pointer, PointsToSet pointsToSet) { logger.trace("Propagate {} to {}", pointsToSet, pointer); Set> filters = pointer.getFilters(); if (!filters.isEmpty()) { @@ -414,7 +414,7 @@ private void processInstanceLoad(CSVar baseVar, PointsToSet pts) { * @param arrayVar the array variable * @param pts set of new discovered arrays pointed by the variable. */ - private void processArrayStore(CSVar arrayVar, PointsToSet pts) { + protected void processArrayStore(CSVar arrayVar, PointsToSet pts) { Context context = arrayVar.getContext(); Var var = arrayVar.getVar(); for (StoreArray store : var.getStoreArrays()) { @@ -441,7 +441,7 @@ private void processArrayStore(CSVar arrayVar, PointsToSet pts) { * @param arrayVar the array variable * @param pts set of new discovered arrays pointed by the variable. */ - private void processArrayLoad(CSVar arrayVar, PointsToSet pts) { + protected void processArrayLoad(CSVar arrayVar, PointsToSet pts) { Context context = arrayVar.getContext(); Var var = arrayVar.getVar(); for (LoadArray load : var.getLoadArrays()) { @@ -464,7 +464,7 @@ private void processArrayLoad(CSVar arrayVar, PointsToSet pts) { * @param recv the receiver variable * @param pts set of new discovered objects pointed by the variable. */ - private void processCall(CSVar recv, PointsToSet pts) { + protected void processCall(CSVar recv, PointsToSet pts) { Context context = recv.getContext(); Var var = recv.getVar(); for (Invoke callSite : var.getInvokes()) { @@ -531,7 +531,7 @@ private void processCallEdge(Edge edge) { } } - private boolean isIgnored(JMethod method) { + protected boolean isIgnored(JMethod method) { return ignoredMethods.contains(method) || onlyApp && !method.isApplication(); } @@ -546,7 +546,7 @@ private void processNewMethod(JMethod method) { } } - private class StmtProcessor { + protected class StmtProcessor { /** * Information shared by all visitors. diff --git a/src/main/java/pascal/taie/analysis/pta/core/solver/PointerFlowGraph.java b/src/main/java/pascal/taie/analysis/pta/core/solver/PointerFlowGraph.java index 5375fb9a7..5279dfe7e 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/solver/PointerFlowGraph.java +++ b/src/main/java/pascal/taie/analysis/pta/core/solver/PointerFlowGraph.java @@ -22,6 +22,7 @@ package pascal.taie.analysis.pta.core.solver; +import pascal.taie.analysis.graph.flowgraph.FlowEdge; import pascal.taie.analysis.graph.flowgraph.FlowKind; import pascal.taie.analysis.pta.core.cs.element.CSManager; import pascal.taie.analysis.pta.core.cs.element.Pointer; @@ -33,6 +34,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; + /** * Represents pointer flow graph in context-sensitive pointer analysis. */ @@ -59,9 +61,7 @@ public PointerFlowEdge addEdge(PointerFlowEdge edge) { } @Override - public Set> getInEdgesOf(Pointer node) { - throw new UnsupportedOperationException(); - } + public Set getInEdgesOf(Pointer node) { return node.getInEdges(); } @Override public Set getOutEdgesOf(Pointer pointer) { @@ -74,7 +74,8 @@ public Stream pointers() { @Override public Set getPredsOf(Pointer node) { - throw new UnsupportedOperationException(); + return Views.toMappedSet(node.getInEdges(), + PointerFlowEdge::target); } @Override diff --git a/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java b/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java index 780fdb1fe..8a75b764a 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java +++ b/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java @@ -26,7 +26,12 @@ import pascal.taie.analysis.pta.core.cs.element.CSCallSite; import pascal.taie.analysis.pta.core.cs.element.CSMethod; import pascal.taie.analysis.pta.core.cs.element.Pointer; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSet; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; import pascal.taie.analysis.pta.pts.PointsToSet; +import pascal.taie.ir.proginfo.FieldRef; +import pascal.taie.language.classes.JMethod; import pascal.taie.util.collection.Maps; import java.util.ArrayDeque; @@ -49,6 +54,13 @@ final class WorkList { */ private final Queue> callEdges = new ArrayDeque<>(); + // Additional work lists for cut-shortcut analysis + private final Queue hostEntries = new ArrayDeque<>(); + + private final Queue setStmtEntries = new ArrayDeque<>(); + + private final Queue getStmtEntries = new ArrayDeque<>(); + void addEntry(Pointer pointer, PointsToSet pointsToSet) { PointsToSet set = pointerEntries.get(pointer); if (set != null) { @@ -67,18 +79,26 @@ Entry pollEntry() { // for correctness, we need to ensure that any call edges in // the work list must be processed prior to the pointer entries return new CallEdgeEntry(callEdges.poll()); - } else if (!pointerEntries.isEmpty()) { + } + else if (!pointerEntries.isEmpty()) { var it = pointerEntries.entrySet().iterator(); var e = it.next(); it.remove(); return new PointerEntry(e.getKey(), e.getValue()); - } else { - throw new NoSuchElementException(); } + else if (!setStmtEntries.isEmpty()) + return setStmtEntries.poll(); + else if (!getStmtEntries.isEmpty()) + return getStmtEntries.poll(); + else if (!hostEntries.isEmpty()) + return hostEntries.poll(); + else + throw new NoSuchElementException(); } boolean isEmpty() { - return pointerEntries.isEmpty() && callEdges.isEmpty(); + return pointerEntries.isEmpty() && hostEntries.isEmpty() && callEdges.isEmpty() + && setStmtEntries.isEmpty() && getStmtEntries.isEmpty(); } interface Entry { @@ -91,4 +111,27 @@ record PointerEntry(Pointer pointer, PointsToSet pointsToSet) record CallEdgeEntry(Edge edge) implements Entry { } + + record HostEntry(Pointer pointer, HostList.Kind kind, HostSet hostSet) + implements Entry { + } + + record SetStmtEntry(JMethod method, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) + implements Entry { + } + + record GetStmtEntry(JMethod method, int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) + implements Entry { + } + + void addHostEntry(Pointer pointer, HostList.Kind kind, HostSet hostSet) { + hostEntries.add(new HostEntry(pointer, kind, hostSet)); + } + void addSetStmtEntry(JMethod method, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) { + setStmtEntries.add(new SetStmtEntry(method, fieldRef, baseIndex, rhsIndex)); + } + + void addGetStmtEntry(JMethod method, int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { + getStmtEntries.add(new GetStmtEntry(method, lhsIndex, baseIndex, fieldRef)); + } } 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 657c2de62..0b82f0edc 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/CompositePlugin.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/CompositePlugin.java @@ -76,6 +76,10 @@ public void addPlugin(Plugin... plugins) { } } + public final List getAllPlugins() { + return allPlugins; + } + private void addPlugin(Plugin plugin, List plugins, String name, Class... parameterTypes) { try { diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/Plugin.java b/src/main/java/pascal/taie/analysis/pta/plugin/Plugin.java index 2886a1c07..3704b0ee1 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/Plugin.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/Plugin.java @@ -28,6 +28,7 @@ import pascal.taie.analysis.pta.core.cs.element.CSMethod; import pascal.taie.analysis.pta.core.cs.element.CSObj; import pascal.taie.analysis.pta.core.cs.element.CSVar; +import pascal.taie.analysis.pta.core.solver.PointerFlowEdge; import pascal.taie.analysis.pta.core.solver.Solver; import pascal.taie.analysis.pta.pts.PointsToSet; import pascal.taie.ir.stmt.Invoke; @@ -129,4 +130,12 @@ default void onNewCSMethod(CSMethod csMethod) { */ default void onUnresolvedCall(CSObj recv, Context context, Invoke invoke) { } + + /** + * Invoked when pointer analysis discover a new PFG Edge + * + * @param edge the new created PFG edge + */ + default void onNewPFGEdge(PointerFlowEdge edge) { + } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java new file mode 100644 index 000000000..41363a37c --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java @@ -0,0 +1,44 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut; + +import pascal.taie.analysis.pta.plugin.reflection.ReflectiveCallEdge; +import pascal.taie.ir.exp.Var; +import pascal.taie.language.type.ArrayType; +import pascal.taie.util.collection.Maps; + +import java.util.Map; + +import static pascal.taie.analysis.pta.core.solver.CutShortcutSolver.isConcerned; + +public class ReflectiveEdgeProperty { + // used for Cut-Shortcut + public enum ReflectiveCallKind { + NEW_INSTANCE, // r = c.newInstance(args), r is the baseVar + METHOD_INVOKE, // r = m.invoke(obj, args): m is a instance of class method, and obj is a instance of m's declaring method, + // args is a variable of array type which stores the argument of m in order + } + + private static Map reflectiveCallKindMap = Maps.newMap(); + + private static Map reflectiveCallVirtualArgMap = Maps.newMap(); + + public static void setgetReflectiveKind(ReflectiveCallEdge reflectiveCallEdge, ReflectiveCallKind kind) { + reflectiveCallKindMap.put(reflectiveCallEdge, kind); + } + + public static ReflectiveCallKind getReflectiveKind(ReflectiveCallEdge reflectiveCallEdge) { + return reflectiveCallKindMap.get(reflectiveCallEdge); + } + + public static void setVirtualArg(ReflectiveCallEdge reflectiveCallEdge) { + Var args = reflectiveCallEdge.getArgs(); + if (args != null && isConcerned(args)) { + Var virtualArg = new Var(args.getMethod(), "VirtualArg", ((ArrayType) args.getType()).elementType(), -1); + SpecialVariables.setVirtualVar(virtualArg); + reflectiveCallVirtualArgMap.put(reflectiveCallEdge, virtualArg); + } + } + + public static Var getVirtualArg(ReflectiveCallEdge reflectiveCallEdge) { + return reflectiveCallVirtualArgMap.getOrDefault(reflectiveCallEdge, null); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java new file mode 100644 index 000000000..25fbe7e27 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java @@ -0,0 +1,50 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut; + +import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.LoadField; + +import java.util.Map; +import java.util.Set; + +public class SpecialVariables { + private static Set definedVars; + + private static Set virtualVars; + + private static Map definedParameterIndexes; + + private static Set relayedLoadFields; + + public static void setDefined(Var var) { + definedVars.add(var); + } + + public static boolean isDefined(Var var) { + return definedVars.contains(var); + } + + public static void setVirtualVar(Var var) { + virtualVars.add(var); + } + + public static boolean isVirtualVar(Var var) { + return virtualVars.contains(var); + } + + public static void setParameterIndex(Var param, ParameterIndex index) { + definedParameterIndexes.put(param, index); + } + + public static ParameterIndex getParameterIndex(Var param) { + return definedParameterIndexes.get(param); + } + + public static void disableRelay(LoadField loadField) { + relayedLoadFields.add(loadField); + } + + public static boolean isNonRelay(LoadField loadField) { + return relayedLoadFields.contains(loadField); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java new file mode 100644 index 000000000..432410873 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java @@ -0,0 +1,80 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container; + +import pascal.taie.World; +import pascal.taie.language.classes.ClassHierarchy; +import pascal.taie.language.classes.JClass; +import pascal.taie.language.type.Type; +import pascal.taie.language.type.TypeSystem; + +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.containerType.MAP; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.containerType.OTHER; + +public class ClassAndTypeClassifier { + private static final ClassHierarchy hierarchy = World.get().getClassHierarchy(); + + private static final TypeSystem typeSystem = World.get().getTypeSystem(); + + private static final JClass mapClass = hierarchy.getClass("java.util.Map"); + private static final JClass collectionClass = hierarchy.getClass("java.util.Collection"); + private static final JClass mapEntryClass = hierarchy.getClass("java.util.Map$Entry"); + + private static final JClass iteratorClass = hierarchy.getClass("java.util.Iterator"); + + private static final JClass enumerationClass = hierarchy.getClass("java.util.Enumeration"); + + private static final JClass hashtableClass = hierarchy.getClass("java.util.Hashtable"); + private static final Type hashtableType = typeSystem.getType("java.util.Hashtable"); + + private static final JClass vectorClass = hierarchy.getClass("java.util.Vector"); + private static final Type vectorType = typeSystem.getType("java.util.Vector"); + + private static final JClass abstractList = hierarchy.getClass("java.util.AbstractList"); + + public enum containerType { + MAP, COLLECTION, OTHER + } + + public static containerType ClassificationOf(Type type) { + JClass clz = hierarchy.getClass(type.getName()); + if (clz == null) + return OTHER; + if (hierarchy.isSubclass(mapClass, clz)) + return MAP; + if (hierarchy.isSubclass(collectionClass, clz)) + return containerType.COLLECTION; + return OTHER; + } + + public static boolean isIteratorClass(JClass iterator) { + return hierarchy.isSubclass(iteratorClass, iterator); + } + + public static boolean isEnumerationClass(JClass enumeration) { + return hierarchy.isSubclass(enumerationClass, enumeration) && !enumeration.isApplication(); + } + + public static boolean isAbstractListClass(JClass clz) { + return hierarchy.isSubclass(abstractList, clz); + } + + public static boolean isMapEntryClass(JClass entry) { + return hierarchy.isSubclass(mapEntryClass, entry) && !entry.isApplication(); + } + + public static boolean isHashtableClass(JClass hashtable) { + return hierarchy.isSubclass(hashtableClass, hashtable) && !hashtable.isApplication(); + } + + public static boolean isVectorClass(JClass vector) { + return hierarchy.isSubclass(vectorClass, vector) && !vector.isApplication(); + } + + public static boolean isVectorType(Type vector) { + return typeSystem.isSubtype(vectorType, vector); + } + + public static boolean isHashtableType(Type hashtable) { + return typeSystem.isSubtype(hashtableType, hashtable); + } + +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java new file mode 100644 index 000000000..0bbf88f22 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java @@ -0,0 +1,678 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container; + +import pascal.taie.analysis.graph.callgraph.CallGraph; +import pascal.taie.analysis.graph.callgraph.CallKind; +import pascal.taie.analysis.graph.callgraph.Edge; +import pascal.taie.analysis.graph.flowgraph.FlowKind; +import pascal.taie.analysis.pta.core.cs.context.Context; +import pascal.taie.analysis.pta.core.cs.element.ArrayIndex; +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.cs.element.HostPointer; +import pascal.taie.analysis.pta.core.cs.element.Pointer; +import pascal.taie.analysis.pta.core.heap.HeapModel; +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.core.solver.CutShortcutSolver; +import pascal.taie.analysis.pta.core.solver.PointerFlowEdge; +import pascal.taie.analysis.pta.core.solver.Solver; +import pascal.taie.analysis.pta.plugin.Plugin; +import pascal.taie.analysis.pta.plugin.cutshortcut.SpecialVariables; +import pascal.taie.analysis.pta.pts.PointsToSet; +import pascal.taie.ir.exp.InvokeExp; +import pascal.taie.ir.exp.InvokeInstanceExp; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.Invoke; +import pascal.taie.ir.stmt.New; +import pascal.taie.language.classes.JClass; +import pascal.taie.language.classes.JMethod; +import pascal.taie.language.classes.Subsignature; +import pascal.taie.language.type.ArrayType; +import pascal.taie.language.type.ClassType; +import pascal.taie.language.type.Type; +import pascal.taie.language.type.TypeSystem; +import pascal.taie.util.AnalysisException; +import pascal.taie.util.collection.*; + +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSet; + +import static pascal.taie.analysis.pta.core.solver.CutShortcutSolver.isConcerned; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.*; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList.Kind.*; + +public class ContainerAccessHandler implements Plugin { + private CutShortcutSolver solver; + + private CSManager csManager; + + private HeapModel heapModel; + + private Context emptyContext; + + private TypeSystem typeSystem; + + private static ContainerConfig containerConfig; + + private CallGraph callGraph; + + private final String[] categories = new String[]{"Map-Key", "Map-Value", "Col-Value"}; + + private final HostList.Kind[] SetKindsInMap = new HostList.Kind[]{MAP_KEY_SET, MAP_VALUES, MAP_ENTRY_SET}; + + private final MultiMap ArrayVarToVirtualArrayVar = Maps.newMultiMap(); + + private final MultiMap CollectionVarToVirtualArrayVar = Maps.newMultiMap(); + + private final MultiMap ExtenderLargerToSmaller = Maps.newMultiMap(); + private final MultiMap ExtenderSmallerToLarger = Maps.newMultiMap(); + + private final Map pointerHostListMap = Maps.newMap(); + + private final Map>> csVarMustRelatedInvokes = Maps.newMap(); + + private final MultiMap, Pair> HostPropagater = Maps.newMultiMap(); + + private final Map VirtualArgsOfEntrySet = Maps.newMap(); + + private final MultiMap hostToExits = Maps.newMultiMap(); + + private final Map abstractListToGet = Maps.newMap(); + + public void setSolver(Solver solver) { + if (solver instanceof CutShortcutSolver cutShortcutSolver) { + this.solver = cutShortcutSolver; + csManager = solver.getCSManager(); + emptyContext = solver.getContextSelector().getEmptyContext(); + typeSystem = solver.getTypeSystem(); + heapModel = solver.getHeapModel(); + containerConfig = ContainerConfig.config; + if (containerConfig == null) + throw new AnalysisException("No containerConfig for Host Manager!"); + callGraph = solver.getCallGraph(); + } + else { + throw new AnalysisException("Invalid solver!"); + } + } + + private JMethod resolveMethod(JClass clz, Subsignature subSig) { + if (clz == null) + return null; + if (clz.getDeclaredMethod(subSig) != null) + return clz.getDeclaredMethod(subSig); + return resolveMethod(clz.getSuperClass(), subSig); + } + + @Override + public void onStart() { + containerConfig.getAllEntrySetClasses().forEach(c -> { + Var argVar = new Var(null, c + "/arg", typeSystem.getType("java.lang.Object"), -1); + SpecialVariables.setVirtualVar(argVar); + VirtualArgsOfEntrySet.put(c, argVar); + }); + containerConfig.taintAbstractListClasses().forEach(jClass -> { + JMethod getMethod = resolveMethod(jClass, Subsignature.get("java.lang.Object get(int)")); + if (getMethod != null) { + abstractListToGet.put(jClass, getMethod); + } + }); + } + + @Override + public void onNewPointsToSet(CSVar csVar, PointsToSet pts) { + HostSet colSet = solver.getEmptyHostSet(), mapSet = solver.getEmptyHostSet(); + pts.forEach(csObj -> { + Obj obj = csObj.getObject(); + Type objType = obj.getType(); + if (containerConfig.isHostType(objType)) { + switch (ClassificationOf(objType)) { + case MAP -> mapSet.addHost(containerConfig.getObjectHost(obj, Host.Classification.MAP)); + case COLLECTION -> colSet.addHost(containerConfig.getObjectHost(obj, Host.Classification.COLLECTION)); + } + } + ArrayVarToVirtualArrayVar.get(csVar.getVar()).forEach(virtualArray -> { + CSVar csVirtualArray = csManager.getCSVar(emptyContext, virtualArray); + ArrayIndex arrayIndex = csManager.getArrayIndex(csObj); + solver.addPFGEdge(arrayIndex, csVirtualArray, FlowKind.VIRTUAL_ARRAY, virtualArray.getType()); + }); + }); + if (!mapSet.isEmpty()) { + getHostListOf(csVar).addHostSet(MAP_0, mapSet); + onNewHostEntry(csVar, MAP_0, mapSet); + } + if (!colSet.isEmpty()) { + getHostListOf(csVar).addHostSet(COL_0, colSet); + onNewHostEntry(csVar, COL_0, colSet); + } + } + + public void onNewHostEntry(CSVar csVar, HostList.Kind kind, HostSet hostSet) { + propagateHostAndKind(csVar, hostSet, kind); + ProcessMustRelatedInvokes(csVar, hostSet); + Var base = csVar.getVar(); + base.getInvokes().forEach(invoke -> { + if (isRelatedEntranceInvoke(invoke)) { + CSCallSite csCallSite = csManager.getCSCallSite(emptyContext, invoke); + InvokeExp invokeExp = invoke.getInvokeExp(); + callGraph.getCalleesOf(csCallSite).forEach(csMethod -> { + JMethod method = csMethod.getMethod(); + int npara = method.getParamCount(); + for (int i = 0; i < npara; i++) { + relateSourceToHosts(invokeExp, method, i, hostSet); + } + }); + } + }); + CollectionVarToVirtualArrayVar.get(base).forEach(virtualArray -> { + if (kind == COL_0) + hostSet.forEach(host -> addSourceToHost(virtualArray, host, "Col-Value")); + else { + hostSet.forEach(host -> addSourceToHost(virtualArray, host, "Map-Value")); + hostSet.forEach(host -> addSourceToHost(virtualArray, host, "Map-Key")); + } + }); + ExtenderLargerToSmaller.get(base).forEach(smaller -> { + CSVar smallerPointer = csManager.getCSVar(emptyContext, smaller); + HostList hostMap = getHostListOf(smallerPointer); + if (kind == COL_0) { + if (hostMap.hasKind(COL_0)) + addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(COL_0)), true, false, false); + if (hostMap.hasKind(MAP_KEY_SET)) + addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(MAP_KEY_SET)), false, true, false); + if (hostMap.hasKind(MAP_VALUES)) + addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(MAP_VALUES)), false, false, true); + } + else if (kind == MAP_0) { + if (hostMap.hasKind(MAP_0)) + addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(MAP_0)), + true, false, false); + } + }); + ExtenderSmallerToLarger.get(base).forEach(larger -> { + CSVar largerPointer = csManager.getCSVar(emptyContext, larger); + HostList hostMap = getHostListOf(largerPointer); + if (hostMap.hasKind(COL_0)) { + HostSet set = hostMap.getHostSetOf(COL_0); + if (kind == COL_0) + addHostSubsetRelation(set, hostSet, true, false, false); + else if (kind == MAP_KEY_SET) + addHostSubsetRelation(set, hostSet, false, true, false); + else if (kind == MAP_VALUES) + addHostSubsetRelation(set, hostSet, false, false, true); + + } + if (kind == MAP_0) { + if (hostMap.hasKind(MAP_0)) + addHostSubsetRelation(Objects.requireNonNull(hostMap.getHostSetOf(MAP_0)), hostSet, + true, false, false); + } + }); + HostPropagater.get(new Pair<>(kind, csVar)).forEach(kindCSVarPair -> solver.addHostEntry(kindCSVarPair.second(), kindCSVarPair.first(), hostSet)); + HostPropagater.get(new Pair<>(ALL, csVar)).forEach(kindCSVarPair -> solver.addHostEntry(kindCSVarPair.second(), kind, hostSet)); + for (HostList.Kind k: SetKindsInMap) { + if (k == kind) { + base.getInvokes().forEach(callSite -> { + CSCallSite csCallSite = csManager.getCSCallSite(emptyContext, callSite); + callGraph.getCalleesOf(csCallSite).forEach(csCallee -> { + Var thisVar = csCallee.getMethod().getIR().getThis(); + if (thisVar != null) { + CSVar csThis = csManager.getCSVar(emptyContext, thisVar); + solver.addHostEntry(csThis, kind, hostSet); + } + }); + }); + } + } + } + + private void processArrayInitializer(Invoke callSite, JMethod callee) { + solver.addSelectedMethod(callee); + Pair arrayCollectionPair = containerConfig.getArrayInitializer(callee); + InvokeExp invoke = callSite.getInvokeExp(); + Var arrayVar = getArgument(invoke, arrayCollectionPair.first()), + collectionVar = getArgument(invoke, arrayCollectionPair.second()); + if (!(arrayVar.getType() instanceof ArrayType)) + throw new AnalysisException("Not Array Type!"); + Type elementType = ((ArrayType) arrayVar.getType()).elementType(); + Var virtualArrayVar = new Var(callSite.getContainer(), "virtualArrayVar", elementType, -1); + SpecialVariables.setVirtualVar(virtualArrayVar); + ArrayVarToVirtualArrayVar.put(arrayVar, virtualArrayVar); + CollectionVarToVirtualArrayVar.put(collectionVar, virtualArrayVar); + CSVar csArray = csManager.getCSVar(emptyContext, arrayVar); + solver.getPointsToSetOf(csArray).forEach(csObj -> { + ArrayIndex arrayIndex = csManager.getArrayIndex(csObj); + solver.addPFGEdge(arrayIndex, csManager.getCSVar(emptyContext, virtualArrayVar), FlowKind.VIRTUAL_ARRAY, elementType); + }); + CSVar csCollection = csManager.getCSVar(emptyContext, collectionVar); + HostList hostMap = getHostListOf(csCollection); + if (hostMap.hasKind(COL_0)) { + hostMap.getHostSetOf(COL_0).forEach(host -> { + addSourceToHost(virtualArrayVar, host, "Col-Value"); + }); + } + else if (hostMap.hasKind(MAP_0)) { + hostMap.getHostSetOf(MAP_0).forEach(host -> { + addSourceToHost(virtualArrayVar, host, "Map-Key"); + addSourceToHost(virtualArrayVar, host, "Map-Value"); + }); + } + } + + @Override + public void onNewCallEdge(Edge edge) { + if (edge.getKind() != CallKind.OTHER) { + CSMethod csCallee = edge.getCallee(); + Invoke callSite = edge.getCallSite().getCallSite(); + JMethod callee = csCallee.getMethod(); + InvokeExp invokeExp = callSite.getInvokeExp(); + if (invokeExp instanceof InvokeInstanceExp instanceExp) { + Var base = instanceExp.getBase(), thisVar = callee.getIR().getThis(); + CSVar csBase = csManager.getCSVar(emptyContext, base); + CSVar csThis = csManager.getCSVar(emptyContext, thisVar); + HostList hostMap = getHostListOf(csBase); + for (HostList.Kind k: SetKindsInMap) { + if (hostMap.hasKind(k)) { + solver.addHostEntry(csThis, k, hostMap.getHostSetOf(k)); + HostPropagater.put(new Pair<>(k, csBase), new Pair<>(k, csThis)); + } + } + } + if (containerConfig.isCorrelationExtender(callee)) + processCorrelationExtender(callSite, callee); + if (containerConfig.getArrayInitializer(callee) != null) + processArrayInitializer(callSite, callee); + for (int i = 0; i < invokeExp.getArgCount(); ++i) { + Var arg = invokeExp.getArg(i); + if (isConcerned(arg)) { + if (isRelatedEntranceInvoke(callSite) && invokeExp instanceof InvokeInstanceExp instanceExp) { + Var base = instanceExp.getBase(); + CSVar csBase = csManager.getCSVar(emptyContext, base); + int finalI = i; + getHostListOf(csBase).forEach((x, s) -> relateSourceToHosts(invokeExp, callee, finalI, s)); + } + } + } + processCollectionOutInvoke(callSite, callee); + } + } + + private static JClass getOuterClass(JClass inner) { + if (inner != null) { + if (inner.hasOuterClass()) + inner = inner.getOuterClass(); + return inner; + } + return null; + } + + private static boolean isIteratorPollMethod(JMethod method) { + String sig = method.getSubsignature().toString(); + return containerConfig.isIteratorClass(method.getDeclaringClass()) && (sig.contains("next()") || + sig.contains("previous()")); + } + + private static boolean isEnumerationPollMethod(JMethod method) { + return isEnumerationClass(method.getDeclaringClass()) + && method.getSubsignature().toString().contains("nextElement()"); + } + + public static boolean CutReturnEdge(Invoke invoke, JMethod method) { + String methodKind = containerConfig.CategoryOfExit(method); + JClass calleeClass = method.getDeclaringClass(); + String signature = method.getSubsignature().toString(); + if (!Objects.equals(methodKind, "Other")) + return true; + if (invoke.getInvokeExp() instanceof InvokeInstanceExp e && !e.getBase().getName().equals("%this")) { + if (!isIteratorClass(invoke.getContainer().getDeclaringClass()) && + isMapEntryClass(calleeClass) && (signature.contains("getValue(") || signature.contains("getKey("))) { + return true; + } + if (isIteratorPollMethod(method)) + return true; + if (isEnumerationPollMethod(method)) { + Type outerType = getOuterClass(calleeClass).getType(); + return containerConfig.isHostType(outerType) || outerType.getName().equals("java.util.Collections"); + } + } + return false; + } + + @Override + public void onNewMethod(JMethod method) { + method.getIR().forEach(s -> { + if (s instanceof New stmt) { + Obj obj = heapModel.getObj(stmt); + Type objType = obj.getType(); + JClass clz = method.getDeclaringClass(); + if (objType instanceof ClassType clzType && isMapEntryClass(clzType.getJClass())) { + containerConfig.getRelatedEntrySetClassesOf(clz).forEach(entry -> { + Var arg = VirtualArgsOfEntrySet.get(entry); + CSVar csArg = csManager.getCSVar(emptyContext, arg); + solver.addPointsTo(csArg, csManager.getCSObj(emptyContext, obj)); + }); + } + if (containerConfig.isHostType(objType)) { + Host newHost = null; + switch (ClassificationOf(objType)) { + case MAP -> newHost = containerConfig.getObjectHost(obj, Host.Classification.MAP); + case COLLECTION -> newHost = containerConfig.getObjectHost(obj, Host.Classification.COLLECTION); + } + JClass hostClass = ((ClassType) objType).getJClass(); + if (containerConfig.isEntrySetClass(hostClass)) { + Var arg = VirtualArgsOfEntrySet.get(hostClass); + addSourceToHost(arg, newHost, "Col-Value"); + } + if (containerConfig.isTaintAbstractListType(objType)) { + JMethod get = abstractListToGet.get(hostClass); + final Host temp = newHost; + get.getIR().getReturnVars().forEach(ret -> { + addSourceToHost(ret, temp, "Col-Value"); + }); + } + } + if (!method.isStatic()) { + CSVar csThis = csManager.getCSVar(emptyContext, method.getIR().getThis()); + CSVar csLHS = csManager.getCSVar(emptyContext, stmt.getLValue()); + if (containerConfig.isKeySetClass(objType.getName())) { + HostPropagater.put(new Pair<>(MAP_0, csThis), new Pair<>(MAP_KEY_SET, csLHS)); + } + if (containerConfig.isValueSetClass(objType.getName())) { + HostPropagater.put(new Pair<>(MAP_0, csThis), new Pair<>(MAP_VALUES, csLHS)); + } + } + } + }); + } + + private void processCollectionOutInvoke(Invoke callSite, JMethod callee) { + Var lhs = callSite.getLValue(); + String calleeSig = callee.getMethodSource().toString(); + if (lhs != null && isConcerned(lhs) && callSite.getInvokeExp() instanceof InvokeInstanceExp instanceExp) { + String methodKind = containerConfig.CategoryOfExit(callee); + Var base = instanceExp.getBase(); + CSVar csBase = csManager.getCSVar(emptyContext, base); + HostList hostMap = getHostListOf(csBase); + if (!Objects.equals(methodKind, "Other")) { + solver.addSelectedMethod(callee); + csVarMustRelatedInvokes.computeIfAbsent(csBase, v -> Sets.newSet()).add(new Pair<>(callSite, methodKind)); + if (Objects.equals(methodKind, "Col-Value")) { + if (hostMap.hasKind(COL_0)) { + Objects.requireNonNull(hostMap.getHostSetOf(COL_0)).forEach(host -> { + if (typeSystem.isSubtype(base.getType(), host.getType())) + checkHostRelatedExit(callSite, host, methodKind); + }); + } + } + else { + if (hostMap.hasKind(MAP_0)) { + hostMap.getHostSetOf(MAP_0).forEach(host -> { + if (typeSystem.isSubtype(base.getType(), host.getType())) + checkHostRelatedExit(callSite, host, methodKind); + }); + } + } + } + if (isMapEntryClass(callee.getDeclaringClass()) && hostMap.hasKind(MAP_ENTRY)) { + HostSet set = hostMap.getHostSetOf(MAP_ENTRY); + if (set != null) { + if (calleeSig.contains("getValue(")) { + solver.addSelectedMethod(callee); + set.forEach(host -> checkHostRelatedExit(callSite, host, "Map-Value")); + } + if (calleeSig.contains("getKey(")) { + solver.addSelectedMethod(callee); + set.forEach(host -> checkHostRelatedExit(callSite, host, "Map-Key")); + } + } + } + if (isIteratorClass(callee.getDeclaringClass()) && (calleeSig.contains("next()") || + calleeSig.contains("previous()"))) { + solver.addSelectedMethod(callee); + if (hostMap.hasKind(MAP_VALUE_ITR)) + hostMap.getHostSetOf(MAP_VALUE_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Value")); + if (hostMap.hasKind(MAP_KEY_ITR)) + hostMap.getHostSetOf(MAP_KEY_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Key")); + if (hostMap.hasKind(COL_ITR)) + hostMap.getHostSetOf(COL_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Col-Value")); + } + if (isEnumerationClass(callee.getDeclaringClass()) && calleeSig.contains("nextElement()")) { + solver.addSelectedMethod(callee); + if (hostMap.hasKind(MAP_VALUE_ITR)) + hostMap.getHostSetOf(MAP_VALUE_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Value")); + if (hostMap.hasKind(MAP_KEY_ITR)) + hostMap.getHostSetOf(MAP_KEY_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Key")); + if (hostMap.hasKind(COL_ITR)) + hostMap.getHostSetOf(COL_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Col-Value")); + } + } + } + + private boolean isRelatedEntranceInvoke(Invoke invoke) { + return !containerConfig.isUnrelatedInInvoke(invoke); + } + + private Var getArgument(InvokeExp invokeExp, int index) { + return index == -1 ? ((InvokeInstanceExp) invokeExp).getBase() + : invokeExp.getArg(index); + } + + private Set coes = Sets.newSet(); + + private void processCorrelationExtender(Invoke callSite, JMethod callee) { + solver.addSelectedMethod(callee); + containerConfig.getCorrelationExtender(callee).forEach(indexPair -> { + if (callSite.getInvokeExp() instanceof InvokeInstanceExp instanceExp && !instanceExp.getBase().equals(callee.getIR().getThis())) { + coes.add(callee); + int largerIndex = indexPair.first(), smallerIndex = indexPair.second(); + Var arg1 = getArgument(instanceExp, largerIndex); + Var arg2 = getArgument(instanceExp, smallerIndex); + if (isConcerned(arg1) && isConcerned(arg2)) { + ExtenderLargerToSmaller.put(arg1, arg2); + ExtenderSmallerToLarger.put(arg2, arg1); + CSVar smallerPointer = csManager.getCSVar(emptyContext, arg2), largerPointer = csManager.getCSVar(emptyContext, arg1); + HostList largerMap = pointerHostListMap.get(largerPointer), smallerMap = pointerHostListMap.get(smallerPointer); + + if (largerMap.hasKind(COL_0)) { + HostSet set = largerMap.getHostSetOf(COL_0); + if (smallerMap.hasKind(COL_0)) + addHostSubsetRelation(set, Objects.requireNonNull(smallerMap.getHostSetOf(COL_0)), true, false, false); + if (smallerMap.hasKind(MAP_KEY_SET)) + addHostSubsetRelation(set, Objects.requireNonNull(smallerMap.getHostSetOf(MAP_KEY_SET)), false, true, false); + if (smallerMap.hasKind(MAP_VALUES)) + addHostSubsetRelation(set, Objects.requireNonNull(smallerMap.getHostSetOf(MAP_VALUES)), false, false, true); + } + if (largerMap.hasKind(MAP_0) && smallerMap.hasKind(MAP_0)) { + addHostSubsetRelation(largerMap.getHostSetOf(MAP_0), + Objects.requireNonNull(smallerMap.getHostSetOf(MAP_0)), true, false, false); + } + } + } + }); + } + + private void addHostSubsetRelation(HostSet largerHosts, HostSet smallerHosts, boolean newHost, boolean keySet, boolean values) { + smallerHosts.forEach(host -> { + largerHosts.forEach(largerHost -> { + boolean taint = containerConfig.isTaintType(host.getType()); + if (!largerHost.getTaint() && !largerHost.getType().getName().contains("java.util.Collections$Empty")) { + if (!taint && !host.getTaint()) { + if (newHost) { + if (host.getClassification() == Host.Classification.COLLECTION + && largerHost.getClassification() == Host.Classification.COLLECTION) { + HostPointer smallerPointer = csManager.getHostPointer(host, "Col-Value"), + largerPointer = csManager.getHostPointer(largerHost, "Col-Value"); + solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); + } + if (host.getClassification() == Host.Classification.MAP + && largerHost.getClassification() == Host.Classification.MAP) { + HostPointer smallerPointer = csManager.getHostPointer(host, "Map-Key"), + largerPointer = csManager.getHostPointer(largerHost, "Map-Key"); + solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); + + smallerPointer = csManager.getHostPointer(host, "Map-Value"); + largerPointer = csManager.getHostPointer(largerHost, "Map-Value"); + solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); + } + } + if (keySet && host.getClassification() == Host.Classification.MAP + && largerHost.getClassification() == Host.Classification.COLLECTION) { + HostPointer smallerPointer = csManager.getHostPointer(host, "Map-Key"), + largerPointer = csManager.getHostPointer(largerHost, "Col-Value"); + solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); + } + if (values && host.getClassification() == Host.Classification.MAP + && largerHost.getClassification() == Host.Classification.COLLECTION) { + HostPointer smallerPointer = csManager.getHostPointer(host, "Map-Value"), + largerPointer = csManager.getHostPointer(largerHost, "Col-Value"); + solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); + } + } + else { + taintHost(largerHost); + } + } + }); + }); + } + + private void propagateHostAndKind(CSVar csVar, HostSet hostSet, HostList.Kind kind) { + Var varBase = csVar.getVar(); + varBase.getInvokes().forEach(invoke -> { + Var lhs = invoke.getLValue(); + if (lhs != null && isConcerned(lhs)) { + InvokeExp invokeExp = invoke.getInvokeExp(); + String invokeString = invokeExp.getMethodRef().getName(); + CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + ContainerConfig.getHostGenerators().forEach((kind_ori, keyString, kind_gen) -> { + if (kind == kind_ori && invokeString.contains(keyString)) { + solver.addHostEntry(csLHS, kind_gen, hostSet); + } + }); + ContainerConfig.getNonContainerExits().forEach((kind_required, invoke_str, category) -> { + if (kind == kind_required && invokeString.contains(invoke_str)) + hostSet.forEach(host -> checkHostRelatedExit(invoke, host, category)); + }); + switch (kind) { + case COL_0 -> { + if (invokeString.equals("elements") && isVectorType(varBase.getType())) { + solver.addHostEntry(csLHS, COL_ITR, hostSet); + } + } + case MAP_0 -> { + if ((invokeString.equals("elements") && isHashtableType(varBase.getType()))) { + solver.addHostEntry(csLHS, MAP_VALUE_ITR, hostSet); + } + } + } + } + }); + } + + private void ProcessMustRelatedInvokes(CSVar csVar, HostSet hostSet) { + Var varBase = csVar.getVar(); + csVarMustRelatedInvokes.computeIfAbsent(csVar, v -> Sets.newSet()).forEach(invokeCategoryPair -> { // out invokes + Var lhs = invokeCategoryPair.first().getLValue(); + if (lhs != null && isConcerned(lhs)) { + hostSet.forEach(host -> { + if (typeSystem.isSubtype(varBase.getType(), host.getType())) + checkHostRelatedExit(invokeCategoryPair.first(), host, invokeCategoryPair.second()); + }); + } + }); + } + + private void recoverCallSite(Invoke callSite) { + if (solver.addRecoveredCallSite(callSite)) { + CSCallSite csCallSite = csManager.getCSCallSite(emptyContext, callSite); + CSVar csLHS = csManager.getCSVar(emptyContext, callSite.getLValue()); + callGraph.getCalleesOf(csCallSite).forEach(csCallee -> { + JMethod callee = csCallee.getMethod(); + callee.getIR().getReturnVars().forEach(ret -> { + CSVar csRet = csManager.getCSVar(emptyContext, ret); + solver.addPFGEdge(csRet, csLHS, FlowKind.RETURN); + }); + }); + } + } + + private void taintHost(Host host) { + if (!host.getTaint() && !containerConfig.isTaintAbstractListType(host.getType())) { + host.setTaint(); + hostToExits.get(host).forEach(this::recoverCallSite); + for (String cat: categories) { + csManager.getHostPointer(host, cat).getOutEdges().forEach(outEdge -> { + Pointer succ = outEdge.target(); + if (succ instanceof HostPointer hostPointer) { + taintHost(hostPointer.getHost()); + } + }); + } + } + } + + private void checkHostRelatedExit(Invoke callSite, Host host, String category) { + Var lhs = callSite.getLValue(); + if (lhs != null && isConcerned(lhs) && !solver.isRecoveredCallSite(callSite)) { + if (host.getTaint()) + recoverCallSite(callSite); + else { + hostToExits.put(host, callSite); + addTargetToHost(lhs, host, category); + } + } + + } + + private void addTargetToHost(Var result, Host host, String category) { + if (result != null && isConcerned(result) && host.addOutResult(result, category)) { + HostPointer hostPointer = csManager.getHostPointer(host, category); + CSVar target = csManager.getCSVar(emptyContext, result); + solver.addPFGEdge(hostPointer, target, //result.getType(), + FlowKind.HOST_TO_RESULT); + } + } + + private void relateSourceToHosts(InvokeExp invokeExp, JMethod callee, int index, HostSet hostSet) { + ClassType classType; + for (String category: categories) { + if ((classType = containerConfig.getTypeConstraintOf(callee, index, category)) != null) { + solver.addSelectedMethod(callee); + Var argument = invokeExp.getArg(index); + ClassType type = classType; + hostSet.hosts().filter(d -> !d.getTaint() && typeSystem.isSubtype(type, d.getType())) + .forEach(d -> addSourceToHost(argument, d, category)); + } + } + } + + private void addSourceToHost(Var arg, Host host, String category) { + if (host.getTaint()) + return; + if (arg != null && isConcerned(arg) && host.addInArgument(arg, category)) { + CSVar source = csManager.getCSVar(emptyContext, arg); + HostPointer hostPointer = csManager.getHostPointer(host, category); + solver.addPFGEdge(source, hostPointer, FlowKind.ARG_TO_HOST); + } + } + + @Override + public void onNewPFGEdge(PointerFlowEdge edge) { + Pointer source = edge.source(), target = edge.target(); + FlowKind kind = edge.kind(); + if (solver.needPropagateHost(source, kind)) { + HostList hostMap = getHostListOf(source); + if (!hostMap.isEmpty()) + hostMap.forEach((k, set) -> solver.addHostEntry(target, k, set)); + } + } + + public HostList getHostListOf(Pointer pointer) { + return pointerHostListMap.computeIfAbsent(pointer, v -> new HostList()); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java new file mode 100644 index 000000000..fe092af92 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java @@ -0,0 +1,428 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pascal.taie.World; +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; +import pascal.taie.ir.stmt.Invoke; +import pascal.taie.language.classes.ClassHierarchy; +import pascal.taie.language.classes.JClass; +import pascal.taie.language.classes.JMethod; +import pascal.taie.language.type.ClassType; +import pascal.taie.language.type.Type; +import pascal.taie.util.AnalysisException; +import pascal.taie.util.Indexer; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.MultiMap; +import pascal.taie.util.collection.Pair; +import pascal.taie.util.collection.Sets; +import pascal.taie.util.collection.TwoKeyMap; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; + +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.ClassificationOf; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList.Kind.*; + +public class ContainerConfig { + public static ContainerConfig config = new ContainerConfig(); + private static final Logger logger = LogManager.getLogger(ContainerConfig.class); + private final TwoKeyMap parameters = Maps.newTwoKeyMap(); + private final Map ParameterOfColValue = Maps.newMap(); + + private final Map ParameterOfMapValue = Maps.newMap(); + private final Map ParameterOfMapKey = Maps.newMap(); + + private final Set excludedContainers = Sets.newSet(); + private final Set keySet = Sets.newSet(); + private final Set valueSet = Sets.newSet(); + + private final Set unrelatedInInvokes = Sets.newSet(); + + private final Set OutMethodsOfMapKey = Sets.newSet(); + private final Set OutMethodsOfMapValue = Sets.newSet(); + private final Set OutMethodsOfColValue = Sets.newSet(); + + private final Set iteratorClasses = Sets.newSet(); + + private final HostManager hostManager = new HostManager(); + + /** + * Map correlation-extending JMethods to their parameters + */ + private final MultiMap> corExtenders = Maps.newMultiMap(); // parameters in methods which are similar to "putAll"/"addAll" + + private final Map> arrayInitializer = Maps.newMap(); + + private static final ClassHierarchy hierarchy = World.get().getClassHierarchy(); + + private static final TwoKeyMap hostGenerators = Maps.newTwoKeyMap(); + + private final Set hostClasses = Sets.newSet(); + + // r = x.foo(...) + // TwoKeyMap pair (type in [Map-Key, Map-Value, Col-Value] + // if x has containerKind kind and foo has a substring (specified in the map), then r will be added to the result set + // (of type) of x + private static final TwoKeyMap NonContainerExits = Maps.newTwoKeyMap(); + + private final MultiMap allocSiteOfEntrySet = Maps.newMultiMap(); + + private final Set taintClasses = Sets.newSet(); + + private final Set taintAbstractListClasses = Sets.newSet(); + + static { // initialize hostGenerators (host-passer means pass a host to lhs at an invoke when the base-variable has a required kind) + hostGenerators.put(COL_0, "iterator", COL_ITR); + hostGenerators.put(COL_0, "Iterator", COL_ITR); + hostGenerators.put(MAP_0, "entrySet", MAP_ENTRY_SET); + hostGenerators.put(MAP_0, "keySet", MAP_KEY_SET); + hostGenerators.put(MAP_0, "KeySet", MAP_KEY_SET); + hostGenerators.put(MAP_0, "values", MAP_VALUES); + hostGenerators.put(MAP_0, "Entry", MAP_ENTRY); + hostGenerators.put(MAP_0, "keys", MAP_KEY_ITR); + hostGenerators.put(MAP_ENTRY_SET, "iterator", MAP_ENTRY_ITR); + hostGenerators.put(MAP_VALUES, "iterator", MAP_VALUE_ITR); + hostGenerators.put(MAP_KEY_SET, "iterator", MAP_KEY_ITR); + hostGenerators.put(MAP_ENTRY_ITR, "next", MAP_ENTRY); + + NonContainerExits.put(MAP_ENTRY, "getValue", "Map-Value"); + NonContainerExits.put(MAP_ENTRY, "getKey", "Map-Key"); + NonContainerExits.put(MAP_KEY_ITR, "next", "Map-Key"); + NonContainerExits.put(MAP_KEY_ITR, "nextElement", "Map-Key"); + NonContainerExits.put(MAP_VALUE_ITR, "next", "Map-Value"); + NonContainerExits.put(MAP_VALUE_ITR, "nextElement", "Map-Value"); + NonContainerExits.put(COL_ITR, "next", "Col-Value"); + NonContainerExits.put(COL_ITR, "nextElement", "Col-Value"); + NonContainerExits.put(COL_ITR, "previous", "Col-Value"); + } + + public boolean isTaintType(Type type) { + if (type instanceof ClassType classType) { +// return ClassificationOf(type) != ClassAndTypeClassifier.containerType.OTHER && classType.getJClass().isApplication(); + return taintClasses.contains(classType.getJClass()); + } + return false; + } + + public boolean isRealHostClass(JClass clz) { + return hostClasses.contains(clz); + } + + public boolean isTaintAbstractListType(Type type) { + if (type instanceof ClassType classType) { + return taintAbstractListClasses.contains(classType.getJClass()); + } + return false; + } + + public Stream taintAbstractListClasses() { + return taintAbstractListClasses.stream(); + } + + public void addHostClass(String clz) { + JClass c = hierarchy.getClass(clz); + if (c != null && !c.isAbstract()) + hostClasses.add(c); + } + + public void computeTaintClass() { + JClass c = hierarchy.getClass("java.util.Collection"), ca = hierarchy.getClass("java.util.AbstractList"); + hierarchy.getAllSubclassesOf(c).forEach(clz -> { + if (!clz.isAbstract() && !hostClasses.contains(clz) && !excludedContainers.contains(clz.getName())) { + if (hierarchy.isSubclass(ca, clz)) + taintAbstractListClasses.add(clz); + else + taintClasses.add(clz); + } + }); + c = hierarchy.getClass("java.util.Map"); + hierarchy.getAllSubclassesOf(c).forEach(clz -> { + if (!clz.isAbstract() && !hostClasses.contains(clz) && !excludedContainers.contains(clz.getName())) { + taintClasses.add(clz); + } + }); +// taintClasses.forEach(System.out::println); + } + + public static void setConfig(ContainerConfig config) { + ContainerConfig.config = config; + } + + public JMethod getMethod(String signature) { + return hierarchy.getMethod(signature); + } + + public Parameter getParameter(JMethod method, int index) { + if (parameters.get(method, index) == null) { + parameters.put(method, index, new Parameter(method, index)); + } + return parameters.get(method, index); + } + + public void addParameterWithType(Parameter parameter, ClassType classType, String type) { + if (classType == null) + return; + switch (type) { + case "Map-Key" -> { + if (ParameterOfMapKey.get(parameter) != null) + throw new AnalysisException("Multiple classType for parameter: " + parameter); + ParameterOfMapKey.put(parameter, classType); + } + case "Map-Value" -> { + if (ParameterOfMapValue.get(parameter) != null) { + throw new AnalysisException("Multiple classType for parameter: " + parameter); + } + ParameterOfMapValue.put(parameter, classType); + } + case "Col-Value" -> { + if (ParameterOfColValue.get(parameter) != null) + throw new AnalysisException("Multiple classType for parameter: " + parameter); + ParameterOfColValue.put(parameter, classType); + } + } + } + + public void addInParameter(String SMethod, int index, String type) { + JMethod method = hierarchy.getMethod(SMethod); + if (method == null) { + return; + } + Parameter parameter = getParameter(method, index); + ClassType classType = method.getDeclaringClass().getType(); + addInParameter(parameter, classType, type); + } + + public void addInParameter(String SMethod, int index, String type, String jClass) { + JMethod method = hierarchy.getMethod(SMethod); + if (method == null) { + throw new AnalysisException("Invalid Input Method!"); + } + Parameter parameter = getParameter(method, index); + ClassType classType = Objects.requireNonNull(hierarchy.getClass(jClass)).getType(); + addInParameter(parameter, classType, type); + } + + public void excludeClass(String clz) { + excludedContainers.add(clz); + } + + public void addKeySetClass(String clz) { + keySet.add(clz); + } + + public void addValueSetClass(String clz) { + valueSet.add(clz); + } + + public boolean isKeySetClass(String clz) { + return keySet.contains(clz); + } + + public boolean isValueSetClass(String clz) { + return valueSet.contains(clz); + } + + private void addInParameter(Parameter parameter, ClassType classType, String type) { + switch(type) { + case "Map-Key", "Map-Value", "Col-Value" -> addParameterWithType(parameter, classType, type); + default -> throw new AnalysisException("No such parameters!"); + } + } + + public ClassType getTypeConstraintOf(JMethod method, int index, String type) { + Parameter parameter = getParameter(method, index); + switch (type) { + case "Map-Key" -> { + return ParameterOfMapKey.get(parameter); + } + case "Map-Value" -> { + return ParameterOfMapValue.get(parameter); + } + case "Col-Value" -> { + return ParameterOfColValue.get(parameter); + } + default -> { + return null; + } + } + } + + public void addMapKeyExit(String... methods) { + for (String method: methods) + OutMethodsOfMapKey.add(getMethod(method)); + } + + public String CategoryOfExit(JMethod method) { + if (OutMethodsOfMapKey.contains(method)) + return "Map-Key"; + if (OutMethodsOfMapValue.contains(method)) + return "Map-Value"; + if (OutMethodsOfColValue.contains(method)) + return "Col-Value"; + return "Other"; + } + + public void addMapValueOutMethod(String... methods) { + for (String method: methods) + OutMethodsOfMapValue.add(getMethod(method)); + } + + public void addCollectionValueOutMethod(String... methods) { + for (String method: methods) + OutMethodsOfColValue.add(getMethod(method)); + } + + public void addCorrelationExtender(String inMethod, int index0, int index1) { + addCorrelationExtender(getMethod(inMethod), index0, index1); + } + + public void addCorrelationExtender(JMethod inMethod, int index0, int index1) { + if (inMethod != null) + corExtenders.put(inMethod, new Pair<>(index0, index1)); + } + + public void addArrayInitializer(String smethod, int index0, int index1) { + // index0: index of array variable, index1: index of Collection variable + JMethod method = getMethod(smethod); + assert method != null; + arrayInitializer.put(method, new Pair<>(index0, index1)); + } + + public boolean isCorrelationExtender(JMethod inMethod) { + return corExtenders.get(inMethod).size() > 0; + } + + public boolean isHostType(Type type) { + return ClassificationOf(type) != ClassAndTypeClassifier.containerType.OTHER && !excludedContainers.contains(type.getName()); + } + + public Host getObjectHost(Obj obj, Host.Classification classification) { + if (!isHostType(obj.getType())) { + logger.warn("{} is not an outer class!", obj.getType()); + return null; + } + else { + return hostManager.getHost(obj, classification); + } + + } + + public int getHostCount() { + return hostManager.getHostCount(); + } + + public void addUnrelatedInvoke(String invoke) { + unrelatedInInvokes.add(invoke); + } + + public boolean isUnrelatedInInvoke(Invoke invoke) { + return unrelatedInInvokes.contains(invoke.getContainer() + "/" + invoke.getIndex()); + } + + public Set> getCorrelationExtender(JMethod method) { + return corExtenders.get(method); + } + + public Pair getArrayInitializer(JMethod method) { + return arrayInitializer.get(method); + } + + public static TwoKeyMap getHostGenerators() { + return hostGenerators; + } + + public static TwoKeyMap getNonContainerExits() { + return NonContainerExits; + } + + public void addIteratorClass(String clz) { + JClass iclz = hierarchy.getClass(clz); + if (clz != null) + iteratorClasses.add(iclz); + } + + public boolean isIteratorClass(JClass clz) { + return iteratorClasses.contains(clz); + } + + public void addAllocationSiteOfEntrySet(String entrySet, String allocClass) { + JClass c1 = hierarchy.getClass(entrySet), c2 = hierarchy.getClass(allocClass); + if (c1 == null || c2 == null) { +// logger.warn("Invalid info about EntrySet: {} and {}", entrySet, allocClass); + } + else + addAllocationSiteOfEntrySet(c1, c2); + } + + private void addAllocationSiteOfEntrySet(JClass entrySet, JClass allocClass) { + // An EntrySet Class may have several allocation Sites which allocate its elements + // We only specified the declaring class of the container of allocation Sites. + allocSiteOfEntrySet.put(allocClass, entrySet); + entrySetClasses.add(entrySet); + } + + public Set getRelatedEntrySetClassesOf(JClass allocClass) { + return allocSiteOfEntrySet.get(allocClass); + } + + private final Set entrySetClasses = Sets.newSet(); + + public Set getAllEntrySetClasses() { + return Collections.unmodifiableSet(entrySetClasses); + } + + public boolean isEntrySetClass(JClass clz) { + return entrySetClasses.contains(clz); + } + + public Indexer getHostIndexer() { + return hostManager; + } + + private static class HostManager implements Indexer { + + private final Map objHostMap = Maps.newMap(); + + private Host[] hosts = new Host[8092]; + + private int counter = 0; + + Host getHost(Obj obj, Host.Classification classification) { + return objHostMap.computeIfAbsent(obj, o -> { + int index = counter ++; + Host host = new Host(o, index, classification); + storeHost(host, index); + return host; + }); + } + + private void storeHost(Host host, int index) { + if (index >= hosts.length) { + int newLength = Math.max(index + 1, (int) (hosts.length * 1.5)); + Host[] oldArray = hosts; + hosts = new Host[newLength]; + System.arraycopy(oldArray, 0, hosts, 0, oldArray.length); + } + hosts[index] = host; + } + + @Override + public int getIndex(Host o) { + return o.getIndex(); + } + + @Override + public Host getObject(int index) { + return hosts[index]; + } + + public int getHostCount() { + return counter; + } + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java new file mode 100644 index 000000000..8448a3b0f --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java @@ -0,0 +1,84 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container; + +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.ir.exp.Var; +import pascal.taie.language.type.Type; +import pascal.taie.util.AnalysisException; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.Sets; + +import java.util.Map; +import java.util.Set; + +public class Host { + private final Obj obj; + + private boolean taint; + + private final int index; + + private final Map> relatedArguments = Maps.newMap(); + + private final Map> relatedResults = Maps.newMap(); + + public enum Classification { + MAP, COLLECTION + } + + private Classification classification; + + public Host(Obj obj, int index, Classification classification) { + this.obj = obj; + this.index = index; + this.classification = classification; + taint = false; + relatedArguments.put("Map-Value", Sets.newSet()); + relatedArguments.put("Map-Key", Sets.newSet()); + relatedArguments.put("Col-Value", Sets.newSet()); + relatedResults.put("Map-Value", Sets.newSet()); + relatedResults.put("Map-Key", Sets.newSet()); + relatedResults.put("Col-Value", Sets.newSet()); + } + + public void setTaint() { + taint = true; + } + + public boolean getTaint() { + return taint; + } + + public int getIndex() { + return index; + } + + public boolean addInArgument(Var var, String category) { + Set arguments = relatedArguments.get(category); + if (arguments == null) + throw new AnalysisException("Invalid Category!"); + return arguments.add(var); + } + + + public boolean addOutResult(Var var, String category) { + Set results = relatedResults.get(category); + return results != null && results.add(var); + } + + public Obj getObject() { + return obj; + } + + public Type getType() { + return obj.getType(); + } + + public Classification getClassification() { + return classification; + } + + @Override + public String toString() { + return "Host-Object{" + obj.toString() + "}"; + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java new file mode 100644 index 000000000..c26cc09d5 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java @@ -0,0 +1,66 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; + +import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; +import pascal.taie.util.collection.SetEx; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Stream; + +abstract public class DelegateHostSet implements HostSet { + protected final SetEx set; + + DelegateHostSet(SetEx set) { + this.set = set; + } + + @Override + public boolean addHost(Host host) { + return set.add(host); + } + + @Override + public boolean addAll(HostSet hostSet) { + if (hostSet instanceof DelegateHostSet other) + return set.addAll(other.set); + else { + boolean changed = false; + for (Host h : hostSet) + changed |= addHost(h); + return changed; + } + } + + @Override + public boolean contains(Host host) { return set.contains(host); } + + @Override + public boolean isEmpty() { return set.isEmpty(); } + + @Override + public int size() { return set.size(); } + + @Override + public Set getHosts() { return Collections.unmodifiableSet(set); } + + @Override + public Stream hosts() { return set.stream(); } + + @Override + public void clear() { this.set.clear(); } + + @Override + public String toString() { return set.toString(); } + + @Override + public HostSet addAllDiff(HostSet hostSet) { + Set otherSet = hostSet instanceof DelegateHostSet other ? + other.set : hostSet.getHosts(); + return newSet(set.addAllDiff(otherSet)); + } + + @Override + public HostSet copy() { return newSet(set.copy()); } + + protected abstract HostSet newSet(SetEx set); +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java new file mode 100644 index 000000000..d647ef8de --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java @@ -0,0 +1,61 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; + +import pascal.taie.analysis.pta.core.cs.element.Pointer; +import pascal.taie.util.collection.Maps; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.function.BiConsumer; + +public class HostList { + private final Map map = Maps.newMap(); + + public HostList() {} + + public boolean addHostSet(Kind kind, HostSet hostSet) { + if (map.get(kind) == null) { + map.put(kind, hostSet); + return true; + } + else { + return map.get(kind).addAll(hostSet); + } + } + + public HostSet addAllDiff(Kind kind, HostSet hostSet) { + if (map.get(kind) == null) { + HostSet set = hostSet.copy(); + map.put(kind, set); + return set; + } + return map.get(kind).addAllDiff(hostSet); + } + + public boolean hasKind(Kind kind) { + return map.containsKey(kind); + } + + @Nullable + public HostSet getHostSetOf(Kind kind) { + return map.get(kind); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public void forEach(BiConsumer action) { + map.forEach(action); + } + + public enum Kind { + MAP_0, + MAP_ENTRY_SET, MAP_ENTRY_ITR, MAP_ENTRY, + MAP_KEY_SET, MAP_KEY_ITR, + MAP_VALUES, MAP_VALUE_ITR, + + COL_0, + COL_ITR, + ALL + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java new file mode 100644 index 000000000..b2facb84a --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java @@ -0,0 +1,33 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; + +import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; +import pascal.taie.util.Copyable; + +import java.util.Iterator; +import java.util.Set; +import java.util.stream.Stream; + +public interface HostSet extends Iterable, Copyable { + + boolean addHost(Host host); + + boolean addAll(HostSet hostSet); + + HostSet addAllDiff(HostSet hostSet); + + boolean contains(Host host); + + boolean isEmpty(); + + int size(); + + Set getHosts(); + + Stream hosts(); + + default Iterator iterator() { + return getHosts().iterator(); + } + + void clear(); +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java new file mode 100644 index 000000000..74854097c --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java @@ -0,0 +1,22 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; + +import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; +import pascal.taie.util.Indexer; + +public class HostSetFactory { + private final Indexer hostIndexer; + + public HostSetFactory(Indexer hostIndexer) { + this.hostIndexer = hostIndexer; + } + + public HostSet make() { + return new HybridBitHostSet(hostIndexer); + } + + public HostSet make(Host host) { + HostSet set = make(); + set.addHost(host); + return set; + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java new file mode 100644 index 000000000..2f03ec063 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java @@ -0,0 +1,21 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; + +import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; +import pascal.taie.util.Indexer; +import pascal.taie.util.collection.HybridBitSet; +import pascal.taie.util.collection.SetEx; + +public class HybridBitHostSet extends DelegateHostSet { + public HybridBitHostSet(Indexer indexer) { + this(new HybridBitSet<>(indexer, true)); + } + + public HybridBitHostSet(SetEx set) { + super(set); + } + + @Override + protected HostSet newSet(SetEx set) { + return new HybridBitHostSet(set); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java new file mode 100644 index 000000000..0511b1206 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java @@ -0,0 +1,148 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pascal.taie.util.AnalysisException; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +public class MakeDefaultContainerConfig { + private static final Logger logger = LogManager.getLogger(MakeDefaultContainerConfig.class); + + private static void readFile(String fileName, String id, ContainerConfig config) { + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader("DefaultContainerConfig/" + fileName)); + String lineStr; + while ((lineStr = reader.readLine()) != null) { + if (lineStr.length() == 0) + continue; + switch (id) { + case "host" -> config.addHostClass(lineStr); + case "exclusion" -> { + int ind = lineStr.indexOf(' '); + if (ind != -1) { + String ks = lineStr.substring(0, ind), vs = lineStr.substring(ind + 1); + config.addKeySetClass(ks); + config.excludeClass(ks); + config.addValueSetClass(vs); + config.excludeClass(vs); + } + else + config.excludeClass(lineStr); + } + case "extender0" -> config.addCorrelationExtender(lineStr, -1, 0); + case "extender1" -> config.addCorrelationExtender(lineStr, -1, 1); // can be merged with the previous case + case "unrelatedInvoke" -> config.addUnrelatedInvoke(lineStr); + case "col-out" -> config.addCollectionValueOutMethod(lineStr); + case "map-val-out" -> config.addMapValueOutMethod(lineStr); + case "parameter" -> { + int space1 = lineStr.indexOf(' '), space2 = lineStr.lastIndexOf(' '); + String type = lineStr.substring(0, space1), + method = lineStr.substring(space1 + 1, space2), + sIndex = lineStr.substring(space2 + 1); + int index = Integer.parseInt(sIndex); + config.addInParameter(method, index, type); + } + case "paraWithConstraint" -> { + int space1 = lineStr.indexOf(' '), space3 = lineStr.lastIndexOf(' '), + space2 = lineStr.lastIndexOf('>') + 1; + String type = lineStr.substring(0, space1), + method = lineStr.substring(space1 + 1, space2), + sIndex = lineStr.substring(space2 + 1, space3), + cons = lineStr.substring(space3 + 1); + int index = Integer.parseInt(sIndex); + config.addInParameter(method, index, type, cons); + } + case "array-init" -> { + String[] info = lineStr.split("#"); + int index0 = Integer.parseInt(info[1]), index1 = Integer.parseInt(info[2]); + config.addArrayInitializer(info[0], index0, index1); + } + case "iter" -> config.addIteratorClass(lineStr); + case "entrySet" -> { + String[] info = lineStr.split("#"); + config.addAllocationSiteOfEntrySet(info[0], info[1]); + } + default -> throw new AnalysisException("No such id for assigned correlation!"); + } + } + reader.close(); + } catch (FileNotFoundException f) { + logger.error(f); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + } + + public static ContainerConfig make() { + ContainerConfig config = ContainerConfig.config; + // In a more expressive way, a table? + // For every object of an outer class type, a new determinant will be generated at its allocation Site. + readFile("hostClasses.txt", "host", config); + readFile("exclusion.txt", "exclusion", config); + readFile("CorrelationExtenders.txt", "extender0", config); + readFile("CorrelationExtenders1.txt", "extender1", config); + readFile("unrelatedInvokes.txt", "unrelatedInvoke", config); + + config.addCorrelationExtender("(javax.security.auth.Subject,int,java.util.Set)>", -1, 2); + // destination is the larger one, which means source is a subset of it. + + readFile("InMethod.txt", "parameter", config); + readFile("InWithCons.txt", "paraWithConstraint", config); + readFile("CollectionOut.txt", "col-out", config); + readFile("MapValueOut.txt", "map-val-out", config); + + readFile("iteratorClass.txt", "iter", config); + readFile("ArrayInitializer.txt", "array-init", config); + + readFile("EntrySet.txt", "entrySet", config); + + // TODO: some set methods are also get methods + config.addMapKeyExit( + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ); + config.computeTaintClass(); + return config; + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java new file mode 100644 index 000000000..6deb44bb3 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java @@ -0,0 +1,10 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container; + +import pascal.taie.language.classes.JMethod; + +public record Parameter(JMethod method, int index) { + @Override + public String toString() { + return "Parameter[" + index + "@" + method + "]"; + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java new file mode 100644 index 000000000..74db9084d --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java @@ -0,0 +1,36 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.field; + +import pascal.taie.ir.exp.FieldAccess; +import pascal.taie.ir.exp.InstanceFieldAccess; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.FieldStmt; +import pascal.taie.ir.stmt.StmtVisitor; + +public class AbstractLoadField extends FieldStmt { + private final boolean terminate; + + public AbstractLoadField(Var lvalue, FieldAccess rvalue, boolean terminate) { + super(lvalue, rvalue); + + if (rvalue instanceof InstanceFieldAccess) { + Var base = ((InstanceFieldAccess) rvalue).getBase(); + base.addAbstractLoadField(this); + } + + this.terminate = terminate; + } + + @Override + public FieldAccess getFieldAccess() { + return getRValue(); + } + + public boolean isNonRelay() { + return !terminate; + } + + @Override + public T accept(StmtVisitor visitor) { + return null; + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java new file mode 100644 index 000000000..9339f80e3 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java @@ -0,0 +1,27 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.field; + +import pascal.taie.ir.exp.FieldAccess; +import pascal.taie.ir.exp.InstanceFieldAccess; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.FieldStmt; +import pascal.taie.ir.stmt.StmtVisitor; + +public class AbstractStoreField extends FieldStmt { + public AbstractStoreField(FieldAccess lvalue, Var rvalue) { + super(lvalue, rvalue); + if (lvalue instanceof InstanceFieldAccess) { + Var base = ((InstanceFieldAccess) lvalue).getBase(); + base.addAbstractStoreField(this); + } + } + + @Override + public FieldAccess getFieldAccess() { + return getLValue(); + } + + @Override + public T accept(StmtVisitor visitor) { + return null; + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java new file mode 100644 index 000000000..5106a5878 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java @@ -0,0 +1,339 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.field; + +import pascal.taie.World; +import pascal.taie.analysis.graph.callgraph.CallGraph; +import pascal.taie.analysis.graph.callgraph.Edge; +import pascal.taie.analysis.graph.flowgraph.FlowKind; +import pascal.taie.analysis.pta.core.cs.context.Context; +import pascal.taie.analysis.pta.core.cs.element.*; +import pascal.taie.analysis.pta.core.solver.CutShortcutSolver; +import pascal.taie.analysis.pta.core.solver.PointerFlowEdge; +import pascal.taie.analysis.pta.core.solver.Solver; +import pascal.taie.analysis.pta.plugin.Plugin; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.ContainerAccessHandler; +import pascal.taie.analysis.pta.pts.PointsToSet; +import pascal.taie.ir.IR; +import pascal.taie.ir.exp.InstanceFieldAccess; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.proginfo.FieldRef; +import pascal.taie.ir.stmt.Invoke; +import pascal.taie.ir.stmt.LoadField; +import pascal.taie.ir.stmt.StoreField; +import pascal.taie.language.classes.JClass; +import pascal.taie.language.classes.JField; +import pascal.taie.language.classes.JMethod; +import pascal.taie.language.type.TypeSystem; +import pascal.taie.util.AnalysisException; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.MultiMap; +import pascal.taie.util.collection.Sets; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static pascal.taie.analysis.pta.core.solver.CutShortcutSolver.isConcerned; +import static pascal.taie.analysis.pta.plugin.cutshortcut.SpecialVariables.*; +import static pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex.THISINDEX; +import static pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex.getRealParameterIndex; + +public class FieldAccessHandler implements Plugin { + private final MultiMap setStatements = Maps.newMultiMap(); + + private final MultiMap getStatements = Maps.newMultiMap(); + + private final Set bannedReturnVars = Sets.newSet(); + + private CutShortcutSolver solver; + + private TypeSystem typeSystem; + + private CSManager csManager; + + private CallGraph callGraph; + + private Context emptyContext; + + @Override + public void setSolver(Solver solver) { + if (solver instanceof CutShortcutSolver cutShortcutSolver) { + this.solver = cutShortcutSolver; + typeSystem = World.get().getTypeSystem(); + callGraph = solver.getCallGraph(); + csManager = solver.getCSManager(); + emptyContext = solver.getContextSelector().getEmptyContext(); + } + else { + throw new AnalysisException("Invalid Solver to " + getClass()); + } + } + + public boolean addSetStatement(JMethod container, ParameterIndex baseIndex, FieldRef fieldRef, ParameterIndex rhsIndex) { + SetStatement setStatement = new SetStatement(baseIndex, fieldRef, rhsIndex); + if (setStatements.get(container).contains(setStatement)) + return false; + setStatements.put(container, setStatement); + return true; + } + + public Set getSetStatementsOf(JMethod method) { // 得到一个方法里所有的SetStatement + return setStatements.get(method); + } + + public Set getGetStatementsOf(JMethod method) { + return getStatements.get(method); + } + + public boolean addGetStatement(JMethod container, int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { + GetStatement getStatement = new GetStatement(lhsIndex, baseIndex, fieldRef); + if (getStatements.get(container).contains(getStatement)) + return false; + getStatements.put(container, getStatement); + return true; + } + + @Override + public void onNewPointsToSet(CSVar csVar, PointsToSet pts) { + processAbstractInstanceLoad(csVar, pts); + processAbstractInstanceStore(csVar, pts); + } + + private void processAbstractInstanceLoad(CSVar csVar, PointsToSet pts) { + Var base = csVar.getVar(); + base.getAbstractLoadFields().forEach(load -> { + Var lhs = load.getLValue(); + JField field = load.getFieldRef().resolve(); + if (isConcerned(lhs) && field != null) { + CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + pts.forEach(baseObj -> { + if (typeSystem.isSubtype(field.getDeclaringClass().getType(), baseObj.getObject().getType())) { + InstanceField instField = csManager.getInstanceField(baseObj, field); + solver.addPFGEdge(instField, csLHS, //lhs.getType(), + load.isNonRelay() ? FlowKind.NON_RELAY_GET : FlowKind.GET); + } + }); + } + }); + } + + private void processAbstractInstanceStore(CSVar csVar, PointsToSet pts) { + Var base = csVar.getVar(); + base.getAbstractStoreFields().forEach(store -> { + Var rhs = store.getRValue(); + JField field = store.getFieldRef().resolve(); + if (isConcerned(rhs) && field != null) { + CSVar csRHS = csManager.getCSVar(emptyContext, rhs); + pts.forEach(baseObj -> { + if (typeSystem.isSubtype(field.getDeclaringClass().getType(), baseObj.getObject().getType())) { + InstanceField instField = csManager.getInstanceField(baseObj, field); + solver.addPFGEdge(csRHS, instField, FlowKind.SET, field.getType()); + } + }); + } + }); + } + + @Override + public void onNewMethod(JMethod method) { + if (!method.isAbstract()) { + IR methodIR = method.getIR(); + methodIR.forEach(stmt -> { + if (stmt.getDef().isPresent() && stmt.getDef().get() instanceof Var def) + setDefined(def); + }); + if (methodIR.getThis() != null) + setParameterIndex(methodIR.getThis(), THISINDEX); + List params = methodIR.getParams(); + for (int i = 0; i < params.size(); i ++) + setParameterIndex(params.get(i), getRealParameterIndex(i)); + + JClass declaringClass = method.getDeclaringClass(); + method.getIR().forEach(stmt -> { + if (!declaringClass.getName().equals("java.awt.Component") + && !declaringClass.getName().equals("javax.swing.JComponent") + && stmt instanceof LoadField load + && load.getFieldAccess() instanceof InstanceFieldAccess fieldAccess) { + // x = y.f; + Var x = load.getLValue(), y = fieldAccess.getBase(); + if (isConcerned(x)) { + int retIndex = ParameterIndex.GetReturnVariableIndex(x); + ParameterIndex baseIndex = getParameterIndex(y); + if (retIndex >= 0 && baseIndex != null && !isDefined(y)) { + disableRelay(load); + addBannedReturnVar(method, x); + solver.addGetStmtEntry(method, retIndex, baseIndex, load.getFieldRef()); + } + } + } + else if (!callGraph.entryMethods().collect(Collectors.toSet()).contains(csManager.getCSMethod(emptyContext, method)) + && stmt instanceof StoreField store + && store.getFieldAccess() instanceof InstanceFieldAccess fieldAccess) { + // x.f = y; + Var x = fieldAccess.getBase(), y = store.getRValue(); + if (isConcerned(y)) { + ParameterIndex baseIndex = getParameterIndex(x), rhsIndex = getParameterIndex(y); + if (baseIndex != null && rhsIndex != null && !isDefined(x) && !isDefined(y)) { + solver.addIgnoredStoreField(store); + solver.addSetStmtEntry(method, store.getFieldRef(), baseIndex, rhsIndex); + } + } + } + }); + } + } + + public void onNewSetStatement(JMethod method, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) { + // when a new SetStatement is found + if (addSetStatement(method, baseIndex, fieldRef, rhsIndex)) { + callGraph.edgesInTo(csManager.getCSMethod(emptyContext, method)) + .forEach(edge -> { + processSetStatementOnCallEdge(edge, fieldRef, baseIndex, rhsIndex); + }); + } + } + + public void processSetStatementOnNewCallEdge(Edge edge) { + // when a new call edge found, process callSite with previous found SetStatement in the callee + JMethod callee = edge.getCallee().getMethod(); + getSetStatementsOf(callee) + .forEach(setStatement -> processSetStatementOnCallEdge(edge, setStatement.fieldRef(), + setStatement.baseIndex(), setStatement.rhsIndex())); + } + + private void processSetStatementOnCallEdge(Edge edge, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) { + Var base = ParameterIndex.getCorrespondingArgument(edge, baseIndex), // 得到SetStatement在callSite处的arg + rhs = ParameterIndex.getCorrespondingArgument(edge, rhsIndex); + if (base != null && rhs != null && isConcerned(rhs)) { + JMethod caller = base.getMethod(); + ParameterIndex baseIndexAtCaller = getParameterIndex(base), rhsIndexAtCaller = getParameterIndex(rhs); + if (baseIndexAtCaller != null && rhsIndexAtCaller != null && !isDefined(base) && !isDefined(rhs)) { + solver.addSelectedMethod(edge.getCallee().getMethod()); + solver.addSetStmtEntry(caller, fieldRef, baseIndexAtCaller, rhsIndexAtCaller); + } + else + processNewAbstractStoreField(base, fieldRef, rhs); + } + } + + private void processNewAbstractStoreField(Var base, FieldRef fieldRef, Var rhs) { + CSVar csBase = csManager.getCSVar(emptyContext, base), csRHS = csManager.getCSVar(emptyContext, rhs); + JField field = fieldRef.resolve(); + new AbstractStoreField(new InstanceFieldAccess(fieldRef, base), rhs); + solver.getPointsToSetOf(csBase).forEach(csObj -> { + if (typeSystem.isSubtype(field.getDeclaringClass().getType(), csObj.getObject().getType())) + solver.addPFGEdge(csRHS, csManager.getInstanceField(csObj, field), FlowKind.SET, field.getType()); + }); + } + + public void onNewGetStatement(JMethod method, Integer lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { + if (addGetStatement(method, lhsIndex, baseIndex, fieldRef)) { + // add deleted return vars (only do it when a new set statement is found) + for (Edge edge: callGraph.edgesInTo(csManager.getCSMethod(emptyContext, method)).toList()) { + processGetStatementOnCallEdge(edge, baseIndex, fieldRef); + } + } + } + + public void processGetStatementOnNewCallEdge(Edge edge) { + JMethod callee = edge.getCallee().getMethod(); + for (GetStatement get: getGetStatementsOf(callee)) + processGetStatementOnCallEdge(edge, get.baseIndex(), get.fieldRef()); + } + + /** + * @param baseIndex base of the Set Statement, it should be a parameter of the callee, and thus has a parameter index + * @param fieldRef fieldRef of the Set Statement + */ + private void processGetStatementOnCallEdge(Edge edge, ParameterIndex baseIndex, FieldRef fieldRef) { + Invoke callSite = edge.getCallSite().getCallSite(); + JMethod callee = edge.getCallee().getMethod(); + Var base = ParameterIndex.getCorrespondingArgument(edge, baseIndex), + lhs = callSite.getLValue(); + if (!ContainerAccessHandler.CutReturnEdge(callSite, callee)) { + if (base != null && lhs != null && isConcerned(lhs)) { + JMethod caller = base.getMethod(); + int lhsIndexAtCaller = ParameterIndex.GetReturnVariableIndex(lhs); + ParameterIndex baseIndexAtCaller = getParameterIndex(base); + solver.addSelectedMethod(edge.getCallee().getMethod()); + if (lhsIndexAtCaller != -1 && baseIndexAtCaller != null) { + addBannedReturnVar(caller, lhs); // 每一层GetStatement的retVar都需要删除 + solver.addGetStmtEntry(caller, lhsIndexAtCaller, baseIndexAtCaller, fieldRef); + processNewAbstractLoadField(lhs, base, fieldRef, false); + + } + else + processNewAbstractLoadField(lhs, base, fieldRef, true); + } + } + } + + private void processNewAbstractLoadField(Var lhs, Var base, FieldRef fieldRef, boolean terminate) { + CSVar csBase = csManager.getCSVar(emptyContext, base), csLHS = csManager.getCSVar(emptyContext, lhs); + JField field = fieldRef.resolve(); + new AbstractLoadField(lhs, new InstanceFieldAccess(fieldRef, base), terminate); + solver.getPointsToSetOf(csBase).forEach(csObj -> { + if (typeSystem.isSubtype(field.getDeclaringClass().getType(), csObj.getObject().getType())) + solver.addPFGEdge(csManager.getInstanceField(csObj, field), csLHS, //lhs.getType(), + terminate ? FlowKind.GET : FlowKind.NON_RELAY_GET); + }); + } + + @Override + public void onNewCallEdge(Edge edge) { + Invoke callSite = edge.getCallSite().getCallSite(); + processSetStatementOnNewCallEdge(edge); + Var lhs = callSite.getLValue(); + if (lhs != null && isConcerned(lhs)) { + processGetStatementOnNewCallEdge(edge); + CSMethod csCallee = edge.getCallee(); + JMethod callee = csCallee.getMethod(); + CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + if (!ContainerAccessHandler.CutReturnEdge(callSite, callee)) { + for (Var ret: callee.getIR().getReturnVars()) { + if (bannedReturnVars.contains(ret)) { + CSVar csRet = csManager.getCSVar(emptyContext, ret); + csRet.getInEdges().forEach(inEdge -> { + // transfer + if (inEdge.kind() != FlowKind.NON_RELAY_GET) + solver.addPFGEdge(inEdge.source(), csLHS, inEdge.kind(), inEdge.getTransfers()); + }); + } + } + } + } + } + + @Override + public void onNewPFGEdge(PointerFlowEdge edge) { + Pointer source = edge.source(), target = edge.target(); + FlowKind kind = edge.kind(); + if (target instanceof CSVar csVar && bannedReturnVars.contains(csVar.getVar()) && kind != FlowKind.NON_RELAY_GET) { + CSMethod csMethod = csManager.getCSMethod(emptyContext, csVar.getVar().getMethod()); + callGraph.getCallersOf(csMethod).forEach(csCallSite -> { + Var lhs = csCallSite.getCallSite().getLValue(); + if (lhs != null && isConcerned(lhs)) { + CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + solver.addPFGEdge(source, csLHS, kind, edge.getTransfers()); + } + }); + } + } + + private void addBannedReturnVar(JMethod method, Var ret) { + bannedReturnVars.add(ret); + CSVar csRet = csManager.getCSVar(emptyContext, ret); + csRet.getInEdges().forEach(edge -> { + if (edge.kind() != FlowKind.NON_RELAY_GET) { + callGraph.getCallersOf(csManager.getCSMethod(emptyContext, method)) + .forEach(csCallSite -> { + Var lhs = csCallSite.getCallSite().getLValue(); + if (lhs != null && isConcerned(lhs)) { + CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + solver.addPFGEdge(edge.source(), csLHS, edge.kind(), edge.getTransfers()); + } + }); + } + }); + solver.addSpecialHandledRetVar(ret); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/GetStatement.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/GetStatement.java new file mode 100644 index 000000000..210ea3181 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/GetStatement.java @@ -0,0 +1,10 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.field; + +import pascal.taie.ir.proginfo.FieldRef; + +public record GetStatement(int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { + @Override + public String toString() { + return "[GetStmt]" + lhsIndex + "=" + baseIndex().toString() + "." + fieldRef.getName(); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java new file mode 100644 index 000000000..d4312a913 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java @@ -0,0 +1,69 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.field; + +import pascal.taie.analysis.graph.callgraph.Edge; +import pascal.taie.analysis.pta.core.cs.element.CSCallSite; +import pascal.taie.analysis.pta.core.cs.element.CSMethod; +import pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty; +import pascal.taie.analysis.pta.plugin.reflection.ReflectiveCallEdge; +import pascal.taie.ir.IR; +import pascal.taie.ir.exp.InvokeExp; +import pascal.taie.ir.exp.InvokeInstanceExp; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.Invoke; +import pascal.taie.util.AnalysisException; +import pascal.taie.util.collection.Maps; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +public record ParameterIndex(boolean isThis, int index) { + public static ParameterIndex THISINDEX = new ParameterIndex(true, 0); + + public static Map realParameters = Maps.newMap(); + + @Override + public String toString() { + return isThis ? "%this" : index + "@parameter"; + } + + public static ParameterIndex getRealParameterIndex(int index) { + return realParameters.computeIfAbsent(index, i -> new ParameterIndex(false, index)); + } + + public static int GetReturnVariableIndex(Var var) { // -1: 不是return Variable, 0, ... : index + // TODO: check definitions of return variables + if (var.getMethod().isAbstract()) + return -1; + IR methodIR = var.getMethod().getIR(); + List returnVars = methodIR.getReturnVars(); + int len = returnVars.size(), i; + for (i = 0; i < len; i ++) + if (returnVars.get(i).equals(var)) + return i; + return -1; + } + + @Nullable + public static Var getCorrespondingArgument(Edge edge, ParameterIndex parameterIndex) { + Invoke invoke = edge.getCallSite().getCallSite(); + InvokeExp invokeExp = invoke.getInvokeExp(); + if (edge instanceof ReflectiveCallEdge reflEdge) { + if (parameterIndex.isThis()) { + switch (ReflectiveEdgeProperty.getReflectiveKind(reflEdge)) { + case NEW_INSTANCE -> { return invoke.getResult(); } + case METHOD_INVOKE -> { return invokeExp.getArg(0); } + default -> throw new AnalysisException("Invalid Reflective Call Edge"); + } + } + else + return ReflectiveEdgeProperty.getVirtualArg(reflEdge); + } + if (!parameterIndex.isThis()) + return invokeExp.getArg(parameterIndex.index()); + else if (invokeExp instanceof InvokeInstanceExp instanceExp) + return instanceExp.getBase(); + + return null; + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java new file mode 100644 index 000000000..27dc7b941 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java @@ -0,0 +1,23 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.field; + +import pascal.taie.ir.proginfo.FieldRef; + +/** + * {@link SetStatement}表示需要向前去寻找callSite,并把这种抽象的Set行为作用到callSite处的指针(参数)的语句 + * 和之前的SetStmt不同,现在的{@link SetStatement}并不一定是简单的{@link pascal.taie.ir.stmt.StoreField}语句 + * 举个例子,如果Set方法内包含两层调用callSite(outer) -> set (包含callSite-inner) -> doSet方法 + * 那么在doSet方法内部存在一个{@link SetStatement},即为简单的{@link pascal.taie.ir.stmt.StoreField}语句 + * 但是当我们从这个{@link SetStatement}出发,找到了set方法中的callSite-inner后,我们发现这个{@link SetStatement}抽象到callSite-inner后的base和rhs + * 仍然是**set**方法中的parameter,于是callSite-inner也被抽象成一个{@link SetStatement},继续类似的操作(寻找callSite), + * 直到找到一个callSite,使得对应的base和rhs不再是方法的参数,此时建立**abstractStoreField,而不是{@link SetStatement} + * 具体的逻辑在solver中体现 + * {@param baseIndex} 表示SetStatement的base变量对应的parameter index(因为他一定是方法的参数) + * {@param fieldRef} 该SetStatement对应的field + * {@param rhsIndex} 与baseIndex类似 + */ +public record SetStatement(ParameterIndex baseIndex, FieldRef fieldRef, ParameterIndex rhsIndex) { + @Override + public String toString() { + return "[SetStmt]" + baseIndex().toString() + "." + fieldRef.getName() + " = " + rhsIndex.toString(); + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java new file mode 100644 index 000000000..846e288b6 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java @@ -0,0 +1,206 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.localflow; + +import pascal.taie.analysis.graph.callgraph.CallGraphs; +import pascal.taie.analysis.graph.callgraph.Edge; +import pascal.taie.analysis.graph.flowgraph.FlowKind; +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.heap.HeapModel; +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.core.solver.CutShortcutSolver; +import pascal.taie.analysis.pta.core.solver.PointerFlowEdge; +import pascal.taie.analysis.pta.core.solver.Solver; +import pascal.taie.analysis.pta.plugin.Plugin; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; +import pascal.taie.analysis.pta.pts.PointsToSet; +import pascal.taie.ir.exp.InvokeExp; +import pascal.taie.ir.exp.InvokeInstanceExp; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.*; +import pascal.taie.language.classes.JMethod; +import pascal.taie.util.AnalysisException; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.MultiMap; + +import java.util.List; + +import static pascal.taie.analysis.pta.core.solver.CutShortcutSolver.isConcerned; +import static pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex.*; +import static pascal.taie.analysis.pta.plugin.cutshortcut.localflow.ParameterIndexOrNewObj.INDEX_THIS; + +public class LocalFlowHandler implements Plugin { + private CutShortcutSolver solver; + + private CSManager csManager; + + private Context emptyContext; + + private HeapModel heapModel; + + private int totIDMethod = 0; + + private final MultiMap directlyReturnParams = Maps.newMultiMap(); + + public void setSolver(Solver solver) { + if (solver instanceof CutShortcutSolver cutShortcutSolver) { + this.solver = cutShortcutSolver; + csManager = solver.getCSManager(); + emptyContext = solver.getContextSelector().getEmptyContext(); + heapModel = solver.getHeapModel(); + } + else + throw new AnalysisException("Invalid solver"); + } + + @Override + public void onNewPointsToSet(CSVar csVar, PointsToSet pts) { + Var base = csVar.getVar(); + for (Invoke callSite : base.getInvokes()) { + Var lhs = callSite.getLValue(); + if (lhs != null && isConcerned(lhs)) { + CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + pts.forEach(recvObj -> { + JMethod callee = CallGraphs.resolveCallee( + recvObj.getObject().getType(), callSite); + if (callee != null && directlyReturnParams.get(callee).contains(INDEX_THIS)) { + solver.addPointsTo(csLHS, recvObj); + } + }); + } + } + } + + @Override + public void onNewMethod(JMethod method) { + if (!method.isAbstract()) { + List retVars = method.getIR().getReturnVars(); + MultiMap result = getVariablesAssignedFromParameters(method); + for (Var ret: retVars) { + if (!result.get(ret).isEmpty()) { + totIDMethod ++; + break; + } + } + result.forEach((var, index) -> { + if (retVars.contains(var)) { + solver.addSelectedMethod(method); + directlyReturnParams.put(method, index); + solver.addSpecialHandledRetVar(var); + } + }); + } + } + + private MultiMap getVariablesAssignedFromParameters(JMethod method) { + MultiMap result = Maps.newMultiMap(); + MultiMap definitions = Maps.newMultiMap(); + method.getIR().forEach(stmt -> { + stmt.getDef().ifPresent(def -> { + if (def instanceof Var varDef) { + definitions.put(varDef, stmt); + } + }); + }); + for (int i = 0; i < method.getParamCount(); i++) { + Var param = method.getIR().getParam(i); + if (isConcerned(param)) { // parameter which is not redefined in the method body + if (definitions.get(param).isEmpty() || definitions.get(param).stream().allMatch(stmt -> stmt instanceof New)) { + result.put(param, new ParameterIndexOrNewObj(false, getRealParameterIndex(i), null)); + definitions.get(param).forEach(stmt -> { + result.put(param, new ParameterIndexOrNewObj(true, null, heapModel.getObj((New) stmt))); + }); + } + } + } + Var thisVar = method.getIR().getThis(); + if (thisVar != null) + result.put(thisVar, INDEX_THIS); + + boolean changed = true; + int size = result.size(); + while (size > 0 && changed) { + method.getIR().getVars().forEach(var -> { + boolean flag = true; + for (Stmt def: definitions.get(var)) { + if (def instanceof Copy copy) { + Var rhs = copy.getRValue(); + if (result.get(rhs).isEmpty()) { + flag = false; + break; + } + } + else if (def instanceof Cast cast) { + Var rhs = cast.getRValue().getValue(); + if (result.get(rhs).isEmpty()) { + flag = false; + break; + } + } + else if (!(def instanceof New)) { + flag = false; + break; + } + } + if (flag) { + for (Stmt def: definitions.get(var)) { + Var rhs; + if (def instanceof Copy copy) + rhs = copy.getRValue(); + else if (def instanceof Cast cast) + rhs = cast.getRValue().getValue(); + else if (def instanceof New stmt) { + Obj obj = heapModel.getObj(stmt); + result.put(var, new ParameterIndexOrNewObj(true, null, obj)); + continue; + } + else + throw new AnalysisException("Neither Copy not Cast: " + def + "!"); + result.get(rhs).forEach(index -> result.put(var, index)); + } + } + }); + changed = result.size() != size; + size = result.size(); + } + return result; + } + + @Override + public void onNewCallEdge(Edge edge) { + CSMethod csCallee = edge.getCallee(); + JMethod callee = csCallee.getMethod(); + CSCallSite csCallSite = edge.getCallSite(); + Invoke callSite = csCallSite.getCallSite(); + InvokeExp invokeExp = callSite.getInvokeExp(); + Var lhs = callSite.getLValue(); + if (lhs != null && isConcerned(lhs)) { + CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + directlyReturnParams.get(callee).forEach(indexOrObj -> { + if (indexOrObj.isObj()) + solver.addPointsTo(csLHS, csManager.getCSObj(emptyContext, indexOrObj.obj())); + else { + ParameterIndex index = indexOrObj.index(); + if (index == THISINDEX && invokeExp instanceof InvokeInstanceExp instanceExp) { + CSVar csBase = csManager.getCSVar(emptyContext, instanceExp.getBase()); + solver.getPointsToSetOf(csBase).forEach(csObj -> { + JMethod realCallee = CallGraphs.resolveCallee(csObj.getObject().getType(), callSite); + if (callee.equals(realCallee)) + solver.addPointsTo(csLHS, csObj); + }); + } + if (index != THISINDEX) { + assert index != null; + Var arg = getCorrespondingArgument(edge, index); + if (arg != null && isConcerned(arg)) + solver.addPFGEdge(new PointerFlowEdge(FlowKind.ID, csManager.getCSVar(emptyContext, arg), csLHS), + lhs.getType()); + } + } + + }); + } + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/ParameterIndexOrNewObj.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/ParameterIndexOrNewObj.java new file mode 100644 index 000000000..99f7632cc --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/ParameterIndexOrNewObj.java @@ -0,0 +1,16 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.localflow; + +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; + +import static pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex.THISINDEX; + +public record ParameterIndexOrNewObj(boolean isObj, ParameterIndex index, Obj obj) { + public static ParameterIndexOrNewObj INDEX_THIS = new ParameterIndexOrNewObj(false, THISINDEX, null); + + @Override + public String toString() { + return isObj ? obj.toString() : index.toString(); + } +} + diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java index da83b6371..b82067664 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java @@ -36,6 +36,7 @@ import pascal.taie.analysis.pta.core.heap.Obj; import pascal.taie.analysis.pta.core.solver.PointerFlowEdge; import pascal.taie.analysis.pta.core.solver.Solver; +import pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty; import pascal.taie.analysis.pta.plugin.util.AnalysisModelPlugin; import pascal.taie.analysis.pta.plugin.util.CSObjs; import pascal.taie.analysis.pta.plugin.util.InvokeHandler; @@ -61,6 +62,9 @@ import static pascal.taie.analysis.graph.flowgraph.FlowKind.PARAMETER_PASSING; import static pascal.taie.analysis.graph.flowgraph.FlowKind.RETURN; import static pascal.taie.analysis.graph.flowgraph.FlowKind.STATIC_STORE; +import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.ReflectiveCallKind.METHOD_INVOKE; +import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.ReflectiveCallKind.NEW_INSTANCE; +import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.setgetReflectiveKind; import static pascal.taie.analysis.pta.plugin.util.InvokeUtils.BASE; /** @@ -112,6 +116,7 @@ public class ReflectiveActionModel extends AnalysisModelPlugin { this.invokesWithLog = invokesWithLog; } + // reflection type: r = c.newInstance(args) @InvokeHandler(signature = "", argIndexes = {BASE}) public void classNewInstance(Context context, Invoke invoke, PointsToSet classes) { classes.forEach(obj -> { @@ -124,12 +129,13 @@ public void classNewInstance(Context context, Invoke invoke, PointsToSet classes if (init != null && !typeMatcher.isUnmatched(invoke, init)) { ClassType type = clazz.getType(); CSObj csNewObj = newReflectiveObj(context, invoke, type); - addReflectiveCallEdge(context, invoke, csNewObj, init, null); + addReflectiveCallEdge(context, invoke, csNewObj, init, null, NEW_INSTANCE); } } }); } + // reflection type: r = c.newInstance(args) @InvokeHandler(signature = "", argIndexes = {BASE}) public void constructorNewInstance(Context context, Invoke invoke, PointsToSet constructors) { constructors.forEach(obj -> { @@ -141,7 +147,7 @@ public void constructorNewInstance(Context context, Invoke invoke, PointsToSet c ClassType type = constructor.getDeclaringClass().getType(); CSObj csNewObj = newReflectiveObj(context, invoke, type); addReflectiveCallEdge(context, invoke, csNewObj, - constructor, invoke.getInvokeExp().getArg(0)); + constructor, invoke.getInvokeExp().getArg(0), NEW_INSTANCE); } }); } @@ -158,6 +164,7 @@ private CSObj newReflectiveObj(Context context, Invoke invoke, ReferenceType typ return csNewObj; } + // reflection type: r = m.invoke(obj, args) @InvokeHandler(signature = "", argIndexes = {BASE, 0}) public void methodInvoke(Context context, Invoke invoke, PointsToSet mtdObjs, PointsToSet recvObjs) { @@ -168,12 +175,11 @@ public void methodInvoke(Context context, Invoke invoke, } JMethod target = CSObjs.toMethod(mtdObj); if (target != null && !typeMatcher.isUnmatched(invoke, target)) { - if (target.isStatic()) { - addReflectiveCallEdge(context, invoke, null, target, argsVar); - } else { + if (target.isStatic()) + addReflectiveCallEdge(context, invoke, null, target, argsVar, METHOD_INVOKE); + else recvObjs.forEach(recvObj -> - addReflectiveCallEdge(context, invoke, recvObj, target, argsVar)); - } + addReflectiveCallEdge(context, invoke, recvObj, target, argsVar, METHOD_INVOKE)); } }); } @@ -272,7 +278,7 @@ private boolean isInvalidTarget(Invoke invoke, CSObj metaObj) { private void addReflectiveCallEdge( Context callerCtx, Invoke callSite, - @Nullable CSObj recvObj, JMethod callee, Var args) { + @Nullable CSObj recvObj, JMethod callee, Var args, ReflectiveEdgeProperty.ReflectiveCallKind kind) { if (!callee.isConstructor() && !callee.isStatic()) { // dispatch for instance method (except constructor) assert recvObj != null : "recvObj is required for instance method"; @@ -284,15 +290,16 @@ private void addReflectiveCallEdge( } CSCallSite csCallSite = csManager.getCSCallSite(callerCtx, callSite); Context calleeCtx; - if (callee.isStatic()) { + if (callee.isStatic()) calleeCtx = selector.selectContext(csCallSite, callee); - } else { + else { calleeCtx = selector.selectContext(csCallSite, recvObj, callee); // pass receiver object to 'this' variable of callee solver.addVarPointsTo(calleeCtx, callee.getIR().getThis(), recvObj); } ReflectiveCallEdge callEdge = new ReflectiveCallEdge(csCallSite, csManager.getCSMethod(calleeCtx, callee), args); + setgetReflectiveKind(callEdge, kind); solver.addCallEdge(callEdge); allTargets.put(callSite, callee); // record target } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveCallEdge.java b/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveCallEdge.java index 423751912..c95994058 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveCallEdge.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveCallEdge.java @@ -32,8 +32,7 @@ /** * Represents reflective call edges. */ -class ReflectiveCallEdge extends OtherEdge { - +public class ReflectiveCallEdge extends OtherEdge { /** * Variable pointing to the array argument of reflective call, * which contains the arguments for the reflective target method, i.e., @@ -49,7 +48,7 @@ class ReflectiveCallEdge extends OtherEdge { } @Nullable - Var getArgs() { + public Var getArgs() { return args; } } diff --git a/src/main/java/pascal/taie/ir/exp/Var.java b/src/main/java/pascal/taie/ir/exp/Var.java index 403ce2f53..a5beea9ae 100644 --- a/src/main/java/pascal/taie/ir/exp/Var.java +++ b/src/main/java/pascal/taie/ir/exp/Var.java @@ -22,6 +22,8 @@ package pascal.taie.ir.exp; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.AbstractLoadField; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.AbstractStoreField; import pascal.taie.ir.stmt.Invoke; import pascal.taie.ir.stmt.LoadArray; import pascal.taie.ir.stmt.LoadField; @@ -216,6 +218,21 @@ public List getInvokes() { return relevantStmts.getInvokes(); } + // Support Cut-Shortcut field access pattern analysis + public void addAbstractLoadField(AbstractLoadField abstractLoadField) { + ensureRelevantStmts(); + relevantStmts.addAbstractLoadField(abstractLoadField); + } + + public List getAbstractLoadFields() { return relevantStmts.getAbstractLoadFields(); } + + public void addAbstractStoreField(AbstractStoreField abstractStoreField) { + ensureRelevantStmts(); + relevantStmts.addAbstractStoreField(abstractStoreField); + } + + public List getAbstractStoreFields() { return relevantStmts.getAbstractStoreFields(); } + /** * Ensure {@link #relevantStmts} points to an instance other than * {@link RelevantStmts#EMPTY}. @@ -253,6 +270,8 @@ private void readObject(ObjectInputStream s) throws IOException, * load array: x = v[i]; * store array: v[i] = x; * invocation: v.f(); + * abstract load field: x = absv.f; (for Cut-Shortcut analysis) + * abstract store field: absv.f = x; (for Cut-Shortcut analysis) * We use a separate class to store these relevant statements * (instead of directly storing them in {@link Var}) for saving space. * Most variables do not have any relevant statements, so these variables @@ -273,6 +292,10 @@ private static class RelevantStmts implements Serializable { private List storeArrays = List.of(); private List invokes = List.of(); + // Support Cut-Shortcut field access pattern analysis + private List abstractLoadFields = List.of(); + private List abstractStoreFields = List.of(); + private List getLoadFields() { return unmodifiable(loadFields); } @@ -328,6 +351,25 @@ private void addInvoke(Invoke invoke) { invokes.add(invoke); } + // Support Cut-Shortcut field access pattern analysis + private List getAbstractStoreFields() { return unmodifiable(abstractStoreFields); } + + private void addAbstractStoreField(AbstractStoreField abstractStoreField) { + if (abstractStoreFields.isEmpty()) { + abstractStoreFields = new ArrayList<>(); + } + abstractStoreFields.add(abstractStoreField); + } + + private List getAbstractLoadFields() { return unmodifiable(abstractLoadFields); } + + private void addAbstractLoadField(AbstractLoadField abstractLoadField) { + if (abstractLoadFields.isEmpty()) { + abstractLoadFields = new ArrayList<>(); + } + abstractLoadFields.add(abstractLoadField); + } + private static List unmodifiable(List list) { return list.isEmpty() ? list : Collections.unmodifiableList(list); } diff --git a/src/main/java/pascal/taie/ir/stmt/FieldStmt.java b/src/main/java/pascal/taie/ir/stmt/FieldStmt.java index 260054c43..0f7b5aa6a 100644 --- a/src/main/java/pascal/taie/ir/stmt/FieldStmt.java +++ b/src/main/java/pascal/taie/ir/stmt/FieldStmt.java @@ -32,7 +32,7 @@ */ public abstract class FieldStmt extends AssignStmt { - FieldStmt(L lvalue, R rvalue) { + protected FieldStmt(L lvalue, R rvalue) { super(lvalue, rvalue); } From dc4371c2d2d98bf0f70dd81884fc895d80cbfae1 Mon Sep 17 00:00:00 2001 From: prophe <1172782111@qq.com> Date: Mon, 16 Feb 2026 18:36:34 +0800 Subject: [PATCH 2/2] Support Cut-Shortcut in context-sensitive setting. Issues: - 1.`Map.entrySet().iterator().next()` shoule be deemed as [Transfer] instead of [Exit]. However, this is not easy to recogize in ContainerAccessHandler.CutReturnEdge. Making point-to set of Map.entrySet().iterator().next() call receiver to be empty. Currently we use a stupid method that collect variables to be `Map$Entry` type and recongize which `Iterator.next()` callsite is `Map$Entry` to filter. - 2.PFG edge from return value of `Map.keySet()/values()` to their callsite receivers is marked with `FlowKind.LOCAL_ASSIGN` instead of `FlowKind.RETURN`. Making ptsH of `Map.keySet()/values()` not optimized. - 3.`Array-Initializer` such as `Collections` and `Arrays%ArrayList ` making objects point-to by array parameters cross-reference and affect the precision of ptsH of `Collection` variables. --- docs/en/pointer-analysis-framework.adoc | 4 + .../pascal/taie/AbstractWorldBuilder.java | 6 +- .../analysis/graph/flowgraph/FlowKind.java | 12 +- .../taie/analysis/pta/PointerAnalysis.java | 2 +- .../pta/core/cs/element/CSManager.java | 7 +- .../pta/core/cs/element/HostPointer.java | 30 - .../core/cs/element/MapBasedCSManager.java | 11 - .../pta/core/solver/CutShortcutSolver.java | 149 +-- .../pta/core/solver/DefaultSolver.java | 5 +- .../analysis/pta/core/solver/WorkList.java | 35 +- .../analysis/pta/plugin/ResultProcessor.java | 14 +- .../cutshortcut/ReflectiveEdgeProperty.java | 6 +- .../plugin/cutshortcut/SpecialVariables.java | 10 +- .../container/ClassAndTypeClassifier.java | 66 +- .../container/ContainerAccessHandler.java | 1026 ++++++++--------- .../container/ContainerConfig.java | 461 ++------ .../plugin/cutshortcut/container/Host.java | 84 -- .../cutshortcut/container/HostManager.java | 55 + .../container/HostMap/DelegateHostSet.java | 66 -- .../container/HostMap/HostList.java | 61 - .../container/HostMap/HostSet.java | 33 - .../container/HostMap/HostSetFactory.java | 22 - .../container/HostMap/HybridBitHostSet.java | 21 - .../container/MakeDefaultContainerConfig.java | 212 ++-- .../cutshortcut/container/Parameter.java | 10 - .../container/enums/ContExitCategory.java | 27 + .../container/enums/ContainerType.java | 31 + .../container/enums/ExtendType.java | 24 + .../cutshortcut/container/enums/HostKind.java | 26 + .../container/enums/IterExitCategory.java | 27 + .../cutshortcut/field/AbstractLoadField.java | 6 - .../cutshortcut/field/AbstractStoreField.java | 4 - .../cutshortcut/field/FieldAccessHandler.java | 397 ++++--- .../cutshortcut/field/ParameterIndex.java | 1 + .../cutshortcut/field/SetStatement.java | 2 + .../localflow/LocalFlowHandler.java | 71 +- .../reflection/ReflectiveActionModel.java | 4 +- src/main/java/pascal/taie/config/Options.java | 8 + src/main/java/pascal/taie/ir/exp/Var.java | 38 - .../pascal/taie/util/collection/Triplet.java | 11 + .../container-config/array-initializer.yml | 4 + .../container-config/entrance-append.yml | 159 +++ .../container-config/entrance-extend.yml | 133 +++ src/main/resources/container-config/exit.yml | 342 ++++++ .../container-config/host-classes.yml | 121 ++ src/main/resources/tai-e-analyses.yml | 2 + 46 files changed, 2063 insertions(+), 1783 deletions(-) delete mode 100644 src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostManager.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContExitCategory.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContainerType.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ExtendType.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/HostKind.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/IterExitCategory.java create mode 100644 src/main/java/pascal/taie/util/collection/Triplet.java create mode 100644 src/main/resources/container-config/array-initializer.yml create mode 100644 src/main/resources/container-config/entrance-append.yml create mode 100644 src/main/resources/container-config/entrance-extend.yml create mode 100644 src/main/resources/container-config/exit.yml create mode 100644 src/main/resources/container-config/host-classes.yml diff --git a/docs/en/pointer-analysis-framework.adoc b/docs/en/pointer-analysis-framework.adoc index d39418e7b..156764078 100644 --- a/docs/en/pointer-analysis-framework.adoc +++ b/docs/en/pointer-analysis-framework.adoc @@ -87,6 +87,10 @@ See <> for more details. ** Default value: `false` ** Specify whether to dump points-to results in YAML format. This option is independent of `dump` and `dump-ci` output formats. +* Only dump points-to results of application code: `only-dump-app:[true|false]` +** Default value: `false` +** When set to `true`, `dump-yaml`, `dump-ci` will only dump analysis results of application code (and ignores library code). + * Time limit: `time-limit:` ** Default value: `-1` ** Specify a time limit for pointer analysis (unit: second).When it is `-1`, there is no time limit. diff --git a/src/main/java/pascal/taie/AbstractWorldBuilder.java b/src/main/java/pascal/taie/AbstractWorldBuilder.java index bb9b477c1..56925866c 100644 --- a/src/main/java/pascal/taie/AbstractWorldBuilder.java +++ b/src/main/java/pascal/taie/AbstractWorldBuilder.java @@ -49,8 +49,6 @@ public abstract class AbstractWorldBuilder implements WorldBuilder { private static final Logger logger = LogManager.getLogger(AbstractWorldBuilder.class); - protected static final String JREs = "java-benchmarks/JREs"; - protected static final List implicitEntries = List.of( "", "(java.lang.ThreadGroup,java.lang.Runnable)>", @@ -75,7 +73,7 @@ protected static String getClassPath(Options options) { .collect(Collectors.joining(File.pathSeparator)); } else { // when prependJVM is not set, we manually specify JRE jars // check existence of JREs - File jreDir = new File(JREs); + File jreDir = new File(options.getLibJREPath()); if (!jreDir.exists()) { throw new RuntimeException(""" Failed to locate Java library. @@ -85,7 +83,7 @@ protected static String getClassPath(Options options) { then put it in Tai-e's working directory."""); } String jrePath = String.format("%s/jre1.%d", - JREs, options.getJavaVersion()); + options.getLibJREPath(), options.getJavaVersion()); try (Stream paths = Files.walk(Path.of(jrePath))) { return Streams.concat( paths.map(Path::toString).filter(p -> p.endsWith(".jar")), diff --git a/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java b/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java index 47a450de3..6d976b7fd 100644 --- a/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java +++ b/src/main/java/pascal/taie/analysis/graph/flowgraph/FlowKind.java @@ -39,11 +39,17 @@ public enum FlowKind { PARAMETER_PASSING, RETURN, - // local flow in cut-shortcut - ARG_TO_HOST, HOST_TO_RESULT, SUBSET, CORRELATION, ARRAYCOPY, ID, + // cut-shortcut + ARG_TO_HOST, // container entrance method, for example, v.add(e), o \in pts(v), e -> host(o) + HOST_TO_RESULT, + SUBSET, + CORRELATION, + ARRAYCOPY, + ID, VIRTUAL_ARRAY, VIRTUAL_ARG, - SET, GET, NON_RELAY_GET, + SET, GET, + NON_RELAY_GET, // relay edge OTHER, } diff --git a/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java b/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java index 4cd7e173d..e47b3576c 100644 --- a/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java +++ b/src/main/java/pascal/taie/analysis/pta/PointerAnalysis.java @@ -116,7 +116,7 @@ private PointerAnalysisResult runAnalysis(HeapModel heapModel, ContextSelector selector, String solverType) { AnalysisOptions options = getOptions(); - Solver solver = null; + Solver solver; if (solverType.equals("default")) solver = new DefaultSolver(options, heapModel, selector, new MapBasedCSManager()); // cut-shortcut diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java index 9392387ef..9c1e055d4 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java +++ b/src/main/java/pascal/taie/analysis/pta/core/cs/element/CSManager.java @@ -24,7 +24,7 @@ import pascal.taie.analysis.pta.core.cs.context.Context; import pascal.taie.analysis.pta.core.heap.Obj; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; + import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.Invoke; import pascal.taie.language.classes.JField; @@ -125,9 +125,4 @@ public interface CSManager { * The indexer is useful for creating efficient points-to sets. */ Indexer getObjectIndexer(); - - /** - * @return the HostPointer for given host and category used in Cut-Shortcut. - */ - HostPointer getHostPointer(Host host, String category); } diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java deleted file mode 100644 index e09be0655..000000000 --- a/src/main/java/pascal/taie/analysis/pta/core/cs/element/HostPointer.java +++ /dev/null @@ -1,30 +0,0 @@ -package pascal.taie.analysis.pta.core.cs.element; - -import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; -import pascal.taie.language.type.Type; - -public class HostPointer extends AbstractPointer { - private final Host host; - - private final String category; - - public HostPointer(Host host, String category, int index) { - super(index); - this.host = host; - this.category = category; - } - - public Host getHost() { - return host; - } - - @Override - public Type getType() { - return host.getType(); - } - - @Override - public String toString() { - return host.toString(); - } -} diff --git a/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java b/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java index 6de293261..b8e304fa7 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java +++ b/src/main/java/pascal/taie/analysis/pta/core/cs/element/MapBasedCSManager.java @@ -25,7 +25,6 @@ import pascal.taie.World; import pascal.taie.analysis.pta.core.cs.context.Context; import pascal.taie.analysis.pta.core.heap.Obj; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.Invoke; import pascal.taie.language.classes.ClassNames; @@ -94,9 +93,6 @@ public Collection getCSVarsOf(Var var) { return ptrManager.getCSVarsOf(var); } - @Override - public HostPointer getHostPointer(Host host, String category) { return ptrManager.getHostPointer(host, category); } - @Override public Collection getStaticFields() { return ptrManager.getStaticFields(); @@ -160,8 +156,6 @@ private static class PointerManager { private final Map arrayIndexes = Maps.newMap(); - private final TwoKeyMap hostPointers = Maps.newTwoKeyMap(); - /** * Counter for assigning unique indexes to Pointers. */ @@ -187,11 +181,6 @@ private ArrayIndex getArrayIndex(CSObj array) { a -> new ArrayIndex(a, counter++)); } - private HostPointer getHostPointer(Host host, String category) { - return hostPointers.computeIfAbsent(host, category, - (h, c) -> new HostPointer(h, c, counter++)); - } - private Collection getVars() { return vars.keySet(); } diff --git a/src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java b/src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java index 27ac8419a..366b52321 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java +++ b/src/main/java/pascal/taie/analysis/pta/core/solver/CutShortcutSolver.java @@ -11,18 +11,14 @@ import pascal.taie.analysis.pta.plugin.Plugin; import pascal.taie.analysis.pta.plugin.cutshortcut.container.ContainerAccessHandler; import pascal.taie.analysis.pta.plugin.cutshortcut.container.ContainerConfig; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSet; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSetFactory; -import pascal.taie.analysis.pta.plugin.cutshortcut.field.FieldAccessHandler; -import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.HostKind; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.*; import pascal.taie.analysis.pta.plugin.reflection.ReflectiveCallEdge; import pascal.taie.analysis.pta.pts.PointsToSet; import pascal.taie.config.AnalysisOptions; import pascal.taie.ir.exp.Exp; import pascal.taie.ir.exp.InvokeExp; import pascal.taie.ir.exp.Var; -import pascal.taie.ir.proginfo.FieldRef; import pascal.taie.ir.stmt.Invoke; import pascal.taie.ir.stmt.LoadField; import pascal.taie.ir.stmt.StoreField; @@ -32,32 +28,35 @@ import pascal.taie.language.type.NullType; import pascal.taie.language.type.ReferenceType; import pascal.taie.language.type.Type; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.MultiMap; import pascal.taie.util.collection.Sets; +import pascal.taie.util.collection.TwoKeyMap; import java.util.Set; import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.setVirtualArg; import static pascal.taie.analysis.pta.plugin.cutshortcut.SpecialVariables.isNonRelay; -import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.isHashtableClass; -import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.isVectorClass; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.isHashtableType; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.isVectorType; public class CutShortcutSolver extends DefaultSolver { private ContainerConfig containerConfig; - private HostSetFactory hostSetFactory; - private FieldAccessHandler fieldAccessHandler = null; private ContainerAccessHandler containerAccessHandler = null; - private final Set specialHandledReturnVars = Sets.newSet(); - - private final Set ignoredStoreFields = Sets.newSet(); + // cutRetuns resolved By LocalFlowHandler + private final Set cutReturnVars = Sets.newSet(); - private final Set recoveredCallSites = Sets.newSet(); + private final Set cutStoreFields = Sets.newSet(); private final Set selectedMethods = Sets.newSet(); + // callsites whose callee should not be cut, handle container access whose object type is not modeled + private final Set recoveredCallSites = Sets.newSet(); + public CutShortcutSolver(AnalysisOptions options, HeapModel heapModel, ContextSelector contextSelector, CSManager csManager) { super(options, heapModel, contextSelector, csManager); @@ -85,7 +84,6 @@ else if (p instanceof ContainerAccessHandler cah) @Override protected void initialize() { containerConfig = ContainerConfig.config; - hostSetFactory = new HostSetFactory(containerConfig.getHostIndexer()); super.initialize(); } @@ -112,35 +110,50 @@ protected void analyze() { else if (entry instanceof WorkList.CallEdgeEntry eEntry) processCallEdge(eEntry.edge()); else if (entry instanceof WorkList.SetStmtEntry sEntry) - fieldAccessHandler.onNewSetStatement(sEntry.method(), sEntry.fieldRef(), sEntry.baseIndex(), sEntry.rhsIndex()); + fieldAccessHandler.onNewSetStatement(sEntry.csMethod(), sEntry.setStmt()); else if (entry instanceof WorkList.GetStmtEntry gEntry) - fieldAccessHandler.onNewGetStatement(gEntry.method(), gEntry.lhsIndex(), gEntry.baseIndex(), gEntry.fieldRef()); + fieldAccessHandler.onNewGetStatement(gEntry.csMethod(), gEntry.getStmt()); else if (entry instanceof WorkList.HostEntry hEntry) { Pointer p = hEntry.pointer(); - HostSet diff = processHostEntry(hEntry); + PointsToSet diff = processHostEntry(hEntry); if (p instanceof CSVar csVar && !diff.isEmpty()) - containerAccessHandler.onNewHostEntry(csVar, hEntry.kind(), diff); + containerAccessHandler.onNewHostEntry(csVar, diff, hEntry.kind()); } } plugin.onFinish(); } + public boolean addRecoveredCallSite(CSCallSite csCallSite) { + return recoveredCallSites.add(csCallSite); + } + + public boolean isRecoveredCallSite(CSCallSite csCallSite) { + return recoveredCallSites.contains(csCallSite); + } + String[] stopSigns = new String[]{"iterator(", "entrySet()", "keySet()", "values()", "Entry(", "Iterator("}; - public boolean needPropagateHost(Pointer source, FlowKind kind) { - if (kind == FlowKind.RETURN) { - CSVar csSource = (CSVar) source; - Var sourceVar = csSource.getVar(); + /* + * @param source: PFG edge s --> v, if source inside Transfer method, do not propagate ptsH + */ + public boolean needPropagateHost(PointerFlowEdge edge) { + // Todo: Here return var of Map.values() --> r is treated as Local_Assign + if (edge.kind() == FlowKind.RETURN) { + Var sourceVar = ((CSVar) edge.source()).getVar(); JClass container = sourceVar.getMethod().getDeclaringClass(); String methodString = sourceVar.getMethod().toString(); - if (containerConfig.isRealHostClass(container)) { - for (String stopSign : stopSigns) { + // container, entryset type + if (containerConfig.isHostClass(container)) { + // source variable is inside function: iterator, entryset, keyset, values. do not propagate to host + for (String stopSign: stopSigns) { if (methodString.contains(stopSign)) return false; } - if (isHashtableClass(container) && (methodString.contains("elements()") || methodString.contains("keys()"))) + // HashTable.elements() return Enumeration of HashTable.values(), .keys() return Enumeration of keys + if (isHashtableType(container.getType()) && (methodString.contains("elements()") || methodString.contains("keys()"))) return false; - return !isVectorClass(container) || !methodString.contains("elements()"); + // vector.elements() return Enumeration of Vector.elements + return !isVectorType(container.getType()) || !methodString.contains("elements()"); } return true; } @@ -156,18 +169,18 @@ public static boolean isConcerned(Exp exp) { return type instanceof ReferenceType && !(type instanceof NullType); } - public void addIgnoredStoreField(StoreField set) { // 需要跳过的StoreField,位于最内层(你应该知道最内层的含义)的set方法 - ignoredStoreFields.add(set); + public void addCutStoreField(StoreField set) { // 需要跳过的StoreField,位于最内层的set方法 + cutStoreFields.add(set); } - private HostSet processHostEntry(WorkList.HostEntry entry) { + private PointsToSet processHostEntry(WorkList.HostEntry entry) { Pointer pointer = entry.pointer(); - HostSet hostSet = entry.hostSet(); - HostList.Kind kind = entry.kind(); - HostSet diff = containerAccessHandler.getHostListOf(pointer).addAllDiff(kind, hostSet); + PointsToSet hostSet = entry.hostSet(); + HostKind kind = entry.kind(); + PointsToSet diff = containerAccessHandler.getHostListOf(pointer, kind).addAllDiff(hostSet); if (!diff.isEmpty()) { pointerFlowGraph.getOutEdgesOf(pointer).forEach(edge -> { - if (needPropagateHost(edge.source(), edge.kind())) { + if (needPropagateHost(edge)) { Pointer target = edge.target(); workList.addHostEntry(target, kind, diff); } @@ -180,18 +193,16 @@ private void processInstanceStore(CSVar baseVar, PointsToSet pts) { Context context = baseVar.getContext(); Var var = baseVar.getVar(); for (StoreField store : var.getStoreFields()) { - // for StoreFields that are recognized as a setStatement, we skip the process - if (ignoredStoreFields.contains(store)) + // skip cutStores + if (cutStoreFields.contains(store)) continue; Var fromVar = store.getRValue(); - if (isConcerned(fromVar)) { + if (propTypes.isAllowed(fromVar)) { CSVar from = getCSManager().getCSVar(context, fromVar); pts.forEach(baseObj -> { JField field = store.getFieldRef().resolve(); - InstanceField instField = getCSManager().getInstanceField( - baseObj, field); - addPFGEdge(from, instField, FlowKind.INSTANCE_STORE, field.getType() - ); + InstanceField instField = getCSManager().getInstanceField(baseObj, field); + addPFGEdge(from, instField, FlowKind.INSTANCE_STORE); }); } } @@ -203,7 +214,7 @@ private void processInstanceLoad(CSVar baseVar, PointsToSet pts) { for (LoadField load : var.getLoadFields()) { Var toVar = load.getLValue(); JField field = load.getFieldRef().resolveNullable(); - if (isConcerned(toVar) && field != null) { + if (propTypes.isAllowed(toVar) && field != null) { CSVar to = getCSManager().getCSVar(context, toVar); pts.forEach(baseObj -> { InstanceField instField = getCSManager().getInstanceField( @@ -223,6 +234,7 @@ public void processCallEdge(Edge edge) { CSMethod csCallee = edge.getCallee(); addCSMethod(csCallee); if (edge.getKind() != CallKind.OTHER && !isIgnored(csCallee.getMethod())) { + CSCallSite csCallSite = edge.getCallSite(); Context callerCtx = edge.getCallSite().getContext(); Invoke callSite = edge.getCallSite().getCallSite(); Context calleeCtx = csCallee.getContext(); @@ -231,7 +243,7 @@ public void processCallEdge(Edge edge) { // pass arguments to parameters for (int i = 0; i < invokeExp.getArgCount(); ++i) { Var arg = invokeExp.getArg(i); - if (isConcerned(arg)) { + if (propTypes.isAllowed(arg)) { Var param = callee.getIR().getParam(i); CSVar argVar = getCSManager().getCSVar(callerCtx, arg); CSVar paramVar = getCSManager().getCSVar(calleeCtx, param); @@ -239,12 +251,12 @@ public void processCallEdge(Edge edge) { } } // pass results to LHS variable - if (!ContainerAccessHandler.CutReturnEdge(callSite, callee) || recoveredCallSites.contains(callSite)) { - Var lhs = callSite.getResult(); - if (lhs != null && isConcerned(lhs)) { + Var lhs = callSite.getResult(); + if (!ContainerAccessHandler.CutReturnEdge(lhs, callee) || recoveredCallSites.contains(csCallSite)) { + if (lhs != null && propTypes.isAllowed(lhs)) { CSVar csLHS = getCSManager().getCSVar(callerCtx, lhs); for (Var ret : callee.getIR().getReturnVars()) { - if (isConcerned(ret) && !specialHandledReturnVars.contains(ret)) { + if (propTypes.isAllowed(ret) && !cutReturnVars.contains(ret)) { CSVar csRet = getCSManager().getCSVar(calleeCtx, ret); addPFGEdge(csRet, csLHS, FlowKind.RETURN); } @@ -256,17 +268,8 @@ public void processCallEdge(Edge edge) { } } - public boolean addRecoveredCallSite(Invoke callSite) { - return recoveredCallSites.add(callSite); - } - - public boolean isRecoveredCallSite(Invoke callSite) { - return recoveredCallSites.contains(callSite); - } - public void addPFGEdge(Pointer source, Pointer target, FlowKind kind, Set transfers) { PointerFlowEdge edge = new PointerFlowEdge(kind, source, target); - transfers.forEach(edge::addTransfer); if (pointerFlowGraph.addEdge(edge) != null) { PointsToSet sourceSet = getPointsToSetOf(source); PointsToSet targetSet = makePointsToSet(); @@ -282,20 +285,34 @@ public void addPFGEdge(Pointer source, Pointer target, FlowKind kind, Set getInvolvedMethods() { return selectedMethods; } - - public HostSet getEmptyHostSet() { - return hostSetFactory.make(); - } } diff --git a/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java b/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java index 4ac8b88a2..fa9062bdf 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java +++ b/src/main/java/pascal/taie/analysis/pta/core/solver/DefaultSolver.java @@ -122,7 +122,7 @@ public class DefaultSolver implements Solver { private final PointsToSetFactory ptsFactory; - private final PropagateTypes propTypes; + protected final PropagateTypes propTypes; /** * Whether only analyzes application code. @@ -400,7 +400,8 @@ private void processInstanceLoad(CSVar baseVar, PointsToSet pts) { JField field = load.getFieldRef().resolve(); pts.forEach(baseObj -> { if (baseObj.getObject().isFunctional()) { - InstanceField instField = csManager.getInstanceField(baseObj, field); + InstanceField instField = csManager.getInstanceField( + baseObj, field); addPFGEdge(instField, to, FlowKind.INSTANCE_LOAD); } }); diff --git a/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java b/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java index 8a75b764a..143cc44f3 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java +++ b/src/main/java/pascal/taie/analysis/pta/core/solver/WorkList.java @@ -26,12 +26,9 @@ import pascal.taie.analysis.pta.core.cs.element.CSCallSite; import pascal.taie.analysis.pta.core.cs.element.CSMethod; import pascal.taie.analysis.pta.core.cs.element.Pointer; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSet; -import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.HostKind; +import pascal.taie.analysis.pta.plugin.cutshortcut.field.*; import pascal.taie.analysis.pta.pts.PointsToSet; -import pascal.taie.ir.proginfo.FieldRef; -import pascal.taie.language.classes.JMethod; import pascal.taie.util.collection.Maps; import java.util.ArrayDeque; @@ -43,7 +40,6 @@ * Represents work list in pointer analysis. */ final class WorkList { - /** * Pointer entries to be processed. */ @@ -78,13 +74,13 @@ Entry pollEntry() { if (!callEdges.isEmpty()) { // for correctness, we need to ensure that any call edges in // the work list must be processed prior to the pointer entries - return new CallEdgeEntry(callEdges.poll()); + return new WorkList.CallEdgeEntry(callEdges.poll()); } else if (!pointerEntries.isEmpty()) { var it = pointerEntries.entrySet().iterator(); var e = it.next(); it.remove(); - return new PointerEntry(e.getKey(), e.getValue()); + return new WorkList.PointerEntry(e.getKey(), e.getValue()); } else if (!setStmtEntries.isEmpty()) return setStmtEntries.poll(); @@ -112,26 +108,27 @@ record CallEdgeEntry(Edge edge) implements Entry { } - record HostEntry(Pointer pointer, HostList.Kind kind, HostSet hostSet) - implements Entry { + record HostEntry(Pointer pointer, HostKind kind, PointsToSet hostSet) + implements WorkList.Entry { } - record SetStmtEntry(JMethod method, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) - implements Entry { + record SetStmtEntry(CSMethod csMethod, SetStatement setStmt) + implements WorkList.Entry { } - record GetStmtEntry(JMethod method, int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) - implements Entry { + record GetStmtEntry(CSMethod csMethod, GetStatement getStmt) + implements WorkList.Entry { } - void addHostEntry(Pointer pointer, HostList.Kind kind, HostSet hostSet) { + void addHostEntry(Pointer pointer, HostKind kind, PointsToSet hostSet) { hostEntries.add(new HostEntry(pointer, kind, hostSet)); } - void addSetStmtEntry(JMethod method, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) { - setStmtEntries.add(new SetStmtEntry(method, fieldRef, baseIndex, rhsIndex)); + + void addSetStmtEntry(CSMethod csMethod, SetStatement setStmt) { + setStmtEntries.add(new SetStmtEntry(csMethod, setStmt)); } - void addGetStmtEntry(JMethod method, int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { - getStmtEntries.add(new GetStmtEntry(method, lhsIndex, baseIndex, fieldRef)); + void addGetStmtEntry(CSMethod csMethod, GetStatement getStmt) { + getStmtEntries.add(new GetStmtEntry(csMethod, getStmt)); } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/ResultProcessor.java b/src/main/java/pascal/taie/analysis/pta/plugin/ResultProcessor.java index 95721aae3..f5a5cfd2d 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/ResultProcessor.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/ResultProcessor.java @@ -108,16 +108,17 @@ public static void process(AnalysisOptions options, boolean taintEnabled = options.getString("taint-config") != null || !((List) options.get("taint-config-providers")).isEmpty(); + boolean onlyDumpApp = options.getBoolean("only-dump-app"); if (options.getBoolean("dump")) { dumpPointsToSet(result, taintEnabled); } if (options.getBoolean("dump-ci")) { - dumpCIPointsToSet(result); + dumpCIPointsToSet(result, onlyDumpApp); } if (options.getBoolean("dump-yaml")) { - dumpPointsToSetInYaml(result); + dumpPointsToSetInYaml(result, onlyDumpApp); } String expectedFile = options.getString("expected-file"); @@ -198,7 +199,7 @@ private static void dumpPointers( out.println(); } - private static void dumpPointsToSetInYaml(PointerAnalysisResult result) { + private static void dumpPointsToSetInYaml(PointerAnalysisResult result, boolean onlyDumpApp) { File outFile = new File(World.get().getOptions().getOutputDir(), RESULTS_YAML_FILE); logger.info("Dumping points-to set (with contexts) in YAML to {}", outFile.getAbsolutePath()); @@ -220,6 +221,7 @@ private static void dumpPointsToSetInYaml(PointerAnalysisResult result) { // - "[]:NewObj{[0@L1] new A}" final var variables = result.getCSVars() .stream() + .filter(csVar -> !onlyDumpApp || csVar.getVar().getMethod().isApplication()) .collect(Collectors.groupingBy(csVar -> csVar.getVar().getMethod().getSignature(), Maps::newOrderedMap, Collectors.collectingAndThen( @@ -250,6 +252,7 @@ private static void dumpPointsToSetInYaml(PointerAnalysisResult result) { // - "[]:NewObj{[0@L1] new String}" final var staticFields = result.getStaticFields() .stream() + .filter(staticField -> !onlyDumpApp || staticField.getField().isApplication()) .collect(Collectors.groupingBy(sField -> sField.getField().getDeclaringClass().getName(), Maps::newOrderedMap, Collectors.mapping(sField -> Maps.ofLinkedHashMap( @@ -268,6 +271,7 @@ private static void dumpPointsToSetInYaml(PointerAnalysisResult result) { // - "[]:NewObj{[0@L1] new String}" final var instanceFields = result.getInstanceFields() .stream() + .filter(instanceField -> !onlyDumpApp || instanceField.getField().isApplication()) .collect(Collectors.groupingBy(iField -> iField.getBase().getObject(), () -> Maps.newOrderedMap(objComparator), Collectors.collectingAndThen( @@ -297,6 +301,7 @@ private static void dumpPointsToSetInYaml(PointerAnalysisResult result) { // - "ConstantObj{java.lang.String: \"hello\"}" final var arrayIndexes = result.getArrayIndexes() .stream() + .filter(arrayIndex -> !onlyDumpApp || (arrayIndex.getArray().getObject().getContainerMethod().isPresent() && arrayIndex.getArray().getObject().getContainerMethod().get().isApplication())) .collect(Collectors.groupingBy(ai -> ai.getArray().getObject(), () -> Maps.newOrderedMap(objComparator), Collectors.collectingAndThen( @@ -334,7 +339,7 @@ private static void dumpPointsToSetInYaml(PointerAnalysisResult result) { /** * Dumps points-to sets for all variables (without contexts). */ - private static void dumpCIPointsToSet(PointerAnalysisResult result) { + private static void dumpCIPointsToSet(PointerAnalysisResult result, boolean onlyDumpApp) { File outFile = new File(World.get().getOptions().getOutputDir(), CI_RESULTS_FILE); try (PrintStream out = new PrintStream(new FileOutputStream(outFile))) { logger.info("Dumping points-to set (without contexts) to {}", @@ -343,6 +348,7 @@ private static void dumpCIPointsToSet(PointerAnalysisResult result) { v -> v.getMethod().toString() + '/' + v.getName(); result.getVars() .stream() + .filter(v -> !onlyDumpApp || v.getMethod().isApplication()) .sorted(Comparator.comparing(toString)) .forEach(v -> { Set pts = result.getPointsToSet(v); diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java index 41363a37c..f765faa3d 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/ReflectiveEdgeProperty.java @@ -17,11 +17,11 @@ public enum ReflectiveCallKind { // args is a variable of array type which stores the argument of m in order } - private static Map reflectiveCallKindMap = Maps.newMap(); + private static final Map reflectiveCallKindMap = Maps.newMap(); - private static Map reflectiveCallVirtualArgMap = Maps.newMap(); + private static final Map reflectiveCallVirtualArgMap = Maps.newMap(); - public static void setgetReflectiveKind(ReflectiveCallEdge reflectiveCallEdge, ReflectiveCallKind kind) { + public static void setReflectiveKind(ReflectiveCallEdge reflectiveCallEdge, ReflectiveCallKind kind) { reflectiveCallKindMap.put(reflectiveCallEdge, kind); } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java index 25fbe7e27..8efe9c0b7 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/SpecialVariables.java @@ -3,18 +3,20 @@ import pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex; import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.LoadField; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.Sets; import java.util.Map; import java.util.Set; public class SpecialVariables { - private static Set definedVars; + private static final Set definedVars = Sets.newSet(); - private static Set virtualVars; + private static final Set virtualVars = Sets.newSet(); - private static Map definedParameterIndexes; + private static final Map definedParameterIndexes = Maps.newMap(); - private static Set relayedLoadFields; + private static final Set relayedLoadFields = Sets.newSet(); public static void setDefined(Var var) { definedVars.add(var); diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java index 432410873..3e46df1b3 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ClassAndTypeClassifier.java @@ -1,72 +1,45 @@ package pascal.taie.analysis.pta.plugin.cutshortcut.container; import pascal.taie.World; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContainerType; import pascal.taie.language.classes.ClassHierarchy; import pascal.taie.language.classes.JClass; import pascal.taie.language.type.Type; import pascal.taie.language.type.TypeSystem; -import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.containerType.MAP; -import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.containerType.OTHER; - public class ClassAndTypeClassifier { private static final ClassHierarchy hierarchy = World.get().getClassHierarchy(); - private static final TypeSystem typeSystem = World.get().getTypeSystem(); private static final JClass mapClass = hierarchy.getClass("java.util.Map"); - private static final JClass collectionClass = hierarchy.getClass("java.util.Collection"); private static final JClass mapEntryClass = hierarchy.getClass("java.util.Map$Entry"); - + private static final JClass collectionClass = hierarchy.getClass("java.util.Collection"); private static final JClass iteratorClass = hierarchy.getClass("java.util.Iterator"); - private static final JClass enumerationClass = hierarchy.getClass("java.util.Enumeration"); - - private static final JClass hashtableClass = hierarchy.getClass("java.util.Hashtable"); private static final Type hashtableType = typeSystem.getType("java.util.Hashtable"); - - private static final JClass vectorClass = hierarchy.getClass("java.util.Vector"); private static final Type vectorType = typeSystem.getType("java.util.Vector"); - private static final JClass abstractList = hierarchy.getClass("java.util.AbstractList"); - - public enum containerType { - MAP, COLLECTION, OTHER - } - - public static containerType ClassificationOf(Type type) { + public static ContainerType ClassificationOf(Type type) { JClass clz = hierarchy.getClass(type.getName()); - if (clz == null) - return OTHER; - if (hierarchy.isSubclass(mapClass, clz)) - return MAP; - if (hierarchy.isSubclass(collectionClass, clz)) - return containerType.COLLECTION; - return OTHER; + return ClassificationOf(clz); } - public static boolean isIteratorClass(JClass iterator) { - return hierarchy.isSubclass(iteratorClass, iterator); - } - - public static boolean isEnumerationClass(JClass enumeration) { - return hierarchy.isSubclass(enumerationClass, enumeration) && !enumeration.isApplication(); - } - - public static boolean isAbstractListClass(JClass clz) { - return hierarchy.isSubclass(abstractList, clz); - } - - public static boolean isMapEntryClass(JClass entry) { - return hierarchy.isSubclass(mapEntryClass, entry) && !entry.isApplication(); - } - - public static boolean isHashtableClass(JClass hashtable) { - return hierarchy.isSubclass(hashtableClass, hashtable) && !hashtable.isApplication(); + public static ContainerType ClassificationOf(JClass clz) { + if (clz == null) + return ContainerType.OTHER; + else if (hierarchy.isSubclass(mapClass, clz)) + return ContainerType.MAP; + else if (hierarchy.isSubclass(collectionClass, clz)) + return ContainerType.COLLECTION; + else if (hierarchy.isSubclass(iteratorClass, clz)) + return ContainerType.ITER; + return ContainerType.OTHER; } - public static boolean isVectorClass(JClass vector) { - return hierarchy.isSubclass(vectorClass, vector) && !vector.isApplication(); + public static JClass getOuterClass(JClass inner) { + if (inner != null && inner.hasOuterClass()) + inner = inner.getOuterClass(); + return inner; } public static boolean isVectorType(Type vector) { @@ -77,4 +50,7 @@ public static boolean isHashtableType(Type hashtable) { return typeSystem.isSubtype(hashtableType, hashtable); } + public static boolean isMapEntryClass(JClass entry) { + return hierarchy.isSubclass(mapEntryClass, entry); + } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java index 0bbf88f22..ff32346f0 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerAccessHandler.java @@ -5,13 +5,7 @@ import pascal.taie.analysis.graph.callgraph.Edge; import pascal.taie.analysis.graph.flowgraph.FlowKind; import pascal.taie.analysis.pta.core.cs.context.Context; -import pascal.taie.analysis.pta.core.cs.element.ArrayIndex; -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.cs.element.HostPointer; -import pascal.taie.analysis.pta.core.cs.element.Pointer; +import pascal.taie.analysis.pta.core.cs.element.*; import pascal.taie.analysis.pta.core.heap.HeapModel; import pascal.taie.analysis.pta.core.heap.Obj; import pascal.taie.analysis.pta.core.solver.CutShortcutSolver; @@ -19,15 +13,17 @@ import pascal.taie.analysis.pta.core.solver.Solver; import pascal.taie.analysis.pta.plugin.Plugin; import pascal.taie.analysis.pta.plugin.cutshortcut.SpecialVariables; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContExitCategory; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ExtendType; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.HostKind; import pascal.taie.analysis.pta.pts.PointsToSet; import pascal.taie.ir.exp.InvokeExp; import pascal.taie.ir.exp.InvokeInstanceExp; import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.Cast; import pascal.taie.ir.stmt.Invoke; import pascal.taie.ir.stmt.New; -import pascal.taie.language.classes.JClass; import pascal.taie.language.classes.JMethod; -import pascal.taie.language.classes.Subsignature; import pascal.taie.language.type.ArrayType; import pascal.taie.language.type.ClassType; import pascal.taie.language.type.Type; @@ -35,644 +31,604 @@ import pascal.taie.util.AnalysisException; import pascal.taie.util.collection.*; -import java.util.Map; -import java.util.Objects; +import java.util.Collections; import java.util.Set; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostSet; - import static pascal.taie.analysis.pta.core.solver.CutShortcutSolver.isConcerned; import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.*; -import static pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList.Kind.*; - +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContExitCategory.*; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.HostKind.*; +// ToDo: 其它自定义AbstractList类型,尤其是匿名内部类,实现了自定义get等方法 public class ContainerAccessHandler implements Plugin { private CutShortcutSolver solver; private CSManager csManager; - private HeapModel heapModel; - - private Context emptyContext; - private TypeSystem typeSystem; - private static ContainerConfig containerConfig; - - private CallGraph callGraph; - - private final String[] categories = new String[]{"Map-Key", "Map-Value", "Col-Value"}; + private HostManager hostManager; - private final HostList.Kind[] SetKindsInMap = new HostList.Kind[]{MAP_KEY_SET, MAP_VALUES, MAP_ENTRY_SET}; + private HeapModel heapModel; - private final MultiMap ArrayVarToVirtualArrayVar = Maps.newMultiMap(); + private CallGraph callGraph; - private final MultiMap CollectionVarToVirtualArrayVar = Maps.newMultiMap(); + private ContainerConfig config; - private final MultiMap ExtenderLargerToSmaller = Maps.newMultiMap(); - private final MultiMap ExtenderSmallerToLarger = Maps.newMultiMap(); + private final Set ContKindsInterested = Set.of(COL, MAP_KEY_SET, MAP_VALUES, MAP_ENTRY_SET); + private final MultiMap, Pair> HostPropagater = Maps.newMultiMap(); - private final Map pointerHostListMap = Maps.newMap(); + // The first key type of pointerHostListMap may not only be ''CSVar'': + // a container field of a object obj.list (InstanceField) may also point-to container object. + private final TwoKeyMap pointerHostListMap = Maps.newTwoKeyMap(); // ptsH - private final Map>> csVarMustRelatedInvokes = Maps.newMap(); + // for Col-Value, Map-Value, Map-key exit calledge e: => , add -> + private final MultiMap> cachedContExitValues = Maps.newMultiMap(); + // for MapEntry-GetKey(Map-Key), MapEntry-GetValue(Map-Value), Iterator-next/previous/nextElement(Col-Value) l: r = v.next(..), add -> + private final MultiMap> cachedIterExitValues = Maps.newMultiMap(); + // \for v = new ArrayList(), l: r = v.get(0) => ArrayList.get, \in pts(), => \in hostToExit + private final MultiMap hostToExits = Maps.newMultiMap(); - private final MultiMap, Pair> HostPropagater = Maps.newMultiMap(); + // assist worklist method to handle container extender + private final MultiMap ExtenderAddedToBase = Maps.newMultiMap(); // for , add v to + private final MultiMap ExtenderBaseToAdded = Maps.newMultiMap(); // for , add v1 to - private final Map VirtualArgsOfEntrySet = Maps.newMap(); + // process ArrayInitializer, l: add(v_d, v_s), where v_s is array, v_d is collection, for worklist, add a temp variable arr_l + private final MultiMap ArrayVarToVirtualArrayVar = Maps.newMultiMap(); // v_s --> arr_l + private final MultiMap CollectionVarToVirtualArrayVar = Maps.newMultiMap(); // v_d --> arr_l - private final MultiMap hostToExits = Maps.newMultiMap(); + // We only model common container access. For user-defined container type like CustomArrayList, we usually ignore because they may define other entrance and exit method. + // For example, CustomArrayList.addAnObject, getAnObject. Since we have not model it, soundness issue could occur. + // But, there is [Entrance-Extend] method like List.addAll. If v is ArrayList, v.addAll(customArrayList). Where customArrayList may miss some src/dst variables. + // After propagate to v, v can not be soundly modeled. + // Hence here, taintHosts records those container objects whose type is modeled but may be influenced by unmodeled type container var like v. + private final Set taintHosts = Sets.newSet(); - private final Map abstractListToGet = Maps.newMap(); + // Collect those var whose type is Map.Entry + private static final Set MapEntryVar = Sets.newSet(); public void setSolver(Solver solver) { if (solver instanceof CutShortcutSolver cutShortcutSolver) { this.solver = cutShortcutSolver; csManager = solver.getCSManager(); - emptyContext = solver.getContextSelector().getEmptyContext(); typeSystem = solver.getTypeSystem(); - heapModel = solver.getHeapModel(); - containerConfig = ContainerConfig.config; - if (containerConfig == null) - throw new AnalysisException("No containerConfig for Host Manager!"); callGraph = solver.getCallGraph(); + heapModel = solver.getHeapModel(); + config = ContainerConfig.config; + hostManager = new HostManager(csManager); } - else { + else throw new AnalysisException("Invalid solver!"); - } - } - - private JMethod resolveMethod(JClass clz, Subsignature subSig) { - if (clz == null) - return null; - if (clz.getDeclaredMethod(subSig) != null) - return clz.getDeclaredMethod(subSig); - return resolveMethod(clz.getSuperClass(), subSig); } @Override - public void onStart() { - containerConfig.getAllEntrySetClasses().forEach(c -> { - Var argVar = new Var(null, c + "/arg", typeSystem.getType("java.lang.Object"), -1); - SpecialVariables.setVirtualVar(argVar); - VirtualArgsOfEntrySet.put(c, argVar); - }); - containerConfig.taintAbstractListClasses().forEach(jClass -> { - JMethod getMethod = resolveMethod(jClass, Subsignature.get("java.lang.Object get(int)")); - if (getMethod != null) { - abstractListToGet.put(jClass, getMethod); + public void onNewMethod(JMethod method) { + method.getIR().forEach(stmt -> { + if (stmt instanceof Cast castStmt) { + Type castType = castStmt.getRValue().getCastType(); + if (castType instanceof ClassType classType && isMapEntryClass(classType.getJClass())) + MapEntryVar.add(castStmt.getRValue().getValue()); } }); } @Override - public void onNewPointsToSet(CSVar csVar, PointsToSet pts) { - HostSet colSet = solver.getEmptyHostSet(), mapSet = solver.getEmptyHostSet(); - pts.forEach(csObj -> { - Obj obj = csObj.getObject(); - Type objType = obj.getType(); - if (containerConfig.isHostType(objType)) { - switch (ClassificationOf(objType)) { - case MAP -> mapSet.addHost(containerConfig.getObjectHost(obj, Host.Classification.MAP)); - case COLLECTION -> colSet.addHost(containerConfig.getObjectHost(obj, Host.Classification.COLLECTION)); + public void onNewCSMethod(CSMethod csMethod) { + Context context = csMethod.getContext(); + JMethod method = csMethod.getMethod(); + method.getIR().forEach(stmt -> { + if (stmt instanceof New newStmt) { + Obj obj = heapModel.getObj(newStmt); + Type objType = obj.getType(); + // called in Map.keySet()/values(), lhs = new KeySet()/values() + if (!method.isStatic()) { + CSVar csThis = csManager.getCSVar(context, method.getIR().getThis()); + CSVar csLHS = csManager.getCSVar(context, newStmt.getLValue()); + if (config.isKeySetClass(objType)) + HostPropagater.put(new Pair<>(HostKind.MAP, csThis), new Pair<>(MAP_KEY_SET, csLHS)); + else if (config.isValueSetClass(objType)) + HostPropagater.put(new Pair<>(HostKind.MAP, csThis), new Pair<>(MAP_VALUES, csLHS)); } } - ArrayVarToVirtualArrayVar.get(csVar.getVar()).forEach(virtualArray -> { - CSVar csVirtualArray = csManager.getCSVar(emptyContext, virtualArray); - ArrayIndex arrayIndex = csManager.getArrayIndex(csObj); - solver.addPFGEdge(arrayIndex, csVirtualArray, FlowKind.VIRTUAL_ARRAY, virtualArray.getType()); - }); }); - if (!mapSet.isEmpty()) { - getHostListOf(csVar).addHostSet(MAP_0, mapSet); - onNewHostEntry(csVar, MAP_0, mapSet); - } - if (!colSet.isEmpty()) { - getHostListOf(csVar).addHostSet(COL_0, colSet); - onNewHostEntry(csVar, COL_0, colSet); - } } - public void onNewHostEntry(CSVar csVar, HostList.Kind kind, HostSet hostSet) { - propagateHostAndKind(csVar, hostSet, kind); - ProcessMustRelatedInvokes(csVar, hostSet); - Var base = csVar.getVar(); - base.getInvokes().forEach(invoke -> { - if (isRelatedEntranceInvoke(invoke)) { - CSCallSite csCallSite = csManager.getCSCallSite(emptyContext, invoke); - InvokeExp invokeExp = invoke.getInvokeExp(); - callGraph.getCalleesOf(csCallSite).forEach(csMethod -> { - JMethod method = csMethod.getMethod(); - int npara = method.getParamCount(); - for (int i = 0; i < npara; i++) { - relateSourceToHosts(invokeExp, method, i, hostSet); - } - }); - } - }); - CollectionVarToVirtualArrayVar.get(base).forEach(virtualArray -> { - if (kind == COL_0) - hostSet.forEach(host -> addSourceToHost(virtualArray, host, "Col-Value")); - else { - hostSet.forEach(host -> addSourceToHost(virtualArray, host, "Map-Value")); - hostSet.forEach(host -> addSourceToHost(virtualArray, host, "Map-Key")); - } - }); - ExtenderLargerToSmaller.get(base).forEach(smaller -> { - CSVar smallerPointer = csManager.getCSVar(emptyContext, smaller); - HostList hostMap = getHostListOf(smallerPointer); - if (kind == COL_0) { - if (hostMap.hasKind(COL_0)) - addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(COL_0)), true, false, false); - if (hostMap.hasKind(MAP_KEY_SET)) - addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(MAP_KEY_SET)), false, true, false); - if (hostMap.hasKind(MAP_VALUES)) - addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(MAP_VALUES)), false, false, true); - } - else if (kind == MAP_0) { - if (hostMap.hasKind(MAP_0)) - addHostSubsetRelation(hostSet, Objects.requireNonNull(hostMap.getHostSetOf(MAP_0)), - true, false, false); - } - }); - ExtenderSmallerToLarger.get(base).forEach(larger -> { - CSVar largerPointer = csManager.getCSVar(emptyContext, larger); - HostList hostMap = getHostListOf(largerPointer); - if (hostMap.hasKind(COL_0)) { - HostSet set = hostMap.getHostSetOf(COL_0); - if (kind == COL_0) - addHostSubsetRelation(set, hostSet, true, false, false); - else if (kind == MAP_KEY_SET) - addHostSubsetRelation(set, hostSet, false, true, false); - else if (kind == MAP_VALUES) - addHostSubsetRelation(set, hostSet, false, false, true); + // --> + @Override + public void onNewCallEdge(Edge edge) { + if (edge.getKind() == CallKind.OTHER) + return; + CSCallSite csCallSite = edge.getCallSite(); + CSMethod csCallee = edge.getCallee(); // + Invoke callSite = csCallSite.getCallSite(); // l + JMethod callee = csCallee.getMethod(); // m + Context callSiteContext = csCallSite.getContext(); // c + InvokeExp invokeExp = callSite.getInvokeExp(); // r = v.k(l_a1, ...) + + if (invokeExp instanceof InvokeInstanceExp instanceExp) { + Var base = instanceExp.getBase(); // v + CSVar csBase = csManager.getCSVar(callSiteContext, base); // + CSVar csThis = csManager.getCSVar(csCallee.getContext(), callee.getIR().getThis()); // + + // propagate ptsH to callee this + ContKindsInterested.forEach(hostKind -> { + PointsToSet baseHostMap = pointerHostListMap.getOrDefault(csBase, hostKind, null); + if (baseHostMap != null && !baseHostMap.isEmpty()) { + solver.addHostEntry(csThis, hostKind, baseHostMap); + HostPropagater.put(new Pair<>(hostKind, csBase), new Pair<>(hostKind, csThis)); + } + }); + // Array Initializer + Pair arrayInitInfo = config.getArrayInitializer(callee); + if (arrayInitInfo != null) { + solver.addSelectedMethod(callee); + processArrayInitializer(csCallSite, arrayInitInfo, instanceExp); } - if (kind == MAP_0) { - if (hostMap.hasKind(MAP_0)) - addHostSubsetRelation(Objects.requireNonNull(hostMap.getHostSetOf(MAP_0)), hostSet, - true, false, false); + // callee is [Container Entrance-Append] like v.add(E) + Set> categoryIndexPairs = config.getEntranceAppendIndex(callee); + if (!categoryIndexPairs.isEmpty()) + pointerHostListMap.getOrDefault(csBase, Collections.emptyMap()).forEach((hostKind, hostSet) -> + relateSourceToHosts(csCallSite, csCallee, categoryIndexPairs, hostSet)); + + // callee is [Container Entrance-Extend] like v.addAll(list) + Pair entranceExtendIndex = config.getEntranceExtendIndex(callee); + if (entranceExtendIndex != null) { + solver.addSelectedMethod(callee); + ExtendType extendType = entranceExtendIndex.first(); + Var addedContainer = instanceExp.getArg(entranceExtendIndex.second()); + processEntranceExtend(csBase, addedContainer, extendType); } - }); - HostPropagater.get(new Pair<>(kind, csVar)).forEach(kindCSVarPair -> solver.addHostEntry(kindCSVarPair.second(), kindCSVarPair.first(), hostSet)); - HostPropagater.get(new Pair<>(ALL, csVar)).forEach(kindCSVarPair -> solver.addHostEntry(kindCSVarPair.second(), kind, hostSet)); - for (HostList.Kind k: SetKindsInMap) { - if (k == kind) { - base.getInvokes().forEach(callSite -> { - CSCallSite csCallSite = csManager.getCSCallSite(emptyContext, callSite); - callGraph.getCalleesOf(csCallSite).forEach(csCallee -> { - Var thisVar = csCallee.getMethod().getIR().getThis(); - if (thisVar != null) { - CSVar csThis = csManager.getCSVar(emptyContext, thisVar); - solver.addHostEntry(csThis, kind, hostSet); - } - }); - }); + + // callee is [Container Exit] + Var lhs = callSite.getLValue(); + if (lhs != null && isConcerned(lhs)) { + // Standard container-element exit method, like l: r = v.get(i);, v is Collection/Map + ContExitCategory exitCategory = config.getContainerExitCategory(callee); + if (exitCategory != null) { + solver.addSelectedMethod(callee); + cachedContExitValues.put(csBase, new Pair<>(callSite, exitCategory)); // prepare for new worklist entry + addTargetToAllHost(csBase, exitCategory == ColValue ? COL : HostKind.MAP, csCallSite, exitCategory, true); + } + + // MapEntry exit method, l: r = v.getKey(), v is MapEntry +// else if (config.isMapEntryGetKeyMethod(callee)) { +// solver.addSelectedMethod(callee); +// addTargetToAllHost(csBase, MAP_ENTRY, csCallSite, MapKey, false); +// cachedIterExitValues.put(csBase, new Triplet<>(callSite, MAP_ENTRY, MapKey)); +// } +// // MapEntry exit method, l: r = v.getValue(), v is MapEntry +// else if (config.isMapEntryGetValueMethod(callee)) { +// solver.addSelectedMethod(callee); +// addTargetToAllHost(csBase, MAP_ENTRY, csCallSite, MapValue, false); +// cachedIterExitValues.put(csBase, new Triplet<>(callSite, MAP_ENTRY, MapValue)); +// } + // iterator-element exit method, l: r = v.next()/previous()/nextElement(), v is iterator/enumeration + else if (config.isIteratorExitMethods(callee)) { + solver.addSelectedMethod(callee); + /* an example, HashSet.iterator() return HashMap.KeyIterator type. + * vs = new HashSet() {allocate os}, is = vs.iterator(), vsl = is.next(); + * vm = new HashMap() {allocate om}, im = vm.keyIterator(), vml = im.next(); + * same next call, the former is os --ColValue--> vsl, later is om --KeyValue--> vml, but with same next method + * difference is that is => COL_ITR while im => MAP_KEY_ITR + */ + addTargetToAllHost(csBase, MAP_VALUE_ITR, csCallSite, MapValue, false); + cachedIterExitValues.put(csBase, new Triplet<>(callSite, MAP_VALUE_ITR, MapValue)); + addTargetToAllHost(csBase, MAP_KEY_ITR, csCallSite, MapKey, false); + cachedIterExitValues.put(csBase, new Triplet<>(callSite, MAP_KEY_ITR, MapKey)); + addTargetToAllHost(csBase, COL_ITR, csCallSite, ColValue, false); + cachedIterExitValues.put(csBase, new Triplet<>(callSite, COL_ITR, ColValue)); + } } } } - private void processArrayInitializer(Invoke callSite, JMethod callee) { - solver.addSelectedMethod(callee); - Pair arrayCollectionPair = containerConfig.getArrayInitializer(callee); - InvokeExp invoke = callSite.getInvokeExp(); - Var arrayVar = getArgument(invoke, arrayCollectionPair.first()), - collectionVar = getArgument(invoke, arrayCollectionPair.second()); + // --> , where m is initializer of array List, l_a0/m_p0 are usually arrays of object + private void processArrayInitializer(CSCallSite csCallSite, Pair arrayInitInfo, InvokeInstanceExp instanceExp) { + Var arrayVar = instanceExp.getArg(arrayInitInfo.first()), // l_ai, copied array + collectionVar = arrayInitInfo.second() == -1 ? instanceExp.getBase(): instanceExp.getArg(arrayInitInfo.second()); // v if (!(arrayVar.getType() instanceof ArrayType)) throw new AnalysisException("Not Array Type!"); Type elementType = ((ArrayType) arrayVar.getType()).elementType(); - Var virtualArrayVar = new Var(callSite.getContainer(), "virtualArrayVar", elementType, -1); + // virtual var for m + Context callSiteContext = csCallSite.getContext(); + JMethod caller = csCallSite.getCallSite().getContainer(); + Var virtualArrayVar = new Var(caller, + "virtualArrayVar[" + caller.getName() + ", line:" + csCallSite.getCallSite().getLineNumber() +"]", + elementType, -1); // arr_arg_caller SpecialVariables.setVirtualVar(virtualArrayVar); - ArrayVarToVirtualArrayVar.put(arrayVar, virtualArrayVar); - CollectionVarToVirtualArrayVar.put(collectionVar, virtualArrayVar); - CSVar csArray = csManager.getCSVar(emptyContext, arrayVar); - solver.getPointsToSetOf(csArray).forEach(csObj -> { - ArrayIndex arrayIndex = csManager.getArrayIndex(csObj); - solver.addPFGEdge(arrayIndex, csManager.getCSVar(emptyContext, virtualArrayVar), FlowKind.VIRTUAL_ARRAY, elementType); + ArrayVarToVirtualArrayVar.put(arrayVar, virtualArrayVar); // vs --> arr_l + CollectionVarToVirtualArrayVar.put(collectionVar, virtualArrayVar); // vd --> arr_l + CSVar csArray = csManager.getCSVar(callSiteContext, arrayVar); // + CSVar csVirtualArray = csManager.getCSVar(callSiteContext, virtualArrayVar); // + + solver.getPointsToSetOf(csArray).forEach(csObj -> { // \forall \in pts() + ArrayIndex arrayIndex = csManager.getArrayIndex(csObj); // + // --> + solver.addPFGEdge(arrayIndex, csVirtualArray, FlowKind.VIRTUAL_ARRAY, elementType); }); - CSVar csCollection = csManager.getCSVar(emptyContext, collectionVar); - HostList hostMap = getHostListOf(csCollection); - if (hostMap.hasKind(COL_0)) { - hostMap.getHostSetOf(COL_0).forEach(host -> { - addSourceToHost(virtualArrayVar, host, "Col-Value"); - }); + + CSVar csCollection = csManager.getCSVar(callSiteContext, collectionVar); // + // \forall \in ptsH(), --> + getHostListOf(csCollection, COL).forEach(hostObj -> addSourceToHost(csVirtualArray, hostObj, ColValue)); + } + + /* + * process c, l: r = v.k(..., v1, ...), where v and v1 are container variable, callee is extend method like List.addAll/Map.putAll + * @params csBaseContainer: // + * @params csAddedContainer: // + */ + private void processEntranceExtend(CSVar csBaseContainer, Var addedContainer, ExtendType extendType) { + // Collection/Map.Keyset()/Map.Values() -> Collection + CSVar csAddedContainer = csManager.getCSVar(csBaseContainer.getContext(), addedContainer); + ExtenderAddedToBase.put(csAddedContainer, csBaseContainer.getVar()); + ExtenderBaseToAdded.put(csBaseContainer, addedContainer); + + if (extendType == ExtendType.ColToCol) { + PointsToSet baseContainerSet = getHostListOf(csBaseContainer, COL); + addHostSubsetRelation(baseContainerSet, getHostListOf(csAddedContainer, COL), ExtendType.ColToCol); + addHostSubsetRelation(baseContainerSet, getHostListOf(csAddedContainer, MAP_KEY_SET), ExtendType.MapKeySetToCol); + addHostSubsetRelation(baseContainerSet, getHostListOf(csAddedContainer, MAP_VALUES), ExtendType.MapValuesToCol); } - else if (hostMap.hasKind(MAP_0)) { - hostMap.getHostSetOf(MAP_0).forEach(host -> { - addSourceToHost(virtualArrayVar, host, "Map-Key"); - addSourceToHost(virtualArrayVar, host, "Map-Value"); - }); + // Map -> Map + else if (extendType == ExtendType.MapToMap) { + PointsToSet baseMapSet = getHostListOf(csBaseContainer, HostKind.MAP); + addHostSubsetRelation(baseMapSet, getHostListOf(csAddedContainer, HostKind.MAP), ExtendType.MapToMap); + } + // Map.keySet() -> Col + else if (extendType == ExtendType.MapKeySetToCol) { + PointsToSet baseContainerSet = getHostListOf(csBaseContainer, COL); + addHostSubsetRelation(baseContainerSet, getHostListOf(csAddedContainer, MAP_KEY_SET), ExtendType.MapKeySetToCol); } } - @Override - public void onNewCallEdge(Edge edge) { - if (edge.getKind() != CallKind.OTHER) { - CSMethod csCallee = edge.getCallee(); - Invoke callSite = edge.getCallSite().getCallSite(); - JMethod callee = csCallee.getMethod(); - InvokeExp invokeExp = callSite.getInvokeExp(); - if (invokeExp instanceof InvokeInstanceExp instanceExp) { - Var base = instanceExp.getBase(), thisVar = callee.getIR().getThis(); - CSVar csBase = csManager.getCSVar(emptyContext, base); - CSVar csThis = csManager.getCSVar(emptyContext, thisVar); - HostList hostMap = getHostListOf(csBase); - for (HostList.Kind k: SetKindsInMap) { - if (hostMap.hasKind(k)) { - solver.addHostEntry(csThis, k, hostMap.getHostSetOf(k)); - HostPropagater.put(new Pair<>(k, csBase), new Pair<>(k, csThis)); - } - } + private void addHostSubsetRelation(PointsToSet baseSet, PointsToSet addedSet, ExtendType extendType) { + for (CSObj csAddedObj: addedSet) { + // if added object is not modeled, then both base/added container variable should not be optimized by Cut-Shotrcut + if (config.isUnmodeledClass(csAddedObj.getObject().getType()) || taintHosts.contains(csAddedObj)) { + baseSet.forEach(this::taintHost); + break; } - if (containerConfig.isCorrelationExtender(callee)) - processCorrelationExtender(callSite, callee); - if (containerConfig.getArrayInitializer(callee) != null) - processArrayInitializer(callSite, callee); - for (int i = 0; i < invokeExp.getArgCount(); ++i) { - Var arg = invokeExp.getArg(i); - if (isConcerned(arg)) { - if (isRelatedEntranceInvoke(callSite) && invokeExp instanceof InvokeInstanceExp instanceExp) { - Var base = instanceExp.getBase(); - CSVar csBase = csManager.getCSVar(emptyContext, base); - int finalI = i; - getHostListOf(csBase).forEach((x, s) -> relateSourceToHosts(invokeExp, callee, finalI, s)); + baseSet.forEach(csBaseObj -> { + // can not add object to empty collection type + if (csBaseObj.getObject().getType().getName().contains("java.util.Collections$Empty")) + return; + switch (extendType) { + // Collection -> Collection + case ColToCol -> { + CSVar csAddedHostPointer = hostManager.getHostVar(csAddedObj, ColValue), + csBaseHostPointer = hostManager.getHostVar(csBaseObj, ColValue); + solver.addPFGEdge(csAddedHostPointer, csBaseHostPointer, FlowKind.SUBSET); + } + // Map -> Map + case MapToMap -> { + CSVar csAddedMapKeyHostPointer = hostManager.getHostVar(csAddedObj, MapKey), + csBaseMapKeyHostPointer = hostManager.getHostVar(csBaseObj, MapKey); + solver.addPFGEdge(csAddedMapKeyHostPointer, csBaseMapKeyHostPointer, FlowKind.SUBSET); + + CSVar csAddedMapValueHostPointer = hostManager.getHostVar(csAddedObj, MapValue), + csBaseMapValueHostPointer = hostManager.getHostVar(csBaseObj, MapValue); + solver.addPFGEdge(csAddedMapValueHostPointer, csBaseMapValueHostPointer, FlowKind.SUBSET); + } + // Map.keySet() -> Collection + case MapKeySetToCol -> { + CSVar csAddedMapKeyHostPointer = hostManager.getHostVar(csAddedObj, MapKey), + csBaseHostPointer = hostManager.getHostVar(csBaseObj, ColValue); + solver.addPFGEdge(csAddedMapKeyHostPointer, csBaseHostPointer, FlowKind.SUBSET); + } + // Map.values() -> Collection + case MapValuesToCol -> { + CSVar csAddedMapValueHostPointer = hostManager.getHostVar(csAddedObj, MapValue), + csBaseHostPointer = hostManager.getHostVar(csBaseObj, ColValue); + solver.addPFGEdge(csAddedMapValueHostPointer, csBaseHostPointer, FlowKind.SUBSET); } } - } - processCollectionOutInvoke(callSite, callee); + }); } } - private static JClass getOuterClass(JClass inner) { - if (inner != null) { - if (inner.hasOuterClass()) - inner = inner.getOuterClass(); - return inner; - } - return null; - } + /* + * process when container object is added into pts() + * @params csVar: + * @params pts: {, .., } added to pts() + */ + @Override + public void onNewPointsToSet(CSVar csVar, PointsToSet pts) { + PointsToSet mapSet = solver.makePointsToSet(), + colSet = solver.makePointsToSet(); + pts.forEach(csObj -> { // + // process when oi is container object + Type objType = csObj.getObject().getType(); // Type(oi) + switch (ClassificationOf(objType)) { + case MAP -> mapSet.addObject(csObj); + case COLLECTION -> colSet.addObject(csObj); + } - private static boolean isIteratorPollMethod(JMethod method) { - String sig = method.getSubsignature().toString(); - return containerConfig.isIteratorClass(method.getDeclaringClass()) && (sig.contains("next()") || - sig.contains("previous()")); + // if v is arguments of ArrayInitializer like ArrayList.(array), add PFG edge: --> + ArrayVarToVirtualArrayVar.get(csVar.getVar()).forEach(arrVar -> { + CSVar csArrVar = csManager.getCSVar(csVar.getContext(), arrVar); // + ArrayIndex arrayIndex = csManager.getArrayIndex(csObj); // + solver.addPFGEdge(arrayIndex, csArrVar, FlowKind.VIRTUAL_ARRAY, arrVar.getType()); // --> + }); + }); + // [MapHost], ptsH(, Map).update(MapSet) + if (!mapSet.isEmpty()) { + getHostListOf(csVar, HostKind.MAP).addAll(mapSet); + onNewHostEntry(csVar, mapSet, HostKind.MAP); + } + // [ColHost], ptsH(, Col).update(ColSet) + if (!colSet.isEmpty()) { + getHostListOf(csVar, COL).addAll(colSet); + onNewHostEntry(csVar, colSet, COL); + } } - private static boolean isEnumerationPollMethod(JMethod method) { - return isEnumerationClass(method.getDeclaringClass()) - && method.getSubsignature().toString().contains("nextElement()"); - } + /* + * @params csVar: + * @params entrySet: {, ... , } , new added to ptsH([host]) + * @params hostkind: type of host variable (MapKey, MapValue, ColValue, ColItr, etc.) + */ + public void onNewHostEntry(CSVar csVar, PointsToSet hostSet, HostKind hostKind) { + TransferAndMapEntryExit(csVar, hostSet, hostKind); + ProcessCachedExitInvokes(csVar, hostSet, hostKind); // process [HostTarget], propagate ptsH to exit variables + Context context = csVar.getContext(); // c + Var base = csVar.getVar(); // v + // process [HostSource] + base.getInvokes().forEach(invoke -> { + CSCallSite csCallSite = csManager.getCSCallSite(context, invoke); // + callGraph.getCalleesOf(csCallSite).forEach(csMethod -> { + Set> entranceInfo = config.getEntranceAppendIndex(csMethod.getMethod()); + // propagate ptsH to this variable of container class. + if (!entranceInfo.isEmpty()) + relateSourceToHosts(csCallSite, csMethod, entranceInfo, hostSet); + }); + }); - public static boolean CutReturnEdge(Invoke invoke, JMethod method) { - String methodKind = containerConfig.CategoryOfExit(method); - JClass calleeClass = method.getDeclaringClass(); - String signature = method.getSubsignature().toString(); - if (!Objects.equals(methodKind, "Other")) - return true; - if (invoke.getInvokeExp() instanceof InvokeInstanceExp e && !e.getBase().getName().equals("%this")) { - if (!isIteratorClass(invoke.getContainer().getDeclaringClass()) && - isMapEntryClass(calleeClass) && (signature.contains("getValue(") || signature.contains("getKey("))) { - return true; - } - if (isIteratorPollMethod(method)) - return true; - if (isEnumerationPollMethod(method)) { - Type outerType = getOuterClass(calleeClass).getType(); - return containerConfig.isHostType(outerType) || outerType.getName().equals("java.util.Collections"); - } + // \forall arr_l -> v, must be collection type + if (hostKind == COL) { + CollectionVarToVirtualArrayVar.get(base).forEach(arrVar -> { + CSVar csArrVar = csManager.getCSVar(context, arrVar); + hostSet.forEach(hostObj -> addSourceToHost(csArrVar, hostObj, ColValue)); + }); } - return false; - } - @Override - public void onNewMethod(JMethod method) { - method.getIR().forEach(s -> { - if (s instanceof New stmt) { - Obj obj = heapModel.getObj(stmt); - Type objType = obj.getType(); - JClass clz = method.getDeclaringClass(); - if (objType instanceof ClassType clzType && isMapEntryClass(clzType.getJClass())) { - containerConfig.getRelatedEntrySetClassesOf(clz).forEach(entry -> { - Var arg = VirtualArgsOfEntrySet.get(entry); - CSVar csArg = csManager.getCSVar(emptyContext, arg); - solver.addPointsTo(csArg, csManager.getCSObj(emptyContext, obj)); - }); - } - if (containerConfig.isHostType(objType)) { - Host newHost = null; - switch (ClassificationOf(objType)) { - case MAP -> newHost = containerConfig.getObjectHost(obj, Host.Classification.MAP); - case COLLECTION -> newHost = containerConfig.getObjectHost(obj, Host.Classification.COLLECTION); - } - JClass hostClass = ((ClassType) objType).getJClass(); - if (containerConfig.isEntrySetClass(hostClass)) { - Var arg = VirtualArgsOfEntrySet.get(hostClass); - addSourceToHost(arg, newHost, "Col-Value"); - } - if (containerConfig.isTaintAbstractListType(objType)) { - JMethod get = abstractListToGet.get(hostClass); - final Host temp = newHost; - get.getIR().getReturnVars().forEach(ret -> { - addSourceToHost(ret, temp, "Col-Value"); - }); - } - } - if (!method.isStatic()) { - CSVar csThis = csManager.getCSVar(emptyContext, method.getIR().getThis()); - CSVar csLHS = csManager.getCSVar(emptyContext, stmt.getLValue()); - if (containerConfig.isKeySetClass(objType.getName())) { - HostPropagater.put(new Pair<>(MAP_0, csThis), new Pair<>(MAP_KEY_SET, csLHS)); - } - if (containerConfig.isValueSetClass(objType.getName())) { - HostPropagater.put(new Pair<>(MAP_0, csThis), new Pair<>(MAP_VALUES, csLHS)); - } - } + // \forall v.addAll(v1) cached, since {, ...} added to ptsH(v), we need to update PFG xx -> according to ptsH(v1) + ExtenderBaseToAdded.get(csVar).forEach(addedContainer -> { // + CSVar csAddedContainer = csManager.getCSVar(csVar.getContext(), addedContainer); + // Collection/Map.keySet()/Map.values() -> Collection, where v can only be collection + if (hostKind == COL) { + addHostSubsetRelation(hostSet, getHostListOf(csAddedContainer, COL), ExtendType.ColToCol); + addHostSubsetRelation(hostSet, getHostListOf(csAddedContainer, MAP_KEY_SET), ExtendType.MapKeySetToCol); + addHostSubsetRelation(hostSet, getHostListOf(csAddedContainer, MAP_VALUES), ExtendType.MapValuesToCol); } + // Map -> Map, v can only be map + else if (hostKind == HostKind.MAP) + addHostSubsetRelation(hostSet, getHostListOf(csAddedContainer, HostKind.MAP), ExtendType.MapToMap); }); - } - private void processCollectionOutInvoke(Invoke callSite, JMethod callee) { - Var lhs = callSite.getLValue(); - String calleeSig = callee.getMethodSource().toString(); - if (lhs != null && isConcerned(lhs) && callSite.getInvokeExp() instanceof InvokeInstanceExp instanceExp) { - String methodKind = containerConfig.CategoryOfExit(callee); - Var base = instanceExp.getBase(); - CSVar csBase = csManager.getCSVar(emptyContext, base); - HostList hostMap = getHostListOf(csBase); - if (!Objects.equals(methodKind, "Other")) { - solver.addSelectedMethod(callee); - csVarMustRelatedInvokes.computeIfAbsent(csBase, v -> Sets.newSet()).add(new Pair<>(callSite, methodKind)); - if (Objects.equals(methodKind, "Col-Value")) { - if (hostMap.hasKind(COL_0)) { - Objects.requireNonNull(hostMap.getHostSetOf(COL_0)).forEach(host -> { - if (typeSystem.isSubtype(base.getType(), host.getType())) - checkHostRelatedExit(callSite, host, methodKind); - }); - } - } - else { - if (hostMap.hasKind(MAP_0)) { - hostMap.getHostSetOf(MAP_0).forEach(host -> { - if (typeSystem.isSubtype(base.getType(), host.getType())) - checkHostRelatedExit(callSite, host, methodKind); - }); - } - } - } - if (isMapEntryClass(callee.getDeclaringClass()) && hostMap.hasKind(MAP_ENTRY)) { - HostSet set = hostMap.getHostSetOf(MAP_ENTRY); - if (set != null) { - if (calleeSig.contains("getValue(")) { - solver.addSelectedMethod(callee); - set.forEach(host -> checkHostRelatedExit(callSite, host, "Map-Value")); - } - if (calleeSig.contains("getKey(")) { - solver.addSelectedMethod(callee); - set.forEach(host -> checkHostRelatedExit(callSite, host, "Map-Key")); - } - } - } - if (isIteratorClass(callee.getDeclaringClass()) && (calleeSig.contains("next()") || - calleeSig.contains("previous()"))) { - solver.addSelectedMethod(callee); - if (hostMap.hasKind(MAP_VALUE_ITR)) - hostMap.getHostSetOf(MAP_VALUE_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Value")); - if (hostMap.hasKind(MAP_KEY_ITR)) - hostMap.getHostSetOf(MAP_KEY_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Key")); - if (hostMap.hasKind(COL_ITR)) - hostMap.getHostSetOf(COL_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Col-Value")); - } - if (isEnumerationClass(callee.getDeclaringClass()) && calleeSig.contains("nextElement()")) { - solver.addSelectedMethod(callee); - if (hostMap.hasKind(MAP_VALUE_ITR)) - hostMap.getHostSetOf(MAP_VALUE_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Value")); - if (hostMap.hasKind(MAP_KEY_ITR)) - hostMap.getHostSetOf(MAP_KEY_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Map-Key")); - if (hostMap.hasKind(COL_ITR)) - hostMap.getHostSetOf(COL_ITR).forEach(host -> checkHostRelatedExit(callSite, host, "Col-Value")); - } - } - } + // \forall vc.addAll(v) cached, since {, ...} added to ptsH(v), we need to update PFG -> xx according to ptsH(vc) + ExtenderAddedToBase.get(csVar).forEach(baseContainer -> { // + CSVar csBaseContainer = csManager.getCSVar(csVar.getContext(), baseContainer); + // Collection -> Collection + if (hostKind == COL) + addHostSubsetRelation(getHostListOf(csBaseContainer, COL), hostSet, ExtendType.ColToCol); + // Map.keySet() -> Collection + else if (hostKind == MAP_KEY_SET) + addHostSubsetRelation(getHostListOf(csBaseContainer, COL), hostSet, ExtendType.MapKeySetToCol); + // Collection -> Collection + else if (hostKind == MAP_VALUES) + addHostSubsetRelation(getHostListOf(csBaseContainer, COL), hostSet, ExtendType.MapValuesToCol); + // Map -> Map, vc can only be map + else if (hostKind == HostKind.MAP) + addHostSubsetRelation(getHostListOf(csBaseContainer, HostKind.MAP), hostSet, ExtendType.MapToMap); + }); - private boolean isRelatedEntranceInvoke(Invoke invoke) { - return !containerConfig.isUnrelatedInInvoke(invoke); + HostPropagater.get(new Pair<>(hostKind, csVar)).forEach(kindCSVarPair -> + solver.addHostEntry(kindCSVarPair.second(), kindCSVarPair.first(), hostSet)); } - private Var getArgument(InvokeExp invokeExp, int index) { - return index == -1 ? ((InvokeInstanceExp) invokeExp).getBase() - : invokeExp.getArg(index); - } + /* + * @params csCallSite: , v is a container variable + * @params csCallee: , m is a container access method + * @params categoryWithindex: {, ...}. For example, Map.put(k, v) -> {, } + * @params hostSet: ptsH() => {, ... , } + */ + private void relateSourceToHosts(CSCallSite csCallSite, CSMethod csCallee, Set> categoryIndexPairs, PointsToSet hostSet) { + JMethod callee = csCallee.getMethod(); // m + ClassType classType = callee.getDeclaringClass().getType(); // container class type + solver.addSelectedMethod(callee); + for (Pair categoryIndexPair: categoryIndexPairs) { + Var argument = csCallSite.getCallSite().getInvokeExp().getArg(categoryIndexPair.second()); // source argument, l_ai + CSVar csArg = csManager.getCSVar(csCallSite.getContext(), argument); // + // filter: csObj must be subtype of container class type can be transfer to this variable + // do: generate source relation: <--category-- + hostSet.getObjects().stream().filter(csObj -> typeSystem.isSubtype(classType, csObj.getObject().getType())) + .forEach(csObj -> addSourceToHost(csArg, csObj, categoryIndexPair.first())); + } - private Set coes = Sets.newSet(); + } - private void processCorrelationExtender(Invoke callSite, JMethod callee) { - solver.addSelectedMethod(callee); - containerConfig.getCorrelationExtender(callee).forEach(indexPair -> { - if (callSite.getInvokeExp() instanceof InvokeInstanceExp instanceExp && !instanceExp.getBase().equals(callee.getIR().getThis())) { - coes.add(callee); - int largerIndex = indexPair.first(), smallerIndex = indexPair.second(); - Var arg1 = getArgument(instanceExp, largerIndex); - Var arg2 = getArgument(instanceExp, smallerIndex); - if (isConcerned(arg1) && isConcerned(arg2)) { - ExtenderLargerToSmaller.put(arg1, arg2); - ExtenderSmallerToLarger.put(arg2, arg1); - CSVar smallerPointer = csManager.getCSVar(emptyContext, arg2), largerPointer = csManager.getCSVar(emptyContext, arg1); - HostList largerMap = pointerHostListMap.get(largerPointer), smallerMap = pointerHostListMap.get(smallerPointer); - - if (largerMap.hasKind(COL_0)) { - HostSet set = largerMap.getHostSetOf(COL_0); - if (smallerMap.hasKind(COL_0)) - addHostSubsetRelation(set, Objects.requireNonNull(smallerMap.getHostSetOf(COL_0)), true, false, false); - if (smallerMap.hasKind(MAP_KEY_SET)) - addHostSubsetRelation(set, Objects.requireNonNull(smallerMap.getHostSetOf(MAP_KEY_SET)), false, true, false); - if (smallerMap.hasKind(MAP_VALUES)) - addHostSubsetRelation(set, Objects.requireNonNull(smallerMap.getHostSetOf(MAP_VALUES)), false, false, true); - } - if (largerMap.hasKind(MAP_0) && smallerMap.hasKind(MAP_0)) { - addHostSubsetRelation(largerMap.getHostSetOf(MAP_0), - Objects.requireNonNull(smallerMap.getHostSetOf(MAP_0)), true, false, false); - } + /* + * [TransferHost] + * @params csVar: + * @params containerType: type of container (Map or Collection) + * @params hostSet: {, ... , } , new added to ptsH([containerType]) + */ + private void TransferAndMapEntryExit(CSVar csVar, PointsToSet hostSet, HostKind hostKind) { + Var varBase = csVar.getVar(); // container variable v + Context context = csVar.getContext(); // c + varBase.getInvokes().forEach(invoke -> { // l: r = v.k(...) + Var lhs = invoke.getLValue(); // r + if (lhs == null || !isConcerned(lhs)) + return; + InvokeExp invokeExp = invoke.getInvokeExp(); // r = v.k(...) + String invokeString = invokeExp.getMethodRef().getName(); + CSVar csLHS = csManager.getCSVar(context, lhs); // + + // Transfer Method: List/Set.iterator, Map.entrySet, Map.keySet, Map.values, MapEntry,iterator, MapEntry.next, etc. + config.getTransferAPIs().forEach(((hostKindOri, methodStr, hostKindGen) -> { + if (hostKind == hostKindOri && invokeString.contains(methodStr)) + solver.addHostEntry(csLHS, hostKindGen, hostSet); + })); + + // Transfer Method: Vector.elements(), Hashtable.elements() + switch (hostKind) { + // Vector.elements() --> col_itr,注意其它list type没有elements方法 + case COL -> { + if (invokeString.equals("elements") && isVectorType(varBase.getType())) + solver.addHostEntry(csLHS, COL_ITR, hostSet); + } + // Hashtable.elements() --> map_value_itr, 注意其它map type没有elements方法 + case MAP -> { + if ((invokeString.equals("elements") && isHashtableType(varBase.getType()))) + solver.addHostEntry(csLHS, MAP_VALUE_ITR, hostSet); } } + + // Map-Entry Exit: map.entry.getKey(), map.entry.getValue() + config.getMapEntryExits().forEach((hostKindOri, methodStr, category) -> { + if (hostKind == hostKindOri && invokeString.contains(methodStr)) + hostSet.forEach(host -> + checkHostRelatedExit(csManager.getCSCallSite(context, invoke), host, category)); + }); }); } - private void addHostSubsetRelation(HostSet largerHosts, HostSet smallerHosts, boolean newHost, boolean keySet, boolean values) { - smallerHosts.forEach(host -> { - largerHosts.forEach(largerHost -> { - boolean taint = containerConfig.isTaintType(host.getType()); - if (!largerHost.getTaint() && !largerHost.getType().getName().contains("java.util.Collections$Empty")) { - if (!taint && !host.getTaint()) { - if (newHost) { - if (host.getClassification() == Host.Classification.COLLECTION - && largerHost.getClassification() == Host.Classification.COLLECTION) { - HostPointer smallerPointer = csManager.getHostPointer(host, "Col-Value"), - largerPointer = csManager.getHostPointer(largerHost, "Col-Value"); - solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); - } - if (host.getClassification() == Host.Classification.MAP - && largerHost.getClassification() == Host.Classification.MAP) { - HostPointer smallerPointer = csManager.getHostPointer(host, "Map-Key"), - largerPointer = csManager.getHostPointer(largerHost, "Map-Key"); - solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); - - smallerPointer = csManager.getHostPointer(host, "Map-Value"); - largerPointer = csManager.getHostPointer(largerHost, "Map-Value"); - solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); - } - } - if (keySet && host.getClassification() == Host.Classification.MAP - && largerHost.getClassification() == Host.Classification.COLLECTION) { - HostPointer smallerPointer = csManager.getHostPointer(host, "Map-Key"), - largerPointer = csManager.getHostPointer(largerHost, "Col-Value"); - solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); - } - if (values && host.getClassification() == Host.Classification.MAP - && largerHost.getClassification() == Host.Classification.COLLECTION) { - HostPointer smallerPointer = csManager.getHostPointer(host, "Map-Value"), - largerPointer = csManager.getHostPointer(largerHost, "Col-Value"); - solver.addPFGEdge(smallerPointer, largerPointer, FlowKind.SUBSET); - } - } - else { - taintHost(largerHost); - } - } + // process related target invoke: l: r = v.k() for + private void ProcessCachedExitInvokes(CSVar csVar, PointsToSet hostSet, HostKind hostKind) { + // container-exit like: r = v.get(..) + cachedContExitValues.get(csVar).forEach(invokeCategoryPair -> { // out invokes + Invoke callSite = invokeCategoryPair.first(); // l: r = v.k(...) + hostSet.forEach(host -> { + if (typeSystem.isSubtype(csVar.getVar().getType(), host.getObject().getType())) + checkHostRelatedExit(csManager.getCSCallSite(csVar.getContext(), callSite), host, invokeCategoryPair.second()); }); }); - } - private void propagateHostAndKind(CSVar csVar, HostSet hostSet, HostList.Kind kind) { - Var varBase = csVar.getVar(); - varBase.getInvokes().forEach(invoke -> { - Var lhs = invoke.getLValue(); - if (lhs != null && isConcerned(lhs)) { - InvokeExp invokeExp = invoke.getInvokeExp(); - String invokeString = invokeExp.getMethodRef().getName(); - CSVar csLHS = csManager.getCSVar(emptyContext, lhs); - ContainerConfig.getHostGenerators().forEach((kind_ori, keyString, kind_gen) -> { - if (kind == kind_ori && invokeString.contains(keyString)) { - solver.addHostEntry(csLHS, kind_gen, hostSet); - } - }); - ContainerConfig.getNonContainerExits().forEach((kind_required, invoke_str, category) -> { - if (kind == kind_required && invokeString.contains(invoke_str)) - hostSet.forEach(host -> checkHostRelatedExit(invoke, host, category)); - }); - switch (kind) { - case COL_0 -> { - if (invokeString.equals("elements") && isVectorType(varBase.getType())) { - solver.addHostEntry(csLHS, COL_ITR, hostSet); - } - } - case MAP_0 -> { - if ((invokeString.equals("elements") && isHashtableType(varBase.getType()))) { - solver.addHostEntry(csLHS, MAP_VALUE_ITR, hostSet); - } - } - } - } + // iterator-exit like: r = v.next(), here v is Iterator type, not Container type. Hence do not check type with typeSystem.isSubtype + cachedIterExitValues.get(csVar).forEach(invokeTrip -> { + // v could be COL_ITR/MAP_KEY_ITR/MAP_VALUE_ITR, so first we need to match + if (hostKind != invokeTrip.second()) + return; + Invoke callSite = invokeTrip.first(); // l: r = v.k(...)> + hostSet.forEach(host -> + checkHostRelatedExit(csManager.getCSCallSite(csVar.getContext(), callSite), host, invokeTrip.third())); }); } - private void ProcessMustRelatedInvokes(CSVar csVar, HostSet hostSet) { - Var varBase = csVar.getVar(); - csVarMustRelatedInvokes.computeIfAbsent(csVar, v -> Sets.newSet()).forEach(invokeCategoryPair -> { // out invokes - Var lhs = invokeCategoryPair.first().getLValue(); - if (lhs != null && isConcerned(lhs)) { - hostSet.forEach(host -> { - if (typeSystem.isSubtype(varBase.getType(), host.getType())) - checkHostRelatedExit(invokeCategoryPair.first(), host, invokeCategoryPair.second()); - }); - } + // for exit calledge e: => , \forall \in ptsH([hostKind]), add --exitCategory--> + private void addTargetToAllHost(CSVar csBase, HostKind hostKind, CSCallSite csCallSite, ContExitCategory exitCategory, boolean checkType) { + getHostListOf(csBase, hostKind).forEach(csObj -> { + if (!checkType || typeSystem.isSubtype(csBase.getType(), csObj.getObject().getType())) + checkHostRelatedExit(csCallSite, csObj, exitCategory); }); } - private void recoverCallSite(Invoke callSite) { - if (solver.addRecoveredCallSite(callSite)) { - CSCallSite csCallSite = csManager.getCSCallSite(emptyContext, callSite); - CSVar csLHS = csManager.getCSVar(emptyContext, callSite.getLValue()); - callGraph.getCalleesOf(csCallSite).forEach(csCallee -> { - JMethod callee = csCallee.getMethod(); - callee.getIR().getReturnVars().forEach(ret -> { - CSVar csRet = csManager.getCSVar(emptyContext, ret); - solver.addPFGEdge(csRet, csLHS, FlowKind.RETURN); - }); - }); + /* + * [HostTarget], --category--> + * @params csVar: + * @params csObj: + * @params category: category of host variable (MapKey, MapValue, ColValue) + */ + private void addTargetToHost(CSVar csVar, CSObj csObj, ContExitCategory category) { + if (hostManager.addHostTarget(csObj, category, csVar)) { + CSVar hostPointer = hostManager.getHostVar(csObj, category); // + solver.addPFGEdge(hostPointer, csVar, FlowKind.HOST_TO_RESULT); // result.getType(), } } - private void taintHost(Host host) { - if (!host.getTaint() && !containerConfig.isTaintAbstractListType(host.getType())) { - host.setTaint(); - hostToExits.get(host).forEach(this::recoverCallSite); - for (String cat: categories) { - csManager.getHostPointer(host, cat).getOutEdges().forEach(outEdge -> { - Pointer succ = outEdge.target(); - if (succ instanceof HostPointer hostPointer) { - taintHost(hostPointer.getHost()); - } - }); - } + /* + * [HostSource], <--category-- + * @params csVar: + * @params csObj: + * @params category: category of host variable (MapKey, MapValue, ColValue) + */ + private void addSourceToHost(CSVar csArg, CSObj csObj, ContExitCategory category) { + if (isConcerned(csArg.getVar()) && hostManager.addHostSource(csObj, category, csArg)) { + CSVar hostPointer = hostManager.getHostVar(csObj, category); // + solver.addPFGEdge(csArg, hostPointer, FlowKind.ARG_TO_HOST); } } - private void checkHostRelatedExit(Invoke callSite, Host host, String category) { - Var lhs = callSite.getLValue(); - if (lhs != null && isConcerned(lhs) && !solver.isRecoveredCallSite(callSite)) { - if (host.getTaint()) - recoverCallSite(callSite); - else { - hostToExits.put(host, callSite); - addTargetToHost(lhs, host, category); - } - } - + // get ptsH for a pointer, usually pointer is a container variable csVar: . + // Sometimes it could be a InstanceField like .list + public PointsToSet getHostListOf(Pointer pointer, HostKind hostKind) { + return pointerHostListMap.computeIfAbsent(pointer, hostKind, (v, t) -> solver.makePointsToSet()); } - private void addTargetToHost(Var result, Host host, String category) { - if (result != null && isConcerned(result) && host.addOutResult(result, category)) { - HostPointer hostPointer = csManager.getHostPointer(host, category); - CSVar target = csManager.getCSVar(emptyContext, result); - solver.addPFGEdge(hostPointer, target, //result.getType(), - FlowKind.HOST_TO_RESULT); + // propagate ptsH along PFG edge + @Override + public void onNewPFGEdge(PointerFlowEdge edge) { + Pointer source = edge.source(), target = edge.target(); + if (solver.needPropagateHost(edge)) { + pointerHostListMap.getOrDefault(source, Collections.emptyMap()).forEach(( + (hostKind, sourcePtsH) -> solver.addHostEntry(target, hostKind, sourcePtsH))); } } - private void relateSourceToHosts(InvokeExp invokeExp, JMethod callee, int index, HostSet hostSet) { - ClassType classType; - for (String category: categories) { - if ((classType = containerConfig.getTypeConstraintOf(callee, index, category)) != null) { - solver.addSelectedMethod(callee); - Var argument = invokeExp.getArg(index); - ClassType type = classType; - hostSet.hosts().filter(d -> !d.getTaint() && typeSystem.isSubtype(type, d.getType())) - .forEach(d -> addSourceToHost(argument, d, category)); + // process l: r = v.k(...) => m + public static boolean CutReturnEdge(Var lhs, JMethod callee) { + // if m is a container-exit method, cut m_ret --> r + if (ContainerConfig.config.getContainerExitCategory(callee) != null) + return true; + // if m is a iterator.next method, cut m_ret --> r, + // here MapEntryVar is to make sure iterator is not Map.entrySet().iterator(), because that next() is Transfer not Exit. + if (ContainerConfig.config.getIterExitCategory(callee) != null && MapEntryVar.contains(lhs)) + return true; + // if m is a modeled map.entry.getKey()/getValue(), cut m_ret --> r + if (ContainerConfig.config.isHostClass(getOuterClass(callee.getDeclaringClass())) + && isMapEntryClass(callee.getDeclaringClass()) + && (callee.getName().equals("getKey")) || callee.getName().equals("getValue")) + return true; + return false; + } + + + // For [Entrance-Extend] generated PFG edges: src --> dst, both src and dst are host variables for container objects. + // If src may point to a object whose type is not modeled, those host var should be conservatively handled. + private void taintHost(CSObj hostObj) { + if (!taintHosts.contains(hostObj)) { + taintHosts.add(hostObj); + hostToExits.get(hostObj).forEach(this::recoverCallSite); + for (ContExitCategory cat: ContExitCategory.values()) { + hostManager.getHostVar(hostObj, cat).getOutEdges().forEach(outEdge -> { + Pointer succ = outEdge.target(); + if (succ instanceof CSVar csVar) { + Obj targetHostObj = hostManager.getHostObj(csVar.getVar()); + if (targetHostObj != null) + taintHost(csManager.getCSObj(csVar.getContext(), targetHostObj)); + } + }); } } } - private void addSourceToHost(Var arg, Host host, String category) { - if (host.getTaint()) - return; - if (arg != null && isConcerned(arg) && host.addInArgument(arg, category)) { - CSVar source = csManager.getCSVar(emptyContext, arg); - HostPointer hostPointer = csManager.getHostPointer(host, category); - solver.addPFGEdge(source, hostPointer, FlowKind.ARG_TO_HOST); + // callsite l: r = v.get(), whose callee may be cut. However, since v may point-to a object whose type is not modeled + // the PFG from callee to r should be recovered to conservatively model + private void recoverCallSite(CSCallSite csCallSite) { + if (solver.addRecoveredCallSite(csCallSite)) { + CSVar csLHS = csManager.getCSVar(csCallSite.getContext(), csCallSite.getCallSite().getLValue()); + callGraph.getCalleesOf(csCallSite).forEach(csCallee -> { + JMethod callee = csCallee.getMethod(); + callee.getIR().getReturnVars().forEach(ret -> { + CSVar csRet = csManager.getCSVar(csCallee.getContext(), ret); // + solver.addPFGEdge(csRet, csLHS, FlowKind.RETURN); // --> + }); + }); } } - @Override - public void onNewPFGEdge(PointerFlowEdge edge) { - Pointer source = edge.source(), target = edge.target(); - FlowKind kind = edge.kind(); - if (solver.needPropagateHost(source, kind)) { - HostList hostMap = getHostListOf(source); - if (!hostMap.isEmpty()) - hostMap.forEach((k, set) -> solver.addHostEntry(target, k, set)); + // call edge e: => is a exit-method call-relation. + private void checkHostRelatedExit(CSCallSite csCallSite, CSObj hostObj, ContExitCategory category) { + Var lhs = csCallSite.getCallSite().getLValue(); // r + // this callsite can be soundly modeled by cut-shortcut + if (!solver.isRecoveredCallSite(csCallSite)) { + if (config.isUnmodeledClass(hostObj.getObject().getType()) || taintHosts.contains(hostObj)) + recoverCallSite(csCallSite); + else { + hostToExits.put(hostObj, csCallSite); + addTargetToHost(csManager.getCSVar(csCallSite.getContext(), lhs), hostObj, category); + } } } - public HostList getHostListOf(Pointer pointer) { - return pointerHostListMap.computeIfAbsent(pointer, v -> new HostList()); - } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java index fe092af92..61167eb2b 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/ContainerConfig.java @@ -1,428 +1,157 @@ package pascal.taie.analysis.pta.plugin.cutshortcut.container; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import pascal.taie.World; -import pascal.taie.analysis.pta.core.heap.Obj; -import pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList; -import pascal.taie.ir.stmt.Invoke; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.*; import pascal.taie.language.classes.ClassHierarchy; import pascal.taie.language.classes.JClass; import pascal.taie.language.classes.JMethod; import pascal.taie.language.type.ClassType; import pascal.taie.language.type.Type; -import pascal.taie.util.AnalysisException; -import pascal.taie.util.Indexer; -import pascal.taie.util.collection.Maps; -import pascal.taie.util.collection.MultiMap; -import pascal.taie.util.collection.Pair; -import pascal.taie.util.collection.Sets; -import pascal.taie.util.collection.TwoKeyMap; - -import java.util.Collections; +import pascal.taie.util.collection.*; + import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.stream.Stream; -import static pascal.taie.analysis.pta.plugin.cutshortcut.container.ClassAndTypeClassifier.ClassificationOf; -import static pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap.HostList.Kind.*; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContExitCategory.MapKey; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContExitCategory.MapValue; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.HostKind.*; +import static pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.IterExitCategory.*; public class ContainerConfig { public static ContainerConfig config = new ContainerConfig(); - private static final Logger logger = LogManager.getLogger(ContainerConfig.class); - private final TwoKeyMap parameters = Maps.newTwoKeyMap(); - private final Map ParameterOfColValue = Maps.newMap(); - - private final Map ParameterOfMapValue = Maps.newMap(); - private final Map ParameterOfMapKey = Maps.newMap(); - - private final Set excludedContainers = Sets.newSet(); - private final Set keySet = Sets.newSet(); - private final Set valueSet = Sets.newSet(); - - private final Set unrelatedInInvokes = Sets.newSet(); - - private final Set OutMethodsOfMapKey = Sets.newSet(); - private final Set OutMethodsOfMapValue = Sets.newSet(); - private final Set OutMethodsOfColValue = Sets.newSet(); - - private final Set iteratorClasses = Sets.newSet(); - - private final HostManager hostManager = new HostManager(); - - /** - * Map correlation-extending JMethods to their parameters - */ - private final MultiMap> corExtenders = Maps.newMultiMap(); // parameters in methods which are similar to "putAll"/"addAll" - - private final Map> arrayInitializer = Maps.newMap(); private static final ClassHierarchy hierarchy = World.get().getClassHierarchy(); - private static final TwoKeyMap hostGenerators = Maps.newTwoKeyMap(); - - private final Set hostClasses = Sets.newSet(); - - // r = x.foo(...) - // TwoKeyMap pair (type in [Map-Key, Map-Value, Col-Value] - // if x has containerKind kind and foo has a substring (specified in the map), then r will be added to the result set - // (of type) of x - private static final TwoKeyMap NonContainerExits = Maps.newTwoKeyMap(); - - private final MultiMap allocSiteOfEntrySet = Maps.newMultiMap(); - - private final Set taintClasses = Sets.newSet(); - - private final Set taintAbstractListClasses = Sets.newSet(); - - static { // initialize hostGenerators (host-passer means pass a host to lhs at an invoke when the base-variable has a required kind) - hostGenerators.put(COL_0, "iterator", COL_ITR); - hostGenerators.put(COL_0, "Iterator", COL_ITR); - hostGenerators.put(MAP_0, "entrySet", MAP_ENTRY_SET); - hostGenerators.put(MAP_0, "keySet", MAP_KEY_SET); - hostGenerators.put(MAP_0, "KeySet", MAP_KEY_SET); - hostGenerators.put(MAP_0, "values", MAP_VALUES); - hostGenerators.put(MAP_0, "Entry", MAP_ENTRY); - hostGenerators.put(MAP_0, "keys", MAP_KEY_ITR); - hostGenerators.put(MAP_ENTRY_SET, "iterator", MAP_ENTRY_ITR); - hostGenerators.put(MAP_VALUES, "iterator", MAP_VALUE_ITR); - hostGenerators.put(MAP_KEY_SET, "iterator", MAP_KEY_ITR); - hostGenerators.put(MAP_ENTRY_ITR, "next", MAP_ENTRY); - - NonContainerExits.put(MAP_ENTRY, "getValue", "Map-Value"); - NonContainerExits.put(MAP_ENTRY, "getKey", "Map-Key"); - NonContainerExits.put(MAP_KEY_ITR, "next", "Map-Key"); - NonContainerExits.put(MAP_KEY_ITR, "nextElement", "Map-Key"); - NonContainerExits.put(MAP_VALUE_ITR, "next", "Map-Value"); - NonContainerExits.put(MAP_VALUE_ITR, "nextElement", "Map-Value"); - NonContainerExits.put(COL_ITR, "next", "Col-Value"); - NonContainerExits.put(COL_ITR, "nextElement", "Col-Value"); - NonContainerExits.put(COL_ITR, "previous", "Col-Value"); - } - - public boolean isTaintType(Type type) { - if (type instanceof ClassType classType) { -// return ClassificationOf(type) != ClassAndTypeClassifier.containerType.OTHER && classType.getJClass().isApplication(); - return taintClasses.contains(classType.getJClass()); - } - return false; - } - - public boolean isRealHostClass(JClass clz) { - return hostClasses.contains(clz); - } - - public boolean isTaintAbstractListType(Type type) { - if (type instanceof ClassType classType) { - return taintAbstractListClasses.contains(classType.getJClass()); - } - return false; - } - - public Stream taintAbstractListClasses() { - return taintAbstractListClasses.stream(); - } - - public void addHostClass(String clz) { - JClass c = hierarchy.getClass(clz); - if (c != null && !c.isAbstract()) - hostClasses.add(c); - } - - public void computeTaintClass() { - JClass c = hierarchy.getClass("java.util.Collection"), ca = hierarchy.getClass("java.util.AbstractList"); - hierarchy.getAllSubclassesOf(c).forEach(clz -> { - if (!clz.isAbstract() && !hostClasses.contains(clz) && !excludedContainers.contains(clz.getName())) { - if (hierarchy.isSubclass(ca, clz)) - taintAbstractListClasses.add(clz); - else - taintClasses.add(clz); - } - }); - c = hierarchy.getClass("java.util.Map"); - hierarchy.getAllSubclassesOf(c).forEach(clz -> { - if (!clz.isAbstract() && !hostClasses.contains(clz) && !excludedContainers.contains(clz.getName())) { - taintClasses.add(clz); - } - }); -// taintClasses.forEach(System.out::println); - } - - public static void setConfig(ContainerConfig config) { - ContainerConfig.config = config; - } - - public JMethod getMethod(String signature) { - return hierarchy.getMethod(signature); - } - - public Parameter getParameter(JMethod method, int index) { - if (parameters.get(method, index) == null) { - parameters.put(method, index, new Parameter(method, index)); - } - return parameters.get(method, index); - } - - public void addParameterWithType(Parameter parameter, ClassType classType, String type) { - if (classType == null) - return; - switch (type) { - case "Map-Key" -> { - if (ParameterOfMapKey.get(parameter) != null) - throw new AnalysisException("Multiple classType for parameter: " + parameter); - ParameterOfMapKey.put(parameter, classType); - } - case "Map-Value" -> { - if (ParameterOfMapValue.get(parameter) != null) { - throw new AnalysisException("Multiple classType for parameter: " + parameter); - } - ParameterOfMapValue.put(parameter, classType); - } - case "Col-Value" -> { - if (ParameterOfColValue.get(parameter) != null) - throw new AnalysisException("Multiple classType for parameter: " + parameter); - ParameterOfColValue.put(parameter, classType); - } - } - } - - public void addInParameter(String SMethod, int index, String type) { - JMethod method = hierarchy.getMethod(SMethod); - if (method == null) { - return; - } - Parameter parameter = getParameter(method, index); - ClassType classType = method.getDeclaringClass().getType(); - addInParameter(parameter, classType, type); - } - - public void addInParameter(String SMethod, int index, String type, String jClass) { - JMethod method = hierarchy.getMethod(SMethod); - if (method == null) { - throw new AnalysisException("Invalid Input Method!"); - } - Parameter parameter = getParameter(method, index); - ClassType classType = Objects.requireNonNull(hierarchy.getClass(jClass)).getType(); - addInParameter(parameter, classType, type); - } + // main modeled collection/map classes + private final MultiMap hostClasses = Maps.newMultiMap(); + // other abstract collection/map classes + private final MultiMap unmodeledClasses = Maps.newMultiMap(); - public void excludeClass(String clz) { - excludedContainers.add(clz); - } - - public void addKeySetClass(String clz) { - keySet.add(clz); - } - - public void addValueSetClass(String clz) { - valueSet.add(clz); - } - - public boolean isKeySetClass(String clz) { - return keySet.contains(clz); - } + // Entrance Append method to argument index: m -> k, store for methods like List.add(k), Map.put(k, v) + private final MultiMap> EntranceAppendMap = Maps.newMultiMap(); - public boolean isValueSetClass(String clz) { - return valueSet.contains(clz); - } + // Entrance Extend method to argument index: m -> k, store for methods like List.addAll(c), Map.putAll(m) + private final Map> EntranceExtendMap = Maps.newMap(); - private void addInParameter(Parameter parameter, ClassType classType, String type) { - switch(type) { - case "Map-Key", "Map-Value", "Col-Value" -> addParameterWithType(parameter, classType, type); - default -> throw new AnalysisException("No such parameters!"); - } - } + // Exit method to category: m -> c, c could be one of [Map-Key, Map-Value, Col-Value] + private final Map ContainerExitMap = Maps.newMap(); + // Exit method - Iterator.previous()/next()/nextElement() + // Here call-relation between Map.Entry.getKey/getValue() not resolved by Tai-e, hence not modeled here. + private final Map IterExitMap = Maps.newMap(); - public ClassType getTypeConstraintOf(JMethod method, int index, String type) { - Parameter parameter = getParameter(method, index); - switch (type) { - case "Map-Key" -> { - return ParameterOfMapKey.get(parameter); - } - case "Map-Value" -> { - return ParameterOfMapValue.get(parameter); - } - case "Col-Value" -> { - return ParameterOfColValue.get(parameter); - } - default -> { - return null; - } - } - } + private final TwoKeyMap TransferAPIs = Maps.newTwoKeyMap(); + private final TwoKeyMap MapEntryExits = Maps.newTwoKeyMap(); - public void addMapKeyExit(String... methods) { - for (String method: methods) - OutMethodsOfMapKey.add(getMethod(method)); - } + // for m: ArrayList., add 0 -> -1, meaning array of index 0 copied to base + private final Map> ArrayInitializer = Maps.newMap(); - public String CategoryOfExit(JMethod method) { - if (OutMethodsOfMapKey.contains(method)) - return "Map-Key"; - if (OutMethodsOfMapValue.contains(method)) - return "Map-Value"; - if (OutMethodsOfColValue.contains(method)) - return "Col-Value"; - return "Other"; + private ContainerConfig() { + initializeTransferAPIs(); } - public void addMapValueOutMethod(String... methods) { - for (String method: methods) - OutMethodsOfMapValue.add(getMethod(method)); - } + private void initializeTransferAPIs() { + TransferAPIs.put(COL, "iterator", COL_ITR); + TransferAPIs.put(COL, "Iterator", COL_ITR); + TransferAPIs.put(MAP, "entrySet", MAP_ENTRY_SET); + TransferAPIs.put(MAP, "keySet", MAP_KEY_SET); + TransferAPIs.put(MAP, "values", MAP_VALUES); + TransferAPIs.put(MAP, "Entry", MAP_ENTRY); + TransferAPIs.put(MAP, "keys", MAP_KEY_ITR); + TransferAPIs.put(MAP_ENTRY_SET, "iterator", MAP_ENTRY_ITR); + TransferAPIs.put(MAP_VALUES, "iterator", MAP_VALUE_ITR); + TransferAPIs.put(MAP_KEY_SET, "iterator", MAP_KEY_ITR); + TransferAPIs.put(MAP_ENTRY_ITR, "next", MAP_ENTRY); - public void addCollectionValueOutMethod(String... methods) { - for (String method: methods) - OutMethodsOfColValue.add(getMethod(method)); + MapEntryExits.put(MAP_ENTRY, "getValue", MapValue); + MapEntryExits.put(MAP_ENTRY, "getKey", MapKey); } - public void addCorrelationExtender(String inMethod, int index0, int index1) { - addCorrelationExtender(getMethod(inMethod), index0, index1); + public void addIterExitCategory(String methodSig, IterExitCategory category) { + JMethod method = hierarchy.getMethod(methodSig); + if (method != null) + IterExitMap.put(method, category); } - public void addCorrelationExtender(JMethod inMethod, int index0, int index1) { - if (inMethod != null) - corExtenders.put(inMethod, new Pair<>(index0, index1)); - } + public IterExitCategory getIterExitCategory(JMethod method) { return IterExitMap.getOrDefault(method, null); } - public void addArrayInitializer(String smethod, int index0, int index1) { - // index0: index of array variable, index1: index of Collection variable - JMethod method = getMethod(smethod); - assert method != null; - arrayInitializer.put(method, new Pair<>(index0, index1)); - } + public boolean isIteratorExitMethods(JMethod method) { return getIterExitCategory(method) == ColIter; } - public boolean isCorrelationExtender(JMethod inMethod) { - return corExtenders.get(inMethod).size() > 0; + public boolean isKeySetClass(Type type) { + if (type instanceof ClassType classType) + return hostClasses.get(ContainerType.KEYSET).contains(classType.getJClass()); + return false; } - public boolean isHostType(Type type) { - return ClassificationOf(type) != ClassAndTypeClassifier.containerType.OTHER && !excludedContainers.contains(type.getName()); + public boolean isValueSetClass(Type type) { + if (type instanceof ClassType classType) + return hostClasses.get(ContainerType.VALUES).contains(classType.getJClass()); + return false; } - public Host getObjectHost(Obj obj, Host.Classification classification) { - if (!isHostType(obj.getType())) { - logger.warn("{} is not an outer class!", obj.getType()); - return null; - } - else { - return hostManager.getHost(obj, classification); - } - + public void addEntranceAppendIndex(String methodSig, ContExitCategory category, Integer index) { + JMethod method = hierarchy.getMethod(methodSig); + if (method != null) + EntranceAppendMap.put(method, new Pair<>(category, index)); } - public int getHostCount() { - return hostManager.getHostCount(); - } + public Set> getEntranceAppendIndex(JMethod method) { return EntranceAppendMap.get(method); } - public void addUnrelatedInvoke(String invoke) { - unrelatedInInvokes.add(invoke); + public void addEntranceExtendIndex(String methodSig, ExtendType extendType, Integer index) { + JMethod method = hierarchy.getMethod(methodSig); + EntranceExtendMap.put(method, new Pair<>(extendType, index)); } - public boolean isUnrelatedInInvoke(Invoke invoke) { - return unrelatedInInvokes.contains(invoke.getContainer() + "/" + invoke.getIndex()); - } + public Pair getEntranceExtendIndex(JMethod method) { return EntranceExtendMap.getOrDefault(method, null); } - public Set> getCorrelationExtender(JMethod method) { - return corExtenders.get(method); + public void addContainerExitCategory(String methodSig, ContExitCategory category) { + JMethod method = hierarchy.getMethod(methodSig); + if (method != null) + ContainerExitMap.put(method, category); } - public Pair getArrayInitializer(JMethod method) { - return arrayInitializer.get(method); + public ContExitCategory getContainerExitCategory(JMethod method) { + return ContainerExitMap.getOrDefault(method, null); } - public static TwoKeyMap getHostGenerators() { - return hostGenerators; + public TwoKeyMap getTransferAPIs() { + return TransferAPIs; } - public static TwoKeyMap getNonContainerExits() { - return NonContainerExits; + public TwoKeyMap getMapEntryExits() { + return MapEntryExits; } - public void addIteratorClass(String clz) { - JClass iclz = hierarchy.getClass(clz); + public void addHostClass(ContainerType containerType, String clzName) { + JClass clz = hierarchy.getClass(clzName); if (clz != null) - iteratorClasses.add(iclz); - } - - public boolean isIteratorClass(JClass clz) { - return iteratorClasses.contains(clz); - } - - public void addAllocationSiteOfEntrySet(String entrySet, String allocClass) { - JClass c1 = hierarchy.getClass(entrySet), c2 = hierarchy.getClass(allocClass); - if (c1 == null || c2 == null) { -// logger.warn("Invalid info about EntrySet: {} and {}", entrySet, allocClass); - } - else - addAllocationSiteOfEntrySet(c1, c2); + hostClasses.put(containerType, clz); } - private void addAllocationSiteOfEntrySet(JClass entrySet, JClass allocClass) { - // An EntrySet Class may have several allocation Sites which allocate its elements - // We only specified the declaring class of the container of allocation Sites. - allocSiteOfEntrySet.put(allocClass, entrySet); - entrySetClasses.add(entrySet); - } + public boolean isHostClass(JClass clz) { return hostClasses.values().contains(clz); } - public Set getRelatedEntrySetClassesOf(JClass allocClass) { - return allocSiteOfEntrySet.get(allocClass); + public void resolveUnmodeledClasses() { + hierarchy.getAllSubclassesOf(hierarchy.getClass("java.util.Collection")).forEach(clz -> { + if (!clz.isAbstract() && !hostClasses.values().contains(clz)) + unmodeledClasses.put(ContainerType.COLLECTION, clz); + }); + hierarchy.getAllSubclassesOf(hierarchy.getClass("java.util.Map")).forEach(clz -> { + if (!clz.isAbstract() && !hostClasses.values().contains(clz)) + unmodeledClasses.put(ContainerType.MAP, clz); + }); } - private final Set entrySetClasses = Sets.newSet(); - - public Set getAllEntrySetClasses() { - return Collections.unmodifiableSet(entrySetClasses); - } - public boolean isEntrySetClass(JClass clz) { - return entrySetClasses.contains(clz); + public boolean isUnmodeledClass(Type type) { + if (type instanceof ClassType classType) + return unmodeledClasses.values().contains(classType.getJClass()); + return false; } - public Indexer getHostIndexer() { - return hostManager; + public void addArrayInitializer(String smethod, int index0, int index1) { + // index0: index of array variable, index1: index of Collection variable + JMethod method = hierarchy.getMethod(smethod); + ArrayInitializer.put(method, new Pair<>(index0, index1)); } - private static class HostManager implements Indexer { - - private final Map objHostMap = Maps.newMap(); - - private Host[] hosts = new Host[8092]; - - private int counter = 0; - - Host getHost(Obj obj, Host.Classification classification) { - return objHostMap.computeIfAbsent(obj, o -> { - int index = counter ++; - Host host = new Host(o, index, classification); - storeHost(host, index); - return host; - }); - } - - private void storeHost(Host host, int index) { - if (index >= hosts.length) { - int newLength = Math.max(index + 1, (int) (hosts.length * 1.5)); - Host[] oldArray = hosts; - hosts = new Host[newLength]; - System.arraycopy(oldArray, 0, hosts, 0, oldArray.length); - } - hosts[index] = host; - } - - @Override - public int getIndex(Host o) { - return o.getIndex(); - } - - @Override - public Host getObject(int index) { - return hosts[index]; - } - - public int getHostCount() { - return counter; - } - } + public Pair getArrayInitializer(JMethod method) { return ArrayInitializer.get(method); } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java deleted file mode 100644 index 8448a3b0f..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Host.java +++ /dev/null @@ -1,84 +0,0 @@ -package pascal.taie.analysis.pta.plugin.cutshortcut.container; - -import pascal.taie.analysis.pta.core.heap.Obj; -import pascal.taie.ir.exp.Var; -import pascal.taie.language.type.Type; -import pascal.taie.util.AnalysisException; -import pascal.taie.util.collection.Maps; -import pascal.taie.util.collection.Sets; - -import java.util.Map; -import java.util.Set; - -public class Host { - private final Obj obj; - - private boolean taint; - - private final int index; - - private final Map> relatedArguments = Maps.newMap(); - - private final Map> relatedResults = Maps.newMap(); - - public enum Classification { - MAP, COLLECTION - } - - private Classification classification; - - public Host(Obj obj, int index, Classification classification) { - this.obj = obj; - this.index = index; - this.classification = classification; - taint = false; - relatedArguments.put("Map-Value", Sets.newSet()); - relatedArguments.put("Map-Key", Sets.newSet()); - relatedArguments.put("Col-Value", Sets.newSet()); - relatedResults.put("Map-Value", Sets.newSet()); - relatedResults.put("Map-Key", Sets.newSet()); - relatedResults.put("Col-Value", Sets.newSet()); - } - - public void setTaint() { - taint = true; - } - - public boolean getTaint() { - return taint; - } - - public int getIndex() { - return index; - } - - public boolean addInArgument(Var var, String category) { - Set arguments = relatedArguments.get(category); - if (arguments == null) - throw new AnalysisException("Invalid Category!"); - return arguments.add(var); - } - - - public boolean addOutResult(Var var, String category) { - Set results = relatedResults.get(category); - return results != null && results.add(var); - } - - public Obj getObject() { - return obj; - } - - public Type getType() { - return obj.getType(); - } - - public Classification getClassification() { - return classification; - } - - @Override - public String toString() { - return "Host-Object{" + obj.toString() + "}"; - } -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostManager.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostManager.java new file mode 100644 index 000000000..cd4d43f32 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostManager.java @@ -0,0 +1,55 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container; + +import pascal.taie.analysis.pta.core.cs.element.CSManager; +import pascal.taie.analysis.pta.core.cs.element.CSObj; +import pascal.taie.analysis.pta.core.cs.element.CSVar; +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.plugin.cutshortcut.SpecialVariables; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContExitCategory; +import pascal.taie.ir.exp.Var; +import pascal.taie.util.collection.Maps; +import pascal.taie.util.collection.TwoKeyMultiMap; + +import java.util.Map; + +public class HostManager { + private final CSManager csManager; + + // assign a host variable for each container object + private final Map hostMap = Maps.newMap(); + // map host variable to host object + private final Map hostVarToObj = Maps.newMap(); + + // denotes [HostSource], mapping container object to set of host variables that container.entrance(v), where o \in pts(container) + private final TwoKeyMultiMap HostSourceMap = Maps.newTwoKeyMultiMap(); + + // denotes [HostTarget], mapping container object to set of host variables that v = container.exit(), where o \in pts(container) + private final TwoKeyMultiMap HostTargetMap = Maps.newTwoKeyMultiMap(); + + public HostManager(CSManager csManager) { + this.csManager = csManager; + } + + public CSVar getHostVar(CSObj containerCSObj, ContExitCategory category) { + Obj containerObj = containerCSObj.getObject(); + + Var hostVar = hostMap.computeIfAbsent(containerObj, obj -> { + String varName = "host of[" + obj.toString() + "]," + category.getCategory(); + Var newHostVar = new Var(obj.getContainerMethod().orElse(null), varName, containerObj.getType(), -1); + SpecialVariables.setVirtualVar(newHostVar); + hostVarToObj.put(newHostVar, obj); + return newHostVar; + }); + return csManager.getCSVar(containerCSObj.getContext(), hostVar); + } + + public boolean addHostSource(CSObj containerCSObj, ContExitCategory category, CSVar hostVar) { + return HostSourceMap.put(containerCSObj, category, hostVar); + } + + public boolean addHostTarget(CSObj containerCSObj, ContExitCategory category, CSVar hostVar) { + return HostTargetMap.put(containerCSObj, category, hostVar); + } + + public Obj getHostObj(Var var) { return hostVarToObj.getOrDefault(var, null); } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java deleted file mode 100644 index c26cc09d5..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/DelegateHostSet.java +++ /dev/null @@ -1,66 +0,0 @@ -package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; - -import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; -import pascal.taie.util.collection.SetEx; - -import java.util.Collections; -import java.util.Set; -import java.util.stream.Stream; - -abstract public class DelegateHostSet implements HostSet { - protected final SetEx set; - - DelegateHostSet(SetEx set) { - this.set = set; - } - - @Override - public boolean addHost(Host host) { - return set.add(host); - } - - @Override - public boolean addAll(HostSet hostSet) { - if (hostSet instanceof DelegateHostSet other) - return set.addAll(other.set); - else { - boolean changed = false; - for (Host h : hostSet) - changed |= addHost(h); - return changed; - } - } - - @Override - public boolean contains(Host host) { return set.contains(host); } - - @Override - public boolean isEmpty() { return set.isEmpty(); } - - @Override - public int size() { return set.size(); } - - @Override - public Set getHosts() { return Collections.unmodifiableSet(set); } - - @Override - public Stream hosts() { return set.stream(); } - - @Override - public void clear() { this.set.clear(); } - - @Override - public String toString() { return set.toString(); } - - @Override - public HostSet addAllDiff(HostSet hostSet) { - Set otherSet = hostSet instanceof DelegateHostSet other ? - other.set : hostSet.getHosts(); - return newSet(set.addAllDiff(otherSet)); - } - - @Override - public HostSet copy() { return newSet(set.copy()); } - - protected abstract HostSet newSet(SetEx set); -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java deleted file mode 100644 index d647ef8de..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostList.java +++ /dev/null @@ -1,61 +0,0 @@ -package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; - -import pascal.taie.analysis.pta.core.cs.element.Pointer; -import pascal.taie.util.collection.Maps; - -import javax.annotation.Nullable; -import java.util.Map; -import java.util.function.BiConsumer; - -public class HostList { - private final Map map = Maps.newMap(); - - public HostList() {} - - public boolean addHostSet(Kind kind, HostSet hostSet) { - if (map.get(kind) == null) { - map.put(kind, hostSet); - return true; - } - else { - return map.get(kind).addAll(hostSet); - } - } - - public HostSet addAllDiff(Kind kind, HostSet hostSet) { - if (map.get(kind) == null) { - HostSet set = hostSet.copy(); - map.put(kind, set); - return set; - } - return map.get(kind).addAllDiff(hostSet); - } - - public boolean hasKind(Kind kind) { - return map.containsKey(kind); - } - - @Nullable - public HostSet getHostSetOf(Kind kind) { - return map.get(kind); - } - - public boolean isEmpty() { - return map.isEmpty(); - } - - public void forEach(BiConsumer action) { - map.forEach(action); - } - - public enum Kind { - MAP_0, - MAP_ENTRY_SET, MAP_ENTRY_ITR, MAP_ENTRY, - MAP_KEY_SET, MAP_KEY_ITR, - MAP_VALUES, MAP_VALUE_ITR, - - COL_0, - COL_ITR, - ALL - } -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java deleted file mode 100644 index b2facb84a..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSet.java +++ /dev/null @@ -1,33 +0,0 @@ -package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; - -import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; -import pascal.taie.util.Copyable; - -import java.util.Iterator; -import java.util.Set; -import java.util.stream.Stream; - -public interface HostSet extends Iterable, Copyable { - - boolean addHost(Host host); - - boolean addAll(HostSet hostSet); - - HostSet addAllDiff(HostSet hostSet); - - boolean contains(Host host); - - boolean isEmpty(); - - int size(); - - Set getHosts(); - - Stream hosts(); - - default Iterator iterator() { - return getHosts().iterator(); - } - - void clear(); -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java deleted file mode 100644 index 74854097c..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HostSetFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; - -import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; -import pascal.taie.util.Indexer; - -public class HostSetFactory { - private final Indexer hostIndexer; - - public HostSetFactory(Indexer hostIndexer) { - this.hostIndexer = hostIndexer; - } - - public HostSet make() { - return new HybridBitHostSet(hostIndexer); - } - - public HostSet make(Host host) { - HostSet set = make(); - set.addHost(host); - return set; - } -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java deleted file mode 100644 index 2f03ec063..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/HostMap/HybridBitHostSet.java +++ /dev/null @@ -1,21 +0,0 @@ -package pascal.taie.analysis.pta.plugin.cutshortcut.container.HostMap; - -import pascal.taie.analysis.pta.plugin.cutshortcut.container.Host; -import pascal.taie.util.Indexer; -import pascal.taie.util.collection.HybridBitSet; -import pascal.taie.util.collection.SetEx; - -public class HybridBitHostSet extends DelegateHostSet { - public HybridBitHostSet(Indexer indexer) { - this(new HybridBitSet<>(indexer, true)); - } - - public HybridBitHostSet(SetEx set) { - super(set); - } - - @Override - protected HostSet newSet(SetEx set) { - return new HybridBitHostSet(set); - } -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java index 0511b1206..61c5da851 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/MakeDefaultContainerConfig.java @@ -1,148 +1,94 @@ package pascal.taie.analysis.pta.plugin.cutshortcut.container; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import pascal.taie.util.AnalysisException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContExitCategory; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ContainerType; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.ExtendType; +import pascal.taie.analysis.pta.plugin.cutshortcut.container.enums.IterExitCategory; -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; public class MakeDefaultContainerConfig { - private static final Logger logger = LogManager.getLogger(MakeDefaultContainerConfig.class); - - private static void readFile(String fileName, String id, ContainerConfig config) { - BufferedReader reader = null; + public static void make() { + ContainerConfig config = ContainerConfig.config; try { - reader = new BufferedReader(new FileReader("DefaultContainerConfig/" + fileName)); - String lineStr; - while ((lineStr = reader.readLine()) != null) { - if (lineStr.length() == 0) - continue; - switch (id) { - case "host" -> config.addHostClass(lineStr); - case "exclusion" -> { - int ind = lineStr.indexOf(' '); - if (ind != -1) { - String ks = lineStr.substring(0, ind), vs = lineStr.substring(ind + 1); - config.addKeySetClass(ks); - config.excludeClass(ks); - config.addValueSetClass(vs); - config.excludeClass(vs); - } - else - config.excludeClass(lineStr); - } - case "extender0" -> config.addCorrelationExtender(lineStr, -1, 0); - case "extender1" -> config.addCorrelationExtender(lineStr, -1, 1); // can be merged with the previous case - case "unrelatedInvoke" -> config.addUnrelatedInvoke(lineStr); - case "col-out" -> config.addCollectionValueOutMethod(lineStr); - case "map-val-out" -> config.addMapValueOutMethod(lineStr); - case "parameter" -> { - int space1 = lineStr.indexOf(' '), space2 = lineStr.lastIndexOf(' '); - String type = lineStr.substring(0, space1), - method = lineStr.substring(space1 + 1, space2), - sIndex = lineStr.substring(space2 + 1); - int index = Integer.parseInt(sIndex); - config.addInParameter(method, index, type); - } - case "paraWithConstraint" -> { - int space1 = lineStr.indexOf(' '), space3 = lineStr.lastIndexOf(' '), - space2 = lineStr.lastIndexOf('>') + 1; - String type = lineStr.substring(0, space1), - method = lineStr.substring(space1 + 1, space2), - sIndex = lineStr.substring(space2 + 1, space3), - cons = lineStr.substring(space3 + 1); - int index = Integer.parseInt(sIndex); - config.addInParameter(method, index, type, cons); - } - case "array-init" -> { - String[] info = lineStr.split("#"); - int index0 = Integer.parseInt(info[1]), index1 = Integer.parseInt(info[2]); - config.addArrayInitializer(info[0], index0, index1); - } - case "iter" -> config.addIteratorClass(lineStr); - case "entrySet" -> { - String[] info = lineStr.split("#"); - config.addAllocationSiteOfEntrySet(info[0], info[1]); - } - default -> throw new AnalysisException("No such id for assigned correlation!"); - } - } - reader.close(); - } catch (FileNotFoundException f) { - logger.error(f); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } - } + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - public static ContainerConfig make() { - ContainerConfig config = ContainerConfig.config; - // In a more expressive way, a table? - // For every object of an outer class type, a new determinant will be generated at its allocation Site. - readFile("hostClasses.txt", "host", config); - readFile("exclusion.txt", "exclusion", config); - readFile("CorrelationExtenders.txt", "extender0", config); - readFile("CorrelationExtenders1.txt", "extender1", config); - readFile("unrelatedInvokes.txt", "unrelatedInvoke", config); + // [Map/Col] classes + Map> rawHostClassesData = mapper.readValue( + Thread.currentThread().getContextClassLoader().getResourceAsStream("container-config/host-classes.yml"), + new TypeReference<>() {}); + rawHostClassesData.forEach((containerName, containerClasses) -> { + ContainerType containerType = ContainerType.getTypeName(containerName); + containerClasses.forEach(containerClassName -> + config.addHostClass(containerType, containerClassName)); + }); - config.addCorrelationExtender("(javax.security.auth.Subject,int,java.util.Set)>", -1, 2); - // destination is the larger one, which means source is a subset of it. + // [Other Class] + config.resolveUnmodeledClasses(); - readFile("InMethod.txt", "parameter", config); - readFile("InWithCons.txt", "paraWithConstraint", config); - readFile("CollectionOut.txt", "col-out", config); - readFile("MapValueOut.txt", "map-val-out", config); + // [Entrance-Append] + Map>> rawEntranceAppendDatas = mapper.readValue( + Thread.currentThread().getContextClassLoader().getResourceAsStream("container-config/entrance-append.yml"), + new TypeReference<>() {}); + rawEntranceAppendDatas.forEach((categoryName, APIInfos) -> { + ContExitCategory category = ContExitCategory.getCategory(categoryName); + APIInfos.stream().filter(Objects::nonNull).forEach(APIInfo -> { + String methodSig = (String) APIInfo.get("signature"); + Integer index = (Integer) APIInfo.get("index"); + config.addEntranceAppendIndex(methodSig, category, index); + }); + }); - readFile("iteratorClass.txt", "iter", config); - readFile("ArrayInitializer.txt", "array-init", config); + // [Entrance-Extend] + Map>> rawEntranceExtendDatas = mapper.readValue( + Thread.currentThread().getContextClassLoader().getResourceAsStream("container-config/entrance-extend.yml"), + new TypeReference<>() {}); + rawEntranceExtendDatas.forEach((extendTypeValue, APIInfos) -> { + ExtendType extendType = ExtendType.getExtendType(extendTypeValue); + APIInfos.stream().filter(Objects::nonNull).forEach(APIInfo -> { + String methodSig = (String) APIInfo.get("signature"); + Integer index = (Integer) APIInfo.get("index"); + config.addEntranceExtendIndex(methodSig, extendType, index); + }); - readFile("EntrySet.txt", "entrySet", config); + }); - // TODO: some set methods are also get methods - config.addMapKeyExit( - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ); - config.computeTaintClass(); - return config; + // [Entrance-Array Initializer] + Map>> rawArrayInitializerDatas = mapper.readValue( + Thread.currentThread().getContextClassLoader().getResourceAsStream("container-config/array-initializer.yml"), + new TypeReference<>() {}); + rawArrayInitializerDatas.get("ArrayInit").forEach(APIInfo -> { + String methodSig = (String) APIInfo.get("signature"); + Integer srcIndex = (Integer) APIInfo.get("src"); + Integer dstIndex = (Integer) APIInfo.get("dst"); + config.addArrayInitializer(methodSig, srcIndex, dstIndex); + }); + + // [Exit] + Map> rawExitDatas = mapper.readValue( + Thread.currentThread().getContextClassLoader().getResourceAsStream("container-config/exit.yml"), + new TypeReference<>() {}); + rawExitDatas.forEach((exitType, methodsigs) -> { + ContExitCategory category = ContExitCategory.getCategory(exitType); + IterExitCategory iterExitCategory = IterExitCategory.getCategory(exitType); + // Container-Exit + if (category != null) + methodsigs.stream().filter(Objects::nonNull).forEach(methodsig -> config.addContainerExitCategory(methodsig, category)); + // Iter-Exit + else if (iterExitCategory != null) + methodsigs.stream().filter(Objects::nonNull).forEach(methodsig -> config.addIterExitCategory(methodsig, iterExitCategory)); + }); + + } + catch (IOException e) { + e.printStackTrace(); + } } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java deleted file mode 100644 index 6deb44bb3..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/Parameter.java +++ /dev/null @@ -1,10 +0,0 @@ -package pascal.taie.analysis.pta.plugin.cutshortcut.container; - -import pascal.taie.language.classes.JMethod; - -public record Parameter(JMethod method, int index) { - @Override - public String toString() { - return "Parameter[" + index + "@" + method + "]"; - } -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContExitCategory.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContExitCategory.java new file mode 100644 index 000000000..21cd3838b --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContExitCategory.java @@ -0,0 +1,27 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.enums; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum ContExitCategory { + MapKey("MapKey"), + MapValue("MapValue"), + ColValue("ColValue"); + + private final String category; + + ContExitCategory(String category) { + this.category = category; + } + + public String getCategory() { + return category; + } + + private final static Map NameToCateGory = Arrays.stream(values()) + .collect(Collectors.toMap(c -> c.category, Function.identity())); + + public static ContExitCategory getCategory(String Name) { return NameToCateGory.getOrDefault(Name, null); } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContainerType.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContainerType.java new file mode 100644 index 000000000..b055f35e2 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ContainerType.java @@ -0,0 +1,31 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.enums; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum ContainerType { + MAP("Map"), + COLLECTION("Col"), + ENTRYSET("EntrySet"), // entrySet of map + KEYSET("KeySet"), // keySet of map + VALUES("Values"), // values of map + ITER("Iter"), + OTHER("OTHER"); + + private final String typeName; + + ContainerType(String typeName) { + this.typeName = typeName; + } + + public String getTypeName() { + return typeName; + } + + private final static Map NameToContainerType = Arrays.stream(values()) + .collect(Collectors.toMap(c -> c.typeName, Function.identity())); + + public static ContainerType getTypeName(String Name) { return NameToContainerType.getOrDefault(Name, null); } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ExtendType.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ExtendType.java new file mode 100644 index 000000000..01506d209 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/ExtendType.java @@ -0,0 +1,24 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.enums; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum ExtendType { + MapToMap("MapToMap"), // Map.putAll(Map) + ColToCol("ColToCol"), // Collection.addAll(Collection) + MapKeySetToCol("MapKeySetToCol"), // Collection.addAll(Map.keySet()) + MapValuesToCol("MapValuesToCol"); // Collection.addAll(Map.values()); + + private final String value; + + public String getValue() { return value; } + + ExtendType(String _value) { value = _value; } + + private final static Map ValueToExtendType = Arrays.stream(values()) + .collect(Collectors.toMap(c -> c.value, Function.identity())); + + public static ExtendType getExtendType(String value) { return ValueToExtendType.getOrDefault(value, null); } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/HostKind.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/HostKind.java new file mode 100644 index 000000000..18a1dea65 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/HostKind.java @@ -0,0 +1,26 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.enums; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum HostKind { + MAP("Map"), MAP_ENTRY("MapEntry"), + MAP_ENTRY_SET("MapEntrySet"), MAP_KEY_SET("MapKeySet"), MAP_VALUES("MapValues"), + MAP_KEY_ITR("MapKeyIter"), MAP_ENTRY_ITR("MapEntryIter"), MAP_VALUE_ITR("MapValueIter"), + + COL("Col"), + COL_ITR("ColIter"); + + private String kind; + + HostKind(String _kind) { kind = _kind; } + + public String getKind() { return kind; } + + private final static Map NameToHostKind = Arrays.stream(values()) + .collect(Collectors.toMap(h -> h.kind, Function.identity())); + + public static HostKind getHostKind(String Name) { return NameToHostKind.get(Name); } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/IterExitCategory.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/IterExitCategory.java new file mode 100644 index 000000000..fc87e5e88 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/container/enums/IterExitCategory.java @@ -0,0 +1,27 @@ +package pascal.taie.analysis.pta.plugin.cutshortcut.container.enums; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum IterExitCategory { + ColIter("ColIter"), + MapGetValue("MapGetValue"), + MapGetKey("MapGetKey"); + + private final String category; + + IterExitCategory(String category) { + this.category = category; + } + + public String getCategory() { + return category; + } + + private final static Map NameToCateGory = Arrays.stream(values()) + .collect(Collectors.toMap(c -> c.category, Function.identity())); + + public static IterExitCategory getCategory(String Name) { return NameToCateGory.getOrDefault(Name, null); } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java index 74db9084d..5bca090a5 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractLoadField.java @@ -11,12 +11,6 @@ public class AbstractLoadField extends FieldStmt { public AbstractLoadField(Var lvalue, FieldAccess rvalue, boolean terminate) { super(lvalue, rvalue); - - if (rvalue instanceof InstanceFieldAccess) { - Var base = ((InstanceFieldAccess) rvalue).getBase(); - base.addAbstractLoadField(this); - } - this.terminate = terminate; } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java index 9339f80e3..1546c5c12 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/AbstractStoreField.java @@ -9,10 +9,6 @@ public class AbstractStoreField extends FieldStmt { public AbstractStoreField(FieldAccess lvalue, Var rvalue) { super(lvalue, rvalue); - if (lvalue instanceof InstanceFieldAccess) { - Var base = ((InstanceFieldAccess) lvalue).getBase(); - base.addAbstractStoreField(this); - } } @Override diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java index 5106a5878..d907e2536 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/FieldAccessHandler.java @@ -28,8 +28,7 @@ import pascal.taie.util.collection.MultiMap; import pascal.taie.util.collection.Sets; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import static pascal.taie.analysis.pta.core.solver.CutShortcutSolver.isConcerned; @@ -38,11 +37,15 @@ import static pascal.taie.analysis.pta.plugin.cutshortcut.field.ParameterIndex.getRealParameterIndex; public class FieldAccessHandler implements Plugin { - private final MultiMap setStatements = Maps.newMultiMap(); + private final MultiMap setStatements = Maps.newMultiMap(); - private final MultiMap getStatements = Maps.newMultiMap(); + private final MultiMap getStatements = Maps.newMultiMap(); - private final Set bannedReturnVars = Sets.newSet(); + private final Set cutReturnVars = Sets.newSet(); + + private final Map> abstractLoadFields = Maps.newMap(); + + private final Map> abstractStoreFields = Maps.newMap(); private CutShortcutSolver solver; @@ -63,33 +66,8 @@ public void setSolver(Solver solver) { csManager = solver.getCSManager(); emptyContext = solver.getContextSelector().getEmptyContext(); } - else { + else throw new AnalysisException("Invalid Solver to " + getClass()); - } - } - - public boolean addSetStatement(JMethod container, ParameterIndex baseIndex, FieldRef fieldRef, ParameterIndex rhsIndex) { - SetStatement setStatement = new SetStatement(baseIndex, fieldRef, rhsIndex); - if (setStatements.get(container).contains(setStatement)) - return false; - setStatements.put(container, setStatement); - return true; - } - - public Set getSetStatementsOf(JMethod method) { // 得到一个方法里所有的SetStatement - return setStatements.get(method); - } - - public Set getGetStatementsOf(JMethod method) { - return getStatements.get(method); - } - - public boolean addGetStatement(JMethod container, int lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { - GetStatement getStatement = new GetStatement(lhsIndex, baseIndex, fieldRef); - if (getStatements.get(container).contains(getStatement)) - return false; - getStatements.put(container, getStatement); - return true; } @Override @@ -98,17 +76,19 @@ public void onNewPointsToSet(CSVar csVar, PointsToSet pts) { processAbstractInstanceStore(csVar, pts); } + // process abstract instance load: , \forall \in pts(), add --> private void processAbstractInstanceLoad(CSVar csVar, PointsToSet pts) { - Var base = csVar.getVar(); - base.getAbstractLoadFields().forEach(load -> { + Context baseContext = csVar.getContext(); // c + Var base = csVar.getVar(); // lhs + getAbstractLoadFields(base).forEach(load -> { Var lhs = load.getLValue(); JField field = load.getFieldRef().resolve(); if (isConcerned(lhs) && field != null) { - CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + CSVar csLHS = csManager.getCSVar(baseContext, lhs); pts.forEach(baseObj -> { if (typeSystem.isSubtype(field.getDeclaringClass().getType(), baseObj.getObject().getType())) { InstanceField instField = csManager.getInstanceField(baseObj, field); - solver.addPFGEdge(instField, csLHS, //lhs.getType(), + solver.addPFGEdge(instField, csLHS, // lhs.getType(), load.isNonRelay() ? FlowKind.NON_RELAY_GET : FlowKind.GET); } }); @@ -116,13 +96,15 @@ private void processAbstractInstanceLoad(CSVar csVar, PointsToSet pts) { }); } + // process abstract store: , \forall \in pts(), add --> private void processAbstractInstanceStore(CSVar csVar, PointsToSet pts) { + Context baseContext = csVar.getContext(); Var base = csVar.getVar(); - base.getAbstractStoreFields().forEach(store -> { + getAbstractStoreFields(base).forEach(store -> { Var rhs = store.getRValue(); JField field = store.getFieldRef().resolve(); if (isConcerned(rhs) && field != null) { - CSVar csRHS = csManager.getCSVar(emptyContext, rhs); + CSVar csRHS = csManager.getCSVar(baseContext, rhs); pts.forEach(baseObj -> { if (typeSystem.isSubtype(field.getDeclaringClass().getType(), baseObj.getObject().getType())) { InstanceField instField = csManager.getInstanceField(baseObj, field); @@ -133,167 +115,189 @@ private void processAbstractInstanceStore(CSVar csVar, PointsToSet pts) { }); } + // [CutStore], [CutPropLoad] @Override public void onNewMethod(JMethod method) { - if (!method.isAbstract()) { - IR methodIR = method.getIR(); - methodIR.forEach(stmt -> { - if (stmt.getDef().isPresent() && stmt.getDef().get() instanceof Var def) - setDefined(def); - }); - if (methodIR.getThis() != null) - setParameterIndex(methodIR.getThis(), THISINDEX); - List params = methodIR.getParams(); - for (int i = 0; i < params.size(); i ++) - setParameterIndex(params.get(i), getRealParameterIndex(i)); - - JClass declaringClass = method.getDeclaringClass(); - method.getIR().forEach(stmt -> { - if (!declaringClass.getName().equals("java.awt.Component") - && !declaringClass.getName().equals("javax.swing.JComponent") - && stmt instanceof LoadField load - && load.getFieldAccess() instanceof InstanceFieldAccess fieldAccess) { - // x = y.f; - Var x = load.getLValue(), y = fieldAccess.getBase(); - if (isConcerned(x)) { - int retIndex = ParameterIndex.GetReturnVariableIndex(x); - ParameterIndex baseIndex = getParameterIndex(y); - if (retIndex >= 0 && baseIndex != null && !isDefined(y)) { - disableRelay(load); - addBannedReturnVar(method, x); - solver.addGetStmtEntry(method, retIndex, baseIndex, load.getFieldRef()); - } + if (method.isAbstract()) + return; + IR methodIR = method.getIR(); + methodIR.forEach(stmt -> { + if (stmt.getDef().isPresent() && stmt.getDef().get() instanceof Var def) + setDefined(def); + }); + if (methodIR.getThis() != null) + setParameterIndex(methodIR.getThis(), THISINDEX); + List params = methodIR.getParams(); + for (int i = 0; i < params.size(); i++) + setParameterIndex(params.get(i), getRealParameterIndex(i)); + } + + @Override + public void onNewCSMethod(CSMethod csMethod) { + JMethod method = csMethod.getMethod(); + if (method.isAbstract()) + return; + JClass declaringClass = method.getDeclaringClass(); + method.getIR().forEach(stmt -> { + if (!declaringClass.getName().equals("java.awt.Component") + && !declaringClass.getName().equals("javax.swing.JComponent") + && stmt instanceof LoadField load + && load.getFieldAccess() instanceof InstanceFieldAccess fieldAccess) { + // m_ret(x) = m_pi.f; + Var x = load.getLValue(), y = fieldAccess.getBase(); + if (isConcerned(x)) { + int retIndex = ParameterIndex.GetReturnVariableIndex(x); + ParameterIndex baseIndex = getParameterIndex(y); + if (retIndex >= 0 && baseIndex != null && !isDefined(y)) { + // the load inst should be relayed + disableRelay(load); + addCutReturnVar(csMethod, x); + GetStatement getStatement = new GetStatement(retIndex, baseIndex, load.getFieldRef()); + solver.addGetStmtEntry(csMethod, getStatement); } } - else if (!callGraph.entryMethods().collect(Collectors.toSet()).contains(csManager.getCSMethod(emptyContext, method)) - && stmt instanceof StoreField store - && store.getFieldAccess() instanceof InstanceFieldAccess fieldAccess) { - // x.f = y; - Var x = fieldAccess.getBase(), y = store.getRValue(); - if (isConcerned(y)) { - ParameterIndex baseIndex = getParameterIndex(x), rhsIndex = getParameterIndex(y); - if (baseIndex != null && rhsIndex != null && !isDefined(x) && !isDefined(y)) { - solver.addIgnoredStoreField(store); - solver.addSetStmtEntry(method, store.getFieldRef(), baseIndex, rhsIndex); - } + } + // not entry method, each entry method is associated with empty context, so this condition can be used to filter non-entry method + else if (!callGraph.entryMethods().collect(Collectors.toSet()).contains(csManager.getCSMethod(emptyContext, method)) + && stmt instanceof StoreField store + && store.getFieldAccess() instanceof InstanceFieldAccess fieldAccess) { + // m_pi.f = m_pj; + Var x = fieldAccess.getBase(), y = store.getRValue(); + if (isConcerned(y)) { + ParameterIndex baseIndex = getParameterIndex(x), rhsIndex = getParameterIndex(y); + if (baseIndex != null && rhsIndex != null && !isDefined(x) && !isDefined(y)) { + solver.addCutStoreField(store); + SetStatement setStatement = new SetStatement(baseIndex, store.getFieldRef(), rhsIndex); + solver.addSetStmtEntry(csMethod, setStatement); } } - }); - } + } + }); } - public void onNewSetStatement(JMethod method, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) { - // when a new SetStatement is found - if (addSetStatement(method, baseIndex, fieldRef, rhsIndex)) { - callGraph.edgesInTo(csManager.getCSMethod(emptyContext, method)) - .forEach(edge -> { - processSetStatementOnCallEdge(edge, fieldRef, baseIndex, rhsIndex); - }); - } + /* + * work when l first appears in + * @param csMethod: , + * @param setStmt: l: m_pi.f = m_pj \in m + */ + public void onNewSetStatement(CSMethod csMethod, SetStatement setStmt) { + if (addSetStatement(csMethod, setStmt)) + callGraph.edgesInTo(csMethod).forEach( + edge -> processSetStatementOnCallEdge(edge, setStmt)); } - public void processSetStatementOnNewCallEdge(Edge edge) { - // when a new call edge found, process callSite with previous found SetStatement in the callee - JMethod callee = edge.getCallee().getMethod(); - getSetStatementsOf(callee) - .forEach(setStatement -> processSetStatementOnCallEdge(edge, setStatement.fieldRef(), - setStatement.baseIndex(), setStatement.rhsIndex())); + /* + * work when l first appears in + * @param csMethod: , + * @param getStmt: l: m_ret = m_pi.f \in m + */ + public void onNewGetStatement(CSMethod csMethod, GetStatement getStmt) { + // add deleted return vars (only do it when a new set statement is found) + if (addGetStatement(csMethod, getStmt)) + callGraph.edgesInTo(csMethod).forEach( + edge -> processGetStatementOnCallEdge(edge, getStmt)); } - private void processSetStatementOnCallEdge(Edge edge, FieldRef fieldRef, ParameterIndex baseIndex, ParameterIndex rhsIndex) { - Var base = ParameterIndex.getCorrespondingArgument(edge, baseIndex), // 得到SetStatement在callSite处的arg - rhs = ParameterIndex.getCorrespondingArgument(edge, rhsIndex); + /* + * [PropStore], generate temp store: or concrete abstract store: + * @param edge: --> , + * @param setStmt: m_pi.f = m_pj \in m + */ + private void processSetStatementOnCallEdge(Edge edge, SetStatement setStmt) { + Var base = ParameterIndex.getCorrespondingArgument(edge, setStmt.baseIndex()), // l_ai + rhs = ParameterIndex.getCorrespondingArgument(edge, setStmt.rhsIndex()); // l_aj if (base != null && rhs != null && isConcerned(rhs)) { - JMethod caller = base.getMethod(); - ParameterIndex baseIndexAtCaller = getParameterIndex(base), rhsIndexAtCaller = getParameterIndex(rhs); + CSMethod csCaller = edge.getCallSite().getContainer(); // + ParameterIndex baseIndexAtCaller = getParameterIndex(base), // l_ai \is m_pu of m_caller + rhsIndexAtCaller = getParameterIndex(rhs); // l_aj \is m_pv of m_caller if (baseIndexAtCaller != null && rhsIndexAtCaller != null && !isDefined(base) && !isDefined(rhs)) { solver.addSelectedMethod(edge.getCallee().getMethod()); - solver.addSetStmtEntry(caller, fieldRef, baseIndexAtCaller, rhsIndexAtCaller); + // setStmt: m_pu.f = m_pv + solver.addSetStmtEntry(csCaller, new SetStatement(baseIndexAtCaller, setStmt.fieldRef(), rhsIndexAtCaller)); } + // top-level temp store, generate a new store field in current context else - processNewAbstractStoreField(base, fieldRef, rhs); + processNewAbstractStoreField(edge.getCallSite().getContext(), base, setStmt.fieldRef(), rhs); } } - private void processNewAbstractStoreField(Var base, FieldRef fieldRef, Var rhs) { - CSVar csBase = csManager.getCSVar(emptyContext, base), csRHS = csManager.getCSVar(emptyContext, rhs); - JField field = fieldRef.resolve(); - new AbstractStoreField(new InstanceFieldAccess(fieldRef, base), rhs); - solver.getPointsToSetOf(csBase).forEach(csObj -> { - if (typeSystem.isSubtype(field.getDeclaringClass().getType(), csObj.getObject().getType())) - solver.addPFGEdge(csRHS, csManager.getInstanceField(csObj, field), FlowKind.SET, field.getType()); - }); - } - - public void onNewGetStatement(JMethod method, Integer lhsIndex, ParameterIndex baseIndex, FieldRef fieldRef) { - if (addGetStatement(method, lhsIndex, baseIndex, fieldRef)) { - // add deleted return vars (only do it when a new set statement is found) - for (Edge edge: callGraph.edgesInTo(csManager.getCSMethod(emptyContext, method)).toList()) { - processGetStatementOnCallEdge(edge, baseIndex, fieldRef); - } - } - } - - public void processGetStatementOnNewCallEdge(Edge edge) { - JMethod callee = edge.getCallee().getMethod(); - for (GetStatement get: getGetStatementsOf(callee)) - processGetStatementOnCallEdge(edge, get.baseIndex(), get.fieldRef()); - } - /** - * @param baseIndex base of the Set Statement, it should be a parameter of the callee, and thus has a parameter index - * @param fieldRef fieldRef of the Set Statement + * [CutPropLoad], add new return var m_ret, generate temp load: + * @param edge: --> + * @param getStmt: m_ret = m_pi.f \in m */ - private void processGetStatementOnCallEdge(Edge edge, ParameterIndex baseIndex, FieldRef fieldRef) { - Invoke callSite = edge.getCallSite().getCallSite(); - JMethod callee = edge.getCallee().getMethod(); - Var base = ParameterIndex.getCorrespondingArgument(edge, baseIndex), - lhs = callSite.getLValue(); - if (!ContainerAccessHandler.CutReturnEdge(callSite, callee)) { + private void processGetStatementOnCallEdge(Edge edge, GetStatement getStmt) { + // ToDo: consider context-sensitive setting + Invoke callSite = edge.getCallSite().getCallSite(); // l + JMethod callee = edge.getCallee().getMethod(); // m + Var base = ParameterIndex.getCorrespondingArgument(edge, getStmt.baseIndex()), // l_ai + lhs = callSite.getLValue(); // m_caller_ret + if (!ContainerAccessHandler.CutReturnEdge(lhs, callee)) { if (base != null && lhs != null && isConcerned(lhs)) { - JMethod caller = base.getMethod(); - int lhsIndexAtCaller = ParameterIndex.GetReturnVariableIndex(lhs); - ParameterIndex baseIndexAtCaller = getParameterIndex(base); + CSMethod csCaller = callGraph.getContainerOf(edge.getCallSite()); // + int lhsIndexAtCaller = ParameterIndex.GetReturnVariableIndex(lhs); // index of m_caller_ret + ParameterIndex baseIndexAtCaller = getParameterIndex(base); // index of l_ai solver.addSelectedMethod(edge.getCallee().getMethod()); + // m_caller_ret is return value of m_caller, l_ai is parameter/this of m_caller, [PropLoad] to caller of m_caller if (lhsIndexAtCaller != -1 && baseIndexAtCaller != null) { - addBannedReturnVar(caller, lhs); // 每一层GetStatement的retVar都需要删除 - solver.addGetStmtEntry(caller, lhsIndexAtCaller, baseIndexAtCaller, fieldRef); - processNewAbstractLoadField(lhs, base, fieldRef, false); - + addCutReturnVar(csCaller, lhs); // 每一层GetStatement的retVar都需要删除 + solver.addGetStmtEntry(csCaller, new GetStatement(lhsIndexAtCaller, baseIndexAtCaller, getStmt.fieldRef())); + // new generated PFG edges are non-relay + processNewAbstractLoadField(edge.getCallSite().getContext(), lhs, base, getStmt.fieldRef(), false); } + // top-level temp load, generate a new load: , new generated PFG edge is terminal(relayed) else - processNewAbstractLoadField(lhs, base, fieldRef, true); + processNewAbstractLoadField(edge.getCallSite().getContext(), lhs, base, getStmt.fieldRef(), true); } } } - private void processNewAbstractLoadField(Var lhs, Var base, FieldRef fieldRef, boolean terminate) { - CSVar csBase = csManager.getCSVar(emptyContext, base), csLHS = csManager.getCSVar(emptyContext, lhs); + // [ShortcutStore]: in context c, generate a new store field: + private void processNewAbstractStoreField(Context context, Var base, FieldRef fieldRef, Var rhs) { + CSVar csBase = csManager.getCSVar(context, base), // + csRHS = csManager.getCSVar(context, rhs); // + JField field = fieldRef.resolve(); + AbstractStoreField storeField = new AbstractStoreField(new InstanceFieldAccess(fieldRef, base), rhs); + getAbstractStoreFields(base).add(storeField); + // \forall \in pts(), add --> + solver.getPointsToSetOf(csBase).forEach(csObj -> { + if (typeSystem.isSubtype(field.getDeclaringClass().getType(), csObj.getObject().getType())) + solver.addPFGEdge(csRHS, csManager.getInstanceField(csObj, field), FlowKind.SET, field.getType()); + }); + } + + // [ShortcutLoad]: in context c, generate a new abstract load field: , terminate determines whether it is lhs = base.f terminal PFG edge + private void processNewAbstractLoadField(Context context, Var lhs, Var base, FieldRef fieldRef, boolean terminate) { + CSVar csBase = csManager.getCSVar(context, base), // + csLHS = csManager.getCSVar(context, lhs); // JField field = fieldRef.resolve(); - new AbstractLoadField(lhs, new InstanceFieldAccess(fieldRef, base), terminate); + AbstractLoadField loadField = new AbstractLoadField(lhs, new InstanceFieldAccess(fieldRef, base), terminate); + getAbstractLoadFields(base).add(loadField); + // \forall \in pts(), add PFG edge: --> solver.getPointsToSetOf(csBase).forEach(csObj -> { if (typeSystem.isSubtype(field.getDeclaringClass().getType(), csObj.getObject().getType())) - solver.addPFGEdge(csManager.getInstanceField(csObj, field), csLHS, //lhs.getType(), + solver.addPFGEdge(csManager.getInstanceField(csObj, field), csLHS, // lhs.getType(), terminate ? FlowKind.GET : FlowKind.NON_RELAY_GET); }); } + // [RelayEdge]: --> @Override public void onNewCallEdge(Edge edge) { - Invoke callSite = edge.getCallSite().getCallSite(); + Invoke callSite = edge.getCallSite().getCallSite(); // l processSetStatementOnNewCallEdge(edge); - Var lhs = callSite.getLValue(); + Var lhs = callSite.getLValue(); // r if (lhs != null && isConcerned(lhs)) { processGetStatementOnNewCallEdge(edge); - CSMethod csCallee = edge.getCallee(); - JMethod callee = csCallee.getMethod(); - CSVar csLHS = csManager.getCSVar(emptyContext, lhs); - if (!ContainerAccessHandler.CutReturnEdge(callSite, callee)) { + CSMethod csCallee = edge.getCallee(); // + JMethod callee = csCallee.getMethod(); // m + CSVar csLHS = csManager.getCSVar(edge.getCallSite().getContext(), lhs); // + if (!ContainerAccessHandler.CutReturnEdge(lhs, callee)) { for (Var ret: callee.getIR().getReturnVars()) { - if (bannedReturnVars.contains(ret)) { - CSVar csRet = csManager.getCSVar(emptyContext, ret); - csRet.getInEdges().forEach(inEdge -> { - // transfer + if (cutReturnVars.contains(ret)) { + CSVar csRet = csManager.getCSVar(csCallee.getContext(), ret); // + csRet.getInEdges().forEach(inEdge -> { // \forall --> + // transfer, add --> , inEdge.terminate = True if (inEdge.kind() != FlowKind.NON_RELAY_GET) solver.addPFGEdge(inEdge.source(), csLHS, inEdge.kind(), inEdge.getTransfers()); }); @@ -303,37 +307,86 @@ public void onNewCallEdge(Edge edge) { } } + // [CutPropLoad]: --> , + private void processSetStatementOnNewCallEdge(Edge edge) { + // when a new call edge found, process callSite with previous found SetStatement in the callee + getSetStatementsOf(edge.getCallee()).forEach(setStmt -> + processSetStatementOnCallEdge(edge, setStmt)); + } + + // --> + private void processGetStatementOnNewCallEdge(Edge edge) { + getGetStatementsOf(edge.getCallee()).forEach(getStmt -> + processGetStatementOnCallEdge(edge, getStmt)); + } + + /* + * [RelayEdge] rules, a relay PFG edge: --> should be converted to --> + * @param edge: --> , edge is not terminal PFG + */ @Override public void onNewPFGEdge(PointerFlowEdge edge) { Pointer source = edge.source(), target = edge.target(); FlowKind kind = edge.kind(); - if (target instanceof CSVar csVar && bannedReturnVars.contains(csVar.getVar()) && kind != FlowKind.NON_RELAY_GET) { - CSMethod csMethod = csManager.getCSMethod(emptyContext, csVar.getVar().getMethod()); + // m_ret \in cutReturns, --> is a relay PFG edge + if (target instanceof CSVar csVar && cutReturnVars.contains(csVar.getVar()) && kind != FlowKind.NON_RELAY_GET) { + CSMethod csMethod = csManager.getCSMethod(csVar.getContext(), csVar.getVar().getMethod()); // c + // \forall --> , add --> callGraph.getCallersOf(csMethod).forEach(csCallSite -> { - Var lhs = csCallSite.getCallSite().getLValue(); + Var lhs = csCallSite.getCallSite().getLValue(); // r if (lhs != null && isConcerned(lhs)) { - CSVar csLHS = csManager.getCSVar(emptyContext, lhs); - solver.addPFGEdge(source, csLHS, kind, edge.getTransfers()); + CSVar csLHS = csManager.getCSVar(csCallSite.getContext(), lhs); + solver.addPFGEdge(source, csLHS, kind, edge.getTransfers()); // --> } }); } } - private void addBannedReturnVar(JMethod method, Var ret) { - bannedReturnVars.add(ret); - CSVar csRet = csManager.getCSVar(emptyContext, ret); - csRet.getInEdges().forEach(edge -> { + /* + * [RelayEdge]: --> translate to --> + * @param csMethod: , + * @param ret: m_ret \in cutReturns + */ + private void addCutReturnVar(CSMethod csMethod, Var ret) { + cutReturnVars.add(ret); + solver.addCutReturnVar(ret); + CSVar csRet = csManager.getCSVar(csMethod.getContext(), ret); // + csRet.getInEdges().forEach(edge -> { // --> if (edge.kind() != FlowKind.NON_RELAY_GET) { - callGraph.getCallersOf(csManager.getCSMethod(emptyContext, method)) - .forEach(csCallSite -> { - Var lhs = csCallSite.getCallSite().getLValue(); - if (lhs != null && isConcerned(lhs)) { - CSVar csLHS = csManager.getCSVar(emptyContext, lhs); - solver.addPFGEdge(edge.source(), csLHS, edge.kind(), edge.getTransfers()); - } - }); + // --> + callGraph.getCallersOf(csMethod).forEach(csCallSite -> { + Var lhs = csCallSite.getCallSite().getLValue(); // r + if (lhs != null && isConcerned(lhs)) { + CSVar csLHS = csManager.getCSVar(csCallSite.getContext(), lhs); // + solver.addPFGEdge(edge.source(), csLHS, edge.kind(), edge.getTransfers()); // --> + } + }); } }); - solver.addSpecialHandledRetVar(ret); + } + + private boolean addSetStatement(CSMethod csMethod, SetStatement setStatement) { + return setStatements.put(csMethod, setStatement); + } + + private boolean addGetStatement(CSMethod csMethod, GetStatement getStmt) { + return getStatements.put(csMethod, getStmt); + } + + private Set getSetStatementsOf(CSMethod csMethod) { // 得到一个方法里所有的SetStatement + return setStatements.get(csMethod); + } + + private Set getGetStatementsOf(CSMethod csMethod) { + return getStatements.get(csMethod); + } + + + private List getAbstractLoadFields(Var base) { + return abstractLoadFields.computeIfAbsent(base, v -> new ArrayList<>()); + } + + private List getAbstractStoreFields(Var base) { + return abstractStoreFields.computeIfAbsent(base, v -> new ArrayList<>()); } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java index d4312a913..088e85cf8 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/ParameterIndex.java @@ -8,6 +8,7 @@ import pascal.taie.ir.IR; import pascal.taie.ir.exp.InvokeExp; import pascal.taie.ir.exp.InvokeInstanceExp; +import pascal.taie.ir.exp.InvokeSpecial; import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.Invoke; import pascal.taie.util.AnalysisException; diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java index 27dc7b941..54ab7fe52 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/field/SetStatement.java @@ -2,6 +2,8 @@ import pascal.taie.ir.proginfo.FieldRef; +import java.util.Objects; + /** * {@link SetStatement}表示需要向前去寻找callSite,并把这种抽象的Set行为作用到callSite处的指针(参数)的语句 * 和之前的SetStmt不同,现在的{@link SetStatement}并不一定是简单的{@link pascal.taie.ir.stmt.StoreField}语句 diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java index 846e288b6..febec7692 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/cutshortcut/localflow/LocalFlowHandler.java @@ -36,38 +36,42 @@ public class LocalFlowHandler implements Plugin { private CSManager csManager; - private Context emptyContext; - private HeapModel heapModel; private int totIDMethod = 0; + // method -> set of parameter indices or new objects that are directly reach return vars without indirect-value flow private final MultiMap directlyReturnParams = Maps.newMultiMap(); public void setSolver(Solver solver) { if (solver instanceof CutShortcutSolver cutShortcutSolver) { this.solver = cutShortcutSolver; csManager = solver.getCSManager(); - emptyContext = solver.getContextSelector().getEmptyContext(); heapModel = solver.getHeapModel(); } else throw new AnalysisException("Invalid solver"); } + /* + * process for callsite r = v.k(...) + */ @Override public void onNewPointsToSet(CSVar csVar, PointsToSet pts) { Var base = csVar.getVar(); + Context varContext = csVar.getContext(); + // foreach r = v.k(...) for (Invoke callSite : base.getInvokes()) { - Var lhs = callSite.getLValue(); + Var lhs = callSite.getLValue(); // r if (lhs != null && isConcerned(lhs)) { - CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + CSVar csLHS = csManager.getCSVar(varContext, lhs); // pts.forEach(recvObj -> { + // resolve v.k based on Type(o) where \in newPts(v) JMethod callee = CallGraphs.resolveCallee( recvObj.getObject().getType(), callSite); - if (callee != null && directlyReturnParams.get(callee).contains(INDEX_THIS)) { + // if v -> r holds for this callee, add to newPts(r) + if (callee != null && directlyReturnParams.get(callee).contains(INDEX_THIS)) solver.addPointsTo(csLHS, recvObj); - } }); } } @@ -80,7 +84,7 @@ public void onNewMethod(JMethod method) { MultiMap result = getVariablesAssignedFromParameters(method); for (Var ret: retVars) { if (!result.get(ret).isEmpty()) { - totIDMethod ++; + totIDMethod++; break; } } @@ -88,7 +92,7 @@ public void onNewMethod(JMethod method) { if (retVars.contains(var)) { solver.addSelectedMethod(method); directlyReturnParams.put(method, index); - solver.addSpecialHandledRetVar(var); + solver.addCutReturnVar(var); } }); } @@ -97,21 +101,18 @@ public void onNewMethod(JMethod method) { private MultiMap getVariablesAssignedFromParameters(JMethod method) { MultiMap result = Maps.newMultiMap(); MultiMap definitions = Maps.newMultiMap(); - method.getIR().forEach(stmt -> { - stmt.getDef().ifPresent(def -> { - if (def instanceof Var varDef) { - definitions.put(varDef, stmt); - } - }); - }); + method.getIR().forEach(stmt -> stmt.getDef().ifPresent(def -> { + if (def instanceof Var varDef) + definitions.put(varDef, stmt); + })); for (int i = 0; i < method.getParamCount(); i++) { Var param = method.getIR().getParam(i); if (isConcerned(param)) { // parameter which is not redefined in the method body if (definitions.get(param).isEmpty() || definitions.get(param).stream().allMatch(stmt -> stmt instanceof New)) { result.put(param, new ParameterIndexOrNewObj(false, getRealParameterIndex(i), null)); - definitions.get(param).forEach(stmt -> { - result.put(param, new ParameterIndexOrNewObj(true, null, heapModel.getObj((New) stmt))); - }); + definitions.get(param).forEach(stmt -> result.put(param, + new ParameterIndexOrNewObj(true, null, heapModel.getObj((New) stmt))) + ); } } } @@ -168,34 +169,44 @@ else if (def instanceof New stmt) { return result; } + /* + * process --> + */ @Override public void onNewCallEdge(Edge edge) { - CSMethod csCallee = edge.getCallee(); - JMethod callee = csCallee.getMethod(); - CSCallSite csCallSite = edge.getCallSite(); - Invoke callSite = csCallSite.getCallSite(); - InvokeExp invokeExp = callSite.getInvokeExp(); - Var lhs = callSite.getLValue(); + CSMethod csCallee = edge.getCallee(); // + JMethod callee = csCallee.getMethod(); // m + Context calleeContext = csCallee.getContext(); // c' + CSCallSite csCallSite = edge.getCallSite(); // + Context callSiteContext = csCallSite.getContext(); // c + Invoke callSite = csCallSite.getCallSite(); // l + InvokeExp invokeExp = callSite.getInvokeExp(); // r = v.k(...) + Var lhs = callSite.getLValue(); // r if (lhs != null && isConcerned(lhs)) { - CSVar csLHS = csManager.getCSVar(emptyContext, lhs); + CSVar csLHS = csManager.getCSVar(callSiteContext, lhs); // directlyReturnParams.get(callee).forEach(indexOrObj -> { + // \in newPts() \for o allocated in m if (indexOrObj.isObj()) - solver.addPointsTo(csLHS, csManager.getCSObj(emptyContext, indexOrObj.obj())); + solver.addPointsTo(csLHS, csManager.getCSObj(calleeContext, indexOrObj.obj())); else { + // in callee m, exist only direct-value flow between: this --> return. ParameterIndex index = indexOrObj.index(); if (index == THISINDEX && invokeExp instanceof InvokeInstanceExp instanceExp) { - CSVar csBase = csManager.getCSVar(emptyContext, instanceExp.getBase()); + CSVar csBase = csManager.getCSVar(callSiteContext, instanceExp.getBase()); // solver.getPointsToSetOf(csBase).forEach(csObj -> { JMethod realCallee = CallGraphs.resolveCallee(csObj.getObject().getType(), callSite); + // \in pts(), Type(o) match m, then add to newPts() if (callee.equals(realCallee)) solver.addPointsTo(csLHS, csObj); }); } + // exist direct-value flow between: parameter i --> return. if (index != THISINDEX) { assert index != null; - Var arg = getCorrespondingArgument(edge, index); + Var arg = getCorrespondingArgument(edge, index); // ai + // --> if (arg != null && isConcerned(arg)) - solver.addPFGEdge(new PointerFlowEdge(FlowKind.ID, csManager.getCSVar(emptyContext, arg), csLHS), + solver.addPFGEdge(new PointerFlowEdge(FlowKind.ID, csManager.getCSVar(callSiteContext, arg), csLHS), lhs.getType()); } } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java index b82067664..e5811b0c2 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/reflection/ReflectiveActionModel.java @@ -54,6 +54,7 @@ import pascal.taie.language.type.VoidType; import pascal.taie.util.collection.Maps; import pascal.taie.util.collection.MultiMap; +import pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty; import javax.annotation.Nullable; import java.util.Set; @@ -64,7 +65,6 @@ import static pascal.taie.analysis.graph.flowgraph.FlowKind.STATIC_STORE; import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.ReflectiveCallKind.METHOD_INVOKE; import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.ReflectiveCallKind.NEW_INSTANCE; -import static pascal.taie.analysis.pta.plugin.cutshortcut.ReflectiveEdgeProperty.setgetReflectiveKind; import static pascal.taie.analysis.pta.plugin.util.InvokeUtils.BASE; /** @@ -299,7 +299,7 @@ private void addReflectiveCallEdge( } ReflectiveCallEdge callEdge = new ReflectiveCallEdge(csCallSite, csManager.getCSMethod(calleeCtx, callee), args); - setgetReflectiveKind(callEdge, kind); + ReflectiveEdgeProperty.setReflectiveKind(callEdge, kind); solver.addCallEdge(callEdge); allTargets.put(callSite, callee); // record target } diff --git a/src/main/java/pascal/taie/config/Options.java b/src/main/java/pascal/taie/config/Options.java index add8a63b7..fe8d49755 100644 --- a/src/main/java/pascal/taie/config/Options.java +++ b/src/main/java/pascal/taie/config/Options.java @@ -162,6 +162,14 @@ public boolean isPrependJVM() { return prependJVM; } + @JsonProperty + @Option(names = {"-lj", "--lib-jre"}, + description = "JRE library path, (default: ${DEFAULT-VALUE})", + defaultValue = "java-benchmarks/JREs") + private String libJREPath; + + public String getLibJREPath() { return libJREPath; } + @JsonProperty @Option(names = {"-ap", "--allow-phantom"}, description = "Allow Tai-e to process phantom references, i.e.," + diff --git a/src/main/java/pascal/taie/ir/exp/Var.java b/src/main/java/pascal/taie/ir/exp/Var.java index a5beea9ae..9bb0b0454 100644 --- a/src/main/java/pascal/taie/ir/exp/Var.java +++ b/src/main/java/pascal/taie/ir/exp/Var.java @@ -218,21 +218,6 @@ public List getInvokes() { return relevantStmts.getInvokes(); } - // Support Cut-Shortcut field access pattern analysis - public void addAbstractLoadField(AbstractLoadField abstractLoadField) { - ensureRelevantStmts(); - relevantStmts.addAbstractLoadField(abstractLoadField); - } - - public List getAbstractLoadFields() { return relevantStmts.getAbstractLoadFields(); } - - public void addAbstractStoreField(AbstractStoreField abstractStoreField) { - ensureRelevantStmts(); - relevantStmts.addAbstractStoreField(abstractStoreField); - } - - public List getAbstractStoreFields() { return relevantStmts.getAbstractStoreFields(); } - /** * Ensure {@link #relevantStmts} points to an instance other than * {@link RelevantStmts#EMPTY}. @@ -292,10 +277,6 @@ private static class RelevantStmts implements Serializable { private List storeArrays = List.of(); private List invokes = List.of(); - // Support Cut-Shortcut field access pattern analysis - private List abstractLoadFields = List.of(); - private List abstractStoreFields = List.of(); - private List getLoadFields() { return unmodifiable(loadFields); } @@ -351,25 +332,6 @@ private void addInvoke(Invoke invoke) { invokes.add(invoke); } - // Support Cut-Shortcut field access pattern analysis - private List getAbstractStoreFields() { return unmodifiable(abstractStoreFields); } - - private void addAbstractStoreField(AbstractStoreField abstractStoreField) { - if (abstractStoreFields.isEmpty()) { - abstractStoreFields = new ArrayList<>(); - } - abstractStoreFields.add(abstractStoreField); - } - - private List getAbstractLoadFields() { return unmodifiable(abstractLoadFields); } - - private void addAbstractLoadField(AbstractLoadField abstractLoadField) { - if (abstractLoadFields.isEmpty()) { - abstractLoadFields = new ArrayList<>(); - } - abstractLoadFields.add(abstractLoadField); - } - private static List unmodifiable(List list) { return list.isEmpty() ? list : Collections.unmodifiableList(list); } diff --git a/src/main/java/pascal/taie/util/collection/Triplet.java b/src/main/java/pascal/taie/util/collection/Triplet.java new file mode 100644 index 000000000..9c6b96bb8 --- /dev/null +++ b/src/main/java/pascal/taie/util/collection/Triplet.java @@ -0,0 +1,11 @@ +package pascal.taie.util.collection; + +import java.io.Serializable; + +public record Triplet (T1 first, T2 second, T3 third) + implements Serializable { + @Override + public String toString() { + return "<" + first + ", " + second + ", " + third + ">"; + } +} diff --git a/src/main/resources/container-config/array-initializer.yml b/src/main/resources/container-config/array-initializer.yml new file mode 100644 index 000000000..7038239a3 --- /dev/null +++ b/src/main/resources/container-config/array-initializer.yml @@ -0,0 +1,4 @@ +ArrayInit: + - {"signature": "(java.lang.Object[])>", "src": 0, "dst": -1} + - {"signature": "", "src": 1, "dst": 0} + - {"signature": "(java.lang.Object[])>", "src": 0, "dst": -1} diff --git a/src/main/resources/container-config/entrance-append.yml b/src/main/resources/container-config/entrance-append.yml new file mode 100644 index 000000000..eb640b9e2 --- /dev/null +++ b/src/main/resources/container-config/entrance-append.yml @@ -0,0 +1,159 @@ +MapValue: + # HashMap, LinkedHashMap, TreeMap, Hashtable, WeakHashMap, IdentityHashMap, EnumMap + # ConcurrentHashMap, ConcurrentSkipListMap, Properties + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 2} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 2} + + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '(java.lang.Object,java.lang.Object)>', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + +MapKey: + # HashMap, LinkedHashMap, TreeMap, Hashtable, WeakHashMap, IdentityHashMap, EnumMap + # ConcurrentHashMap, ConcurrentSkipListMap, Properties + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '(java.lang.Object,java.lang.Object)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + +ColValue: + # ArrayList, LinkedList, Vector, ArrayDeque, Stack, AbstractQueue, PriorityQueue, TreeSet + # SynchronousQueue, ConcurrentLinkedQueue, DelayQueue, ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue + # ConcurrentSkipListSet, CopyOnWriteArrayList + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 0} + - {signature: '', index: 0} + + # those collection variables returned by Collection.xx(..), Arrays.asList(..). UnmodifiedCollection,List do not support entrance + - {signature: '(int,java.lang.Object)>', index: 1} + - {signature: '(java.lang.Object)>', index: 0} + - {signature: '(java.lang.Object)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 1} + + # Arrays.asList() 返回的内部类 (固定大小列表,set可用,add会抛异常但需建模) + - {signature: '', index: 1} + # ArrayList.subList()返回的内部类 + - {signature: '', index: 1} + - {signature: '', index: 1} + # CopyOnWriteArrayList.subList()返回的内部类 + - {signature: '', index: 1} + - {signature: '', index: 1} + # ScheduledThreadPoolExecutor内部类 + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + diff --git a/src/main/resources/container-config/entrance-extend.yml b/src/main/resources/container-config/entrance-extend.yml new file mode 100644 index 000000000..374d0bdf2 --- /dev/null +++ b/src/main/resources/container-config/entrance-extend.yml @@ -0,0 +1,133 @@ +MapToMap: + - {signature: '(java.util.Map)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '(java.util.SortedMap)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '(java.util.EnumMap)>', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Properties)>', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Map)>', index: 0} + - {signature: '(java.util.SortedMap)>', index: 0} + - {signature: '', index: 0} + + # called in Collections.synchronizedMap(...) + - {signature: '(java.util.Map)>', index: 0} + # called in Collections.unmodifiableMap(...) + - {signature: '(java.util.Map)>', index: 0} + - {signature: '', index: 0} + # called in Collections.unmodifiableSortedMap(...) + - {signature: '(java.util.SortedMap)>', index: 0} + - {signature: '', index: 0} + # called in Collections.checkedMap(...) + - {signature: '', index: 0} + - {signature: '(java.util.Map,java.lang.Class,java.lang.Class)>', index: 0} + # called in Collections.checkedSortedMap(...) + - {signature: '(java.util.SortedMap,java.lang.Class,java.lang.Class)>', index: 0} + + # TreeMap subView + - {signature: '(java.util.TreeMap,boolean,java.lang.Object,boolean,boolean,java.lang.Object,boolean)>', index: 0} + - {signature: '(java.util.TreeMap,boolean,java.lang.Object,boolean,boolean,java.lang.Object,boolean)>', index: 0} + - {signature: '(java.util.TreeMap,boolean,java.lang.Object,boolean,boolean,java.lang.Object,boolean)>', index: 0} + - {signature: '(java.util.concurrent.ConcurrentSkipListMap,java.lang.Object,boolean,java.lang.Object,boolean,boolean)>', index: 0} + +ColToCol: + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.SortedSet)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.PriorityQueue)>', index: 0} + - {signature: '(java.util.SortedSet)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.AbstractList,int,int)>', index: 0} + - {signature: '', index: 1} + - {signature: '(java.util.AbstractList,int,int)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.SortedSet)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '', index: 0} + + # inner class + - {signature: '(java.util.ArrayList,java.util.AbstractList,int,int,int)>', index: 1} + - {signature: '', index: 1} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + - {signature: '(java.util.concurrent.CopyOnWriteArrayList,int,int)>', index: 0} + - {signature: '(java.util.Collection)>', index: 0} + - {signature: '(java.util.Collection,java.lang.Class)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Set)>', index: 0} + - {signature: '(java.util.SortedSet)>', index: 0} + - {signature: '(java.util.SortedSet,java.lang.Object)>', index: 0} + - {signature: '(java.util.Deque)>', index: 0} + - {signature: '(java.util.Set,java.lang.Class)>', index: 0} + - {signature: '(java.util.Collection,java.lang.Class)>', index: 0} + - {signature: '', index: 0} + - {signature: '(java.util.Collection,java.lang.Object)>', index: 0} + - {signature: '(java.util.List,java.lang.Class)>', index: 0} + - {signature: '(java.util.Set)>', index: 0} + - {signature: '(java.util.Set,java.lang.Object)>', index: 0} + - {signature: '(java.util.List)>', index: 0} + - {signature: '(java.util.List,java.lang.Object)>', index: 0} + - {signature: '(java.util.List)>', index: 0} + - {signature: '(java.util.List)>', index: 0} + - {signature: '(java.util.List,java.lang.Object)>', index: 0} + - {signature: '(java.util.Collections$1)>', index: 0} + - {signature: '(java.util.SortedSet)>', index: 0} + - {signature: '(java.util.SortedSet,java.lang.Class)>', index: 0} + - {signature: '(java.util.List)>', index: 0} + - {signature: '(java.util.Set)>', index: 0} + - {signature: '(java.util.List,java.lang.Class)>', index: 0} + - {signature: '', index: 0} + - {signature: '', index: 1} + - {signature: '', index: 1} + +MapKeySetToCol: + - {signature: '(java.util.Map)>', index: 0} + - # Although ConcurrentSkipListSet is collection, its iterator return ConcurrentNavigableMap.keyIter(), hence for ptsH, consider it as a map. + - {signature: '(java.util.concurrent.ConcurrentNavigableMap)>', index: 0} diff --git a/src/main/resources/container-config/exit.yml b/src/main/resources/container-config/exit.yml new file mode 100644 index 000000000..4234f670b --- /dev/null +++ b/src/main/resources/container-config/exit.yml @@ -0,0 +1,342 @@ +ColValue: + # ArrayList, LinkedList, Vector, ArrayDeque, Stack, SubList, PriorityQueue, TreeSet + # SynchronousQueue, ConcurrentLinkedQueue, DelayQueue, ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue + # ConcurrentSkipListSet, CopyOnWriteArrayList + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + +MapValue: + # HashMap, LinkedHashMap, TreeMap, Hashtable, WeakHashMap, IdentityHashMap, EnumMap + # ConcurrentHashMap, ConcurrentSkipListMap, Properties + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + # ===== 漏掉的 ConcurrentSkipListMap 子视图操作 ===== + - '' + - '' + - '' + - '' + - '' + + +MapKey: + # TreeMap, ConcurrentSkipListMap + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + # inner class + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + + +# ArrayList, LinkedList, Vector, ArrayDeque, Stack, SubList, PriorityQueue, TreeSet +# SynchronousQueue, ConcurrentLinkedQueue, DelayQueue, ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue +# ConcurrentSkipListSet, CopyOnWriteArrayList +# 注意不要包括Map.entrySet.Iterator.next方法,这一类为Transfer而不是Exit, +# Set的iterator返回的可能是Map.keyIterator(),因此如果r = v.next()调用Map.keyIterator(),如果v是collection,那么r是Col-Value;如果是Map.keySet,那么是 +ColIter: + - '' # AbstractList.iterator() + - '' # AbstractList.listIterator() + - '' # ArrayList.iterator() + - '' # ArrayList.listIterator() + - '' # ArrayList.SubList.listIterator(), anymous class + - '' + - '' # LinkedList.iterator()/listIterator() + - '' + - '' # LinkedList.descendingIterator() + - '' # SubList.listIterator(), anymous class + - '' + - '' # Vector.iterator() + - '' # Vector.listIterator() + - '' # Vector.elements() + - '' # PriorityDeque.iterator() + - '' # ArrayDeque.iterator() + - '' # ArrayDeque.descendingIterator() + - '' # AbstractMap.keySet().iterator() + - '' # AbstractMap.values().iterator() + - '' # HashMap.values().iterator() + - '' # HashMap.keySet().iterator()/HashSet.iterator() + - '' # HashMap.values().iterator() + - '' # WeakHashMap.keySet().iterator() + - '' # WeakHashMap.values().iterator() + - '' # IdentityHashMap.keySet().iterator() + - '' # IdentityHashMap.values().iterator() + - '' # EnumMap.keySet().iterator() + - '' # EnumMap.values().iterator() + - '' # TreeMap.keySet().iterator()/TreeSet.iterator() + - '' # TreeMap.keySet().descendingIterator()/TreeSet.descendingIterator() + - '' # TreeMap.values().iterator() + - '' # TreeMap.keySet().iterator()/TreeSet.iterator() + - '' # TreeMap.keySet().descendingIterator()/TreeSet.descendingIterator() + # Hashtable.keys() => MAP_KEY_ITR, keySet() => MAP_KEY_SET, elements() => MAP_VALUE_ITR, values() => MAP_VALUES + - '' # HashTable.keySet()/values().iterator() + - '' + # ConcurrentHashMap.keys() => MAP_KEY_ITR, keySet() => MAP_KEY_SET, elements() => MAP_VALUE_ITR, values() => MAP_VALUES + - '' # ConcurrentHashMap.keySet().iterator() + - '' + - '' # ConcurrentHashMap.values().iterator() + - '' + # ConcurrentSkipListMap + - '' # ConcurrentSkipListMap.keySet().iterator() + - '' # ConcurrentSkipListMap.values().iterator() + - '' # ConcurrentSkipListMap.subMap().keySet().iterator() + - '' # ConcurrentSkipListMap.subMap().values().iterator() + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' # Collections.unmodifiableCollection().iterator() + - '' # Collections.unmodifiableList().iterator() + - '' # Collections.checkedCollection().iterator() + - '' # Collections.checkedList().listIterator() + - '' # Collections.singletonSet()/singletonList().listIterator() + - '' # HashMap.entrySet().iterator() + - '' # WeakHashMap.entrySet().iterator() + - '' # IdentityHashMap.entrySet().iterator() + - '' # TreeMap.subMap().entrySet().iterator() + - '' # TreeMap.descendingMap().entrySet().iterator() + - '' # EnumMap.entrySet().iterator() + - '' # TreeMap.entrySet().iterator() + +#MapGetKey: +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# +#MapGetValue: +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' +# - '' diff --git a/src/main/resources/container-config/host-classes.yml b/src/main/resources/container-config/host-classes.yml new file mode 100644 index 000000000..113d7d8ec --- /dev/null +++ b/src/main/resources/container-config/host-classes.yml @@ -0,0 +1,121 @@ +Col: + - "java.util.ArrayList" + - "java.util.LinkedList" + - "java.util.SubList" + - "java.util.RandomAccessSubList" + - "java.util.Vector" + - "java.util.Stack" + - "java.util.PriorityQueue" + - "java.util.ArrayDeque" + - "java.util.HashSet" + - "java.util.LinkedHashSet" + - "java.util.TreeSet" + - "java.util.concurrent.ConcurrentLinkedQueue" + - "java.util.concurrent.ArrayBlockingQueue" + - "java.util.concurrent.LinkedBlockingQueue" + - "java.util.concurrent.LinkedBlockingDeque" + - "java.util.concurrent.PriorityBlockingQueue" + - "java.util.concurrent.DelayQueue" + - "java.util.concurrent.SynchronousQueue" + - "java.util.concurrent.ConcurrentSkipListSet" + - "java.util.concurrent.CopyOnWriteArrayList" + - "java.util.concurrent.CopyOnWriteArraySet" + + # inner class + - "java.util.Arrays$ArrayList" + - "java.util.ArrayList$SubList" + - "java.util.concurrent.CopyOnWriteArrayList$COWSubList" + - "java.util.Collections$UnmodifiableCollection" + - "java.util.Collections$UnmodifiableSet" + - "java.util.Collections$UnmodifiableSortedSet" + - "java.util.Collections$CopiesList" + - "java.util.Collections$AsLIFOQueue" + - "java.util.Collections$SynchronizedCollection" + - "java.util.Collections$UnmodifiableList" + - "java.util.Collections$CheckedCollection" + - "java.util.Collections$EmptySet" + - "java.util.Collections$SetFromMap" + - "java.util.Collections$SynchronizedList" + - "java.util.Collections$SynchronizedRandomAccessList" + - "java.util.Collections$SynchronizedSet" + - "java.util.Collections$CheckedList" + - "java.util.Collections$CheckedRandomAccessList" + - "java.util.Collections$UnmodifiableRandomAccessList" + - "java.util.Collections$SingletonSet" + - "java.util.Collections$SingletonList" + - "java.util.Collections$CheckedSet" + - "java.util.Collections$EmptyList" + - "java.util.Collections$CheckedSortedSet" + - "java.util.Collections$SynchronizedSortedSet" + - "java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue" + +Map: + - "java.util.HashMap" + - "java.util.LinkedHashMap" + - "java.util.IdentityHashMap" + - "java.util.TreeMap" + - "java.util.AbstractMap" + - "java.util.Hashtable" + - "java.util.WeakHashMap" + - "java.util.EnumMap" + - "java.util.Properties" + + - "java.util.concurrent.ConcurrentHashMap" + - "java.util.concurrent.ConcurrentSkipListMap" + - "java.util.Collections$SynchronizedMap" + - "java.util.Collections$SynchronizedSortedMap" + - "java.util.Collections$EmptyMap" + - "java.util.Collections$UnmodifiableMap" + - "java.util.Collections$UnmodifiableSortedMap" + - "java.util.Collections$SingletonMap" + - "java.util.Collections$CheckedMap" + - "java.util.Collections$CheckedSortedMap" + + - "java.util.TreeMap$NavigableSubMap" + - "java.util.TreeMap$DescendingSubMap" + - "java.util.TreeMap$AscendingSubMap" + - "java.util.TreeMap$SubMap" + +EntrySet: + - "java.util.HashMap$EntrySet" + - "java.util.LinkedHashMap$LinkedEntrySet" + - "java.util.TreeMap$EntrySet" + - "java.util.EnumMap$EntrySet" + - "java.util.IdentityHashMap$EntrySet" + - "java.util.WeakHashMap$EntrySet" + - "java.util.Hashtable$EntrySet" + - "java.util.concurrent.ConcurrentHashMap$EntrySetView" + - "java.util.concurrent.ConcurrentSkipListMap$EntrySet" + + - "java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet" + - "java.util.Collections$CheckedMap$CheckedEntrySet" + - "java.util.TreeMap$NavigableSubMap$EntrySetView" + - "java.util.TreeMap$DescendingSubMap$DescendingEntrySetView" + - "java.util.TreeMap$AscendingSubMap$AscendingEntrySetView" + +KeySet: + - "java.util.HashMap$KeySet" + - "java.util.LinkedHashMap$LinkedKeySet" + - "java.util.IdentityHashMap$KeySet" + - "java.util.WeakHashMap$KeySet" + - "java.util.Hashtable$KeySet" + - "java.util.AbstractMap$1" # AbstractMap.keySet() is anonymous class + - "java.util.TreeMap$KeySet" + - "java.util.EnumMap$KeySet" + - "java.util.concurrent.ConcurrentHashMap$KeySetView" + - "java.util.concurrent.ConcurrentSkipListMap$KeySet" + + +Values: + - "java.util.HashMap$Values" + - "java.util.LinkedHashMap$LinkedValues" + - "java.util.IdentityHashMap$Values" + - "java.util.WeakHashMap$Values" + - "java.util.Hashtable$ValueCollection" + - "java.util.AbstractMap$2" # AbstractMap.values() is anonymous class + - "java.util.TreeMap$Values2" + - "java.util.EnumMap$Values" + - "java.util.concurrent.ConcurrentHashMap$ValuesView" + - "java.util.concurrent.ConcurrentSkipListMap$Values" + + diff --git a/src/main/resources/tai-e-analyses.yml b/src/main/resources/tai-e-analyses.yml index 72e244ece..23943b9ae 100644 --- a/src/main/resources/tai-e-analyses.yml +++ b/src/main/resources/tai-e-analyses.yml @@ -3,6 +3,7 @@ id: pta options: cs: ci # | k-[obj|type|call][-k'h] + solver: default # | csc, which solver to use. Currently default or cut-shortcut only-app: false # only analyze application code implicit-entries: true # analyze implicit entries distinguish-string-constants: reflection # (distinguish reflection-relevant @@ -23,6 +24,7 @@ dump: false # whether dump points-to results (with contexts) dump-ci: false # whether dump points-to results (without contexts) dump-yaml: false # whether dump points-to results in yaml format + only-dump-app: false # only dump results of application code expected-file: null # path of expected file for comparing results reflection-inference: string-constant # | solar | null reflection-log: null # path to reflection log, required when reflection option is log