From a93a50da280ffc86fbdae6997723d5d6ec184416 Mon Sep 17 00:00:00 2001 From: lucaneg Date: Fri, 29 Oct 2021 13:04:02 +0200 Subject: [PATCH 1/3] #137 exposing callgraph in semantic checks + new callgraph methods --- .../java/it/unive/lisa/LiSAConfiguration.java | 10 ++-- .../main/java/it/unive/lisa/LiSARunner.java | 11 ++-- .../interprocedural/ContextBasedAnalysis.java | 2 + .../nonInterference/NonInterferenceTest.java | 55 +++++++++++------ .../CheckToolWithAnalysisResults.java | 60 +++++++++++++++++-- .../lisa/checks/semantic/SemanticCheck.java | 11 +++- .../InterproceduralAnalysis.java | 17 ++---- .../callgraph/BaseCallGraph.java | 28 +++++++++ .../interprocedural/callgraph/CallGraph.java | 18 ++++++ .../program/cfg/statement/call/CFGCall.java | 3 +- .../CheckToolWithAnalysisResultsTest.java | 50 ++++++++++++++-- 11 files changed, 213 insertions(+), 52 deletions(-) diff --git a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAConfiguration.java b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAConfiguration.java index 6c5c5eeda..e44e5727b 100644 --- a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAConfiguration.java +++ b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAConfiguration.java @@ -43,7 +43,7 @@ public class LiSAConfiguration { /** * The collection of semantic checks to execute */ - private final Collection semanticChecks; + private final Collection> semanticChecks; /** * The callgraph to use during the analysis @@ -179,7 +179,7 @@ public LiSAConfiguration addSyntacticChecks(Collection checks) { * * @return the current (modified) configuration */ - public LiSAConfiguration addSemanticCheck(SemanticCheck check) { + public LiSAConfiguration addSemanticCheck(SemanticCheck check) { semanticChecks.add(check); return this; } @@ -192,7 +192,7 @@ public LiSAConfiguration addSemanticCheck(SemanticCheck check) { * * @return the current (modified) configuration */ - public LiSAConfiguration addSemanticChecks(Collection checks) { + public LiSAConfiguration addSemanticChecks(Collection> checks) { semanticChecks.addAll(checks); return this; } @@ -469,7 +469,7 @@ public Collection getSyntacticChecks() { * * @return the semantic checks */ - public Collection getSemanticChecks() { + public Collection> getSemanticChecks() { return semanticChecks; } @@ -672,7 +672,7 @@ public String toString() { .append(semanticChecks.size()) .append(" semantic checks to execute") .append((semanticChecks.isEmpty() ? "" : ":")); - for (SemanticCheck check : semanticChecks) + for (SemanticCheck check : semanticChecks) res.append("\n ") .append(check.getClass().getSimpleName()); return res.toString(); diff --git a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java index 62ec57bfa..f08b54972 100644 --- a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java +++ b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java @@ -11,6 +11,7 @@ import it.unive.lisa.caches.Caches; import it.unive.lisa.checks.ChecksExecutor; import it.unive.lisa.checks.semantic.CheckToolWithAnalysisResults; +import it.unive.lisa.checks.semantic.SemanticCheck; import it.unive.lisa.checks.syntactic.CheckTool; import it.unive.lisa.checks.warnings.Warning; import it.unive.lisa.interprocedural.InterproceduralAnalysis; @@ -152,11 +153,13 @@ Collection run(Program program, FileManager fileManager) { for (CFG cfg : allCFGs) results.put(cfg, interproc.getAnalysisResultsOf(cfg)); - if (!conf.getSemanticChecks().isEmpty()) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + Collection> semanticChecks = (Collection) conf.getSemanticChecks(); + if (!semanticChecks.isEmpty()) { CheckToolWithAnalysisResults toolWithResults = new CheckToolWithAnalysisResults<>(tool, results); - tool = toolWithResults; - ChecksExecutor.executeAll(toolWithResults, program, conf.getSemanticChecks()); + V> tool2 = new CheckToolWithAnalysisResults<>(tool, results, callGraph); + tool = tool2; + ChecksExecutor.executeAll(tool2, program, semanticChecks); } else LOG.warn("Skipping semantic checks execution since none have been provided"); } else diff --git a/lisa/lisa-core/src/main/java/it/unive/lisa/interprocedural/ContextBasedAnalysis.java b/lisa/lisa-core/src/main/java/it/unive/lisa/interprocedural/ContextBasedAnalysis.java index 5589cfb01..539a1d6a1 100644 --- a/lisa/lisa-core/src/main/java/it/unive/lisa/interprocedural/ContextBasedAnalysis.java +++ b/lisa/lisa-core/src/main/java/it/unive/lisa/interprocedural/ContextBasedAnalysis.java @@ -224,6 +224,8 @@ public final AnalysisState getAbstractResultOf(CFGCall call, AnalysisSt token = token.popToken(); + callgraph.registerCall(call); + return result; } diff --git a/lisa/lisa-core/src/test/java/it/unive/lisa/cron/nonInterference/NonInterferenceTest.java b/lisa/lisa-core/src/test/java/it/unive/lisa/cron/nonInterference/NonInterferenceTest.java index e12ec6e56..c89067742 100644 --- a/lisa/lisa-core/src/test/java/it/unive/lisa/cron/nonInterference/NonInterferenceTest.java +++ b/lisa/lisa-core/src/test/java/it/unive/lisa/cron/nonInterference/NonInterferenceTest.java @@ -1,13 +1,11 @@ package it.unive.lisa.cron.nonInterference; -import static it.unive.lisa.LiSAFactory.getDefaultFor; - import it.unive.lisa.AnalysisSetupException; import it.unive.lisa.AnalysisTestExecutor; import it.unive.lisa.LiSAConfiguration; -import it.unive.lisa.analysis.AbstractState; import it.unive.lisa.analysis.CFGWithAnalysisResults; -import it.unive.lisa.analysis.heap.HeapDomain; +import it.unive.lisa.analysis.SimpleAbstractState; +import it.unive.lisa.analysis.heap.MonolithicHeap; import it.unive.lisa.analysis.nonInterference.NonInterference; import it.unive.lisa.analysis.nonrelational.inference.InferenceSystem; import it.unive.lisa.checks.semantic.CheckToolWithAnalysisResults; @@ -30,8 +28,8 @@ public class NonInterferenceTest extends AnalysisTestExecutor { @Test public void testConfidentialityNI() throws AnalysisSetupException { LiSAConfiguration conf = new LiSAConfiguration().setDumpAnalysis(true) - .setAbstractState(getDefaultFor(AbstractState.class, - getDefaultFor(HeapDomain.class), new NonInterference())) + .setAbstractState( + new SimpleAbstractState<>(new MonolithicHeap(), new InferenceSystem<>(new NonInterference()))) .addSemanticCheck(new NICheck()); perform("non-interference/confidentiality", "program.imp", conf); } @@ -39,8 +37,8 @@ public void testConfidentialityNI() throws AnalysisSetupException { @Test public void testIntegrityNI() throws AnalysisSetupException { LiSAConfiguration conf = new LiSAConfiguration().setDumpAnalysis(true) - .setAbstractState(getDefaultFor(AbstractState.class, - getDefaultFor(HeapDomain.class), new NonInterference())) + .setAbstractState( + new SimpleAbstractState<>(new MonolithicHeap(), new InferenceSystem<>(new NonInterference()))) .addSemanticCheck(new NICheck()); perform("non-interference/integrity", "program.imp", conf); } @@ -48,42 +46,60 @@ public void testIntegrityNI() throws AnalysisSetupException { @Test public void testDeclassification() throws AnalysisSetupException { LiSAConfiguration conf = new LiSAConfiguration().setDumpAnalysis(true) - .setAbstractState(getDefaultFor(AbstractState.class, - getDefaultFor(HeapDomain.class), new NonInterference())) + .setAbstractState( + new SimpleAbstractState<>(new MonolithicHeap(), new InferenceSystem<>(new NonInterference()))) .setCallGraph(new RTACallGraph()) .setInterproceduralAnalysis(new ContextBasedAnalysis<>(RecursionFreeToken.getSingleton())) .addSemanticCheck(new NICheck()); perform("non-interference/interproc", "program.imp", conf); } - private static class NICheck implements SemanticCheck { + private static class NICheck + implements SemanticCheck>, + MonolithicHeap, InferenceSystem> { @Override - public void beforeExecution(CheckToolWithAnalysisResults tool) { + public void beforeExecution( + CheckToolWithAnalysisResults>, + MonolithicHeap, InferenceSystem> tool) { } @Override - public void afterExecution(CheckToolWithAnalysisResults tool) { + public void afterExecution( + CheckToolWithAnalysisResults>, + MonolithicHeap, InferenceSystem> tool) { } @Override - public boolean visitCompilationUnit(CheckToolWithAnalysisResults tool, CompilationUnit unit) { + public boolean visitCompilationUnit( + CheckToolWithAnalysisResults>, + MonolithicHeap, InferenceSystem> tool, + CompilationUnit unit) { return true; } @Override - public void visitGlobal(CheckToolWithAnalysisResults tool, Unit unit, Global global, + public void visitGlobal( + CheckToolWithAnalysisResults>, + MonolithicHeap, InferenceSystem> tool, + Unit unit, Global global, boolean instance) { } @Override - public boolean visit(CheckToolWithAnalysisResults tool, CFG graph) { + public boolean visit( + CheckToolWithAnalysisResults>, + MonolithicHeap, InferenceSystem> tool, + CFG graph) { return true; } @Override @SuppressWarnings({ "unchecked" }) - public boolean visit(CheckToolWithAnalysisResults tool, CFG graph, Statement node) { + public boolean visit( + CheckToolWithAnalysisResults>, + MonolithicHeap, InferenceSystem> tool, + CFG graph, Statement node) { if (!(node instanceof Assignment)) return true; @@ -119,7 +135,10 @@ public boolean visit(CheckToolWithAnalysisResults tool, CFG graph, Stat } @Override - public boolean visit(CheckToolWithAnalysisResults tool, CFG graph, Edge edge) { + public boolean visit( + CheckToolWithAnalysisResults>, + MonolithicHeap, InferenceSystem> tool, + CFG graph, Edge edge) { return true; } diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java index 218c92328..857278dfa 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java @@ -5,7 +5,10 @@ import it.unive.lisa.analysis.heap.HeapDomain; import it.unive.lisa.analysis.value.ValueDomain; import it.unive.lisa.checks.syntactic.CheckTool; +import it.unive.lisa.interprocedural.callgraph.CallGraph; import it.unive.lisa.program.cfg.CFG; +import it.unive.lisa.program.cfg.CodeMember; +import it.unive.lisa.program.cfg.statement.call.Call; import java.util.Collection; import java.util.Map; @@ -25,26 +28,33 @@ public class CheckToolWithAnalysisResults, private final Map>> results; + private final CallGraph callgraph; + /** * Builds the tool, storing the given results. * - * @param results the results to store + * @param results the results to store + * @param callgraph the callgraph that has been built during the analysis */ - public CheckToolWithAnalysisResults(Map>> results) { - super(); + public CheckToolWithAnalysisResults(Map>> results, + CallGraph callgraph) { this.results = results; + this.callgraph = callgraph; } /** * Builds the tool, copying the given tool and storing the given results. * - * @param other the tool to copy - * @param results the results to store + * @param other the tool to copy + * @param results the results to store + * @param callgraph the callgraph that has been built during the analysis */ public CheckToolWithAnalysisResults(CheckTool other, - Map>> results) { + Map>> results, + CallGraph callgraph) { super(other); this.results = results; + this.callgraph = callgraph; } /** @@ -58,4 +68,42 @@ public CheckToolWithAnalysisResults(CheckTool other, public Collection> getResultOf(CFG cfg) { return results.get(cfg); } + + /** + * Yields all the {@link CodeMember}s that call the given one, according to + * the {@link CallGraph} that has been built during the analysis. + * + * @param cm the target code member + * + * @return the collection of callers code members + */ + public Collection getCallers(CodeMember cm) { + return callgraph.getCallers(cm); + } + + /** + * Yields all the {@link CodeMember}s that are called by the given one, + * according to the {@link CallGraph} that has been built during the + * analysis. + * + * @param cm the target code member + * + * @return the collection of called code members + */ + public Collection getCallees(CodeMember cm) { + return callgraph.getCallees(cm); + } + + /** + * Yields all the {@link Call}s that targets the given {@link CodeMember}, + * according to the {@link CallGraph} that has been built during the + * analysis. + * + * @param cm the target code member + * + * @return the collection of calls that target the code member + */ + public Collection getCallSites(CodeMember cm) { + return callgraph.getCallSites(cm); + } } diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/SemanticCheck.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/SemanticCheck.java index 6a0cc7ab6..b04c8896b 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/SemanticCheck.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/SemanticCheck.java @@ -1,5 +1,8 @@ package it.unive.lisa.checks.semantic; +import it.unive.lisa.analysis.AbstractState; +import it.unive.lisa.analysis.heap.HeapDomain; +import it.unive.lisa.analysis.value.ValueDomain; import it.unive.lisa.checks.Check; /** @@ -9,6 +12,12 @@ * as auxiliary tool during the inspection. * * @author Luca Negrini + * + * @param the type of {@link AbstractState} contained in the results + * @param the type of {@link HeapDomain} contained in the results + * @param the type of {@link ValueDomain} contained in the results */ -public interface SemanticCheck extends Check> { +public interface SemanticCheck, + H extends HeapDomain, + V extends ValueDomain> extends Check> { } diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/InterproceduralAnalysis.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/InterproceduralAnalysis.java index 758c6ab3d..943e78744 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/InterproceduralAnalysis.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/InterproceduralAnalysis.java @@ -15,7 +15,6 @@ import it.unive.lisa.program.cfg.statement.Statement; import it.unive.lisa.program.cfg.statement.call.CFGCall; import it.unive.lisa.program.cfg.statement.call.Call; -import it.unive.lisa.program.cfg.statement.call.OpenCall; import it.unive.lisa.program.cfg.statement.call.UnresolvedCall; import it.unive.lisa.symbolic.SymbolicExpression; import it.unive.lisa.symbolic.value.Identifier; @@ -90,7 +89,10 @@ void fixpoint(AnalysisState entryState, * computes an analysis state that abstracts the execution of the possible * targets considering that they were given {@code parameters} as actual * parameters. The abstract value of each parameter is computed on - * {@code entryState}. + * {@code entryState}.
+ *
+ * Note that the interprocedural analysis is also responsible for + * registering the call to the {@link CallGraph}, if needed. * * @param call the call to resolve and evaluate * @param entryState the abstract analysis state when the call is reached @@ -111,15 +113,8 @@ AnalysisState getAbstractResultOf(CFGCall call, AnalysisState /** * Yields a {@link Call} implementation that corresponds to the resolution - * of the given {@link UnresolvedCall}. This method will return: - *
    - *
  • a {@link CFGCall}, if at least one {@link CFG} that matches - * {@link UnresolvedCall#getTargetName()} is found. The returned - * {@link CFGCall} will be linked to all the possible runtime targets - * matching {@link UnresolvedCall#getTargetName()};
  • - *
  • an {@link OpenCall}, if no {@link CFG} matching - * {@link UnresolvedCall#getTargetName()} is found.
  • - *
+ * of the given {@link UnresolvedCall}. This method will forward the call to + * {@link CallGraph#resolve(UnresolvedCall)} if needed. * * @param unresolvedCall the call to resolve * diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java index 1efcee12c..f29925433 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java @@ -16,6 +16,9 @@ import it.unive.lisa.util.datastructures.graph.Graph; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -34,11 +37,29 @@ public abstract class BaseCallGraph extends Graph> callsites = new HashMap<>(); + @Override public final void init(Program program) throws CallGraphConstructionException { this.program = program; } + @Override + public void registerCall(CFGCall call) { + CallGraphNode source = new CallGraphNode(this, call.getCFG()); + if (!adjacencyMatrix.containsNode(source)) + addNode(source, program.getEntryPoints().contains(call.getCFG())); + + for (CFG cfg : call.getTargets()) { + callsites.computeIfAbsent(cfg, cm -> new HashSet<>()).add(call); + + CallGraphNode t = new CallGraphNode(this, cfg); + if (!adjacencyMatrix.containsNode(t)) + addNode(t, program.getEntryPoints().contains(call.getCFG())); + addEdge(new CallGraphEdge(source, t)); + } + } + @Override public final Call resolve(UnresolvedCall call) throws CallResolutionException { Collection targets = new ArrayList<>(); @@ -95,6 +116,7 @@ else if (nativeTargets.isEmpty()) if (!adjacencyMatrix.containsNode(t)) addNode(t, program.getEntryPoints().contains(call.getCFG())); addEdge(new CallGraphEdge(source, t)); + callsites.computeIfAbsent(target, cm -> new HashSet<>()).add(call); } for (NativeCFG target : nativeTargets) { @@ -102,6 +124,7 @@ else if (nativeTargets.isEmpty()) if (!adjacencyMatrix.containsNode(t)) addNode(t, false); addEdge(new CallGraphEdge(source, t)); + callsites.computeIfAbsent(target, cm -> new HashSet<>()).add(call); } return resolved; @@ -130,6 +153,11 @@ public Collection getCallers(CodeMember cm) { .collect(Collectors.toList()); } + @Override + public Collection getCallSites(CodeMember cm) { + return callsites.get(cm); + } + @Override protected DotGraph toDot( Function labelGenerator) { diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java index 9188fb317..a5b60bc6a 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java @@ -48,6 +48,13 @@ public interface CallGraph { */ Call resolve(UnresolvedCall call) throws CallResolutionException; + /** + * Registers an already resolved {@link CFGCall} in this {@link CallGraph}. + * + * @param call the call to register + */ + void registerCall(CFGCall call); + /** * Yields all the {@link CodeMember}s that call the given one. The returned * collection might contain partial results if this call graph is not fully @@ -69,4 +76,15 @@ public interface CallGraph { * @return the collection of called code members */ Collection getCallees(CodeMember cm); + + /** + * Yields all the {@link Call}s that targets the given {@link CodeMember}. + * The returned collection might contain partial results if this call graph + * is not fully built. + * + * @param cm the target code member + * + * @return the collection of calls that target the code member + */ + Collection getCallSites(CodeMember cm); } diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/program/cfg/statement/call/CFGCall.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/program/cfg/statement/call/CFGCall.java index a44408493..5ab5bb7db 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/program/cfg/statement/call/CFGCall.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/program/cfg/statement/call/CFGCall.java @@ -161,7 +161,8 @@ public final Identifier getMetaVariable() { public
, H extends HeapDomain, V extends ValueDomain> AnalysisState callSemantics( - AnalysisState entryState, InterproceduralAnalysis interprocedural, + AnalysisState entryState, + InterproceduralAnalysis interprocedural, AnalysisState[] computedStates, ExpressionSet[] params) throws SemanticException { diff --git a/lisa/lisa-sdk/src/test/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResultsTest.java b/lisa/lisa-sdk/src/test/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResultsTest.java index b9ad9564d..29809e07d 100644 --- a/lisa/lisa-sdk/src/test/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResultsTest.java +++ b/lisa/lisa-sdk/src/test/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResultsTest.java @@ -17,15 +17,23 @@ import it.unive.lisa.checks.warnings.StatementWarning; import it.unive.lisa.checks.warnings.UnitWarning; import it.unive.lisa.checks.warnings.Warning; +import it.unive.lisa.interprocedural.callgraph.CallGraph; +import it.unive.lisa.interprocedural.callgraph.CallGraphConstructionException; +import it.unive.lisa.interprocedural.callgraph.CallResolutionException; import it.unive.lisa.program.CompilationUnit; import it.unive.lisa.program.Global; +import it.unive.lisa.program.Program; import it.unive.lisa.program.SourceCodeLocation; import it.unive.lisa.program.Unit; import it.unive.lisa.program.cfg.CFG; import it.unive.lisa.program.cfg.CFGDescriptor; +import it.unive.lisa.program.cfg.CodeMember; import it.unive.lisa.program.cfg.statement.Expression; import it.unive.lisa.program.cfg.statement.NoOp; import it.unive.lisa.program.cfg.statement.Statement; +import it.unive.lisa.program.cfg.statement.call.CFGCall; +import it.unive.lisa.program.cfg.statement.call.Call; +import it.unive.lisa.program.cfg.statement.call.UnresolvedCall; import it.unive.lisa.program.cfg.statement.literal.Int32Literal; import java.util.Collection; import java.util.Collections; @@ -47,6 +55,36 @@ public class CheckToolWithAnalysisResultsTest { "faa"); private static final CFG cfg2 = new CFG(descriptor2); + private static final CallGraph fakeCallGraph = new CallGraph() { + @Override + public Call resolve(UnresolvedCall call) throws CallResolutionException { + return null; + } + + @Override + public void registerCall(CFGCall call) { + } + + @Override + public void init(Program program) throws CallGraphConstructionException { + } + + @Override + public Collection getCallers(CodeMember cm) { + return null; + } + + @Override + public Collection getCallees(CodeMember cm) { + return null; + } + + @Override + public Collection getCallSites(CodeMember cm) { + return null; + } + }; + private static Warning build(CheckTool tool, Object target, String message) { if (target == null) { tool.warn(message); @@ -76,7 +114,7 @@ private static Warning build(CheckTool tool, Object target, String message) { @Test public void testCopy() { CheckToolWithAnalysisResults tool = new CheckToolWithAnalysisResults<>(Map.of()); + TestValueDomain> tool = new CheckToolWithAnalysisResults<>(Map.of(), fakeCallGraph); Collection exp = new HashSet<>(); exp.add(build(tool, null, "foo")); @@ -99,13 +137,13 @@ public void testCopy() { assertTrue("Wrong set of warnings", CollectionUtils.isEqualCollection(exp, tool.getWarnings())); assertTrue("Wrong set of warnings", CollectionUtils.isEqualCollection(exp, - new CheckToolWithAnalysisResults<>(tool, Map.of()).getWarnings())); + new CheckToolWithAnalysisResults<>(tool, Map.of(), fakeCallGraph).getWarnings())); } @Test public void testSimpleFill() { CheckToolWithAnalysisResults tool = new CheckToolWithAnalysisResults<>(Map.of()); + TestValueDomain> tool = new CheckToolWithAnalysisResults<>(Map.of(), fakeCallGraph); Collection exp = new HashSet<>(); exp.add(build(tool, null, "foo")); @@ -121,7 +159,7 @@ public void testSimpleFill() { @Test public void testDisjointWarnings() { CheckToolWithAnalysisResults tool = new CheckToolWithAnalysisResults<>(Map.of()); + TestValueDomain> tool = new CheckToolWithAnalysisResults<>(Map.of(), fakeCallGraph); Collection exp = new HashSet<>(); exp.add(build(tool, new NoOp(cfg, new SourceCodeLocation("fake", 3, 0)), "foo")); @@ -135,7 +173,7 @@ public void testDisjointWarnings() { @Test public void testDuplicateWarnings() { CheckToolWithAnalysisResults tool = new CheckToolWithAnalysisResults<>(Map.of()); + TestValueDomain> tool = new CheckToolWithAnalysisResults<>(Map.of(), fakeCallGraph); Collection exp = new HashSet<>(); exp.add(build(tool, new NoOp(cfg, new SourceCodeLocation("fake", 3, 0)), "foo")); @@ -167,7 +205,7 @@ public void testResultRetrieval() { CheckToolWithAnalysisResults tool = new CheckToolWithAnalysisResults<>( - Map.of(cfg, Collections.singleton(res1), cfg2, Collections.singleton(res2))); + Map.of(cfg, Collections.singleton(res1), cfg2, Collections.singleton(res2)), fakeCallGraph); assertEquals(res1, tool.getResultOf(cfg).iterator().next()); assertEquals(res2, tool.getResultOf(cfg2).iterator().next()); From a7209ea266b025ebc69f33c5347f83c696431d6a Mon Sep 17 00:00:00 2001 From: lucaneg Date: Fri, 29 Oct 2021 16:09:46 +0200 Subject: [PATCH 2/3] #137 adding cache for call resolution + exposing it to check tool --- .../main/java/it/unive/lisa/LiSAFactory.java | 20 +++++++++++++++++++ .../main/java/it/unive/lisa/LiSARunner.java | 7 +++++-- .../CheckToolWithAnalysisResults.java | 19 ++++++++++++++++++ .../callgraph/BaseCallGraph.java | 9 +++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAFactory.java b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAFactory.java index eb08b69b8..0a98ef562 100644 --- a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAFactory.java +++ b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSAFactory.java @@ -16,12 +16,16 @@ import it.unive.lisa.analysis.nonrelational.value.ValueEnvironment; import it.unive.lisa.analysis.numeric.Interval; import it.unive.lisa.analysis.value.ValueDomain; +import it.unive.lisa.interprocedural.ContextBasedAnalysis; +import it.unive.lisa.interprocedural.ContextSensitivityToken; import it.unive.lisa.interprocedural.InterproceduralAnalysis; import it.unive.lisa.interprocedural.ModularWorstCaseAnalysis; +import it.unive.lisa.interprocedural.RecursionFreeToken; import it.unive.lisa.interprocedural.callgraph.CallGraph; import it.unive.lisa.interprocedural.callgraph.RTACallGraph; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -76,14 +80,30 @@ public final class LiSAFactory { DEFAULT_IMPLEMENTATIONS.put(ValueDomain.class, Interval.class); DEFAULT_IMPLEMENTATIONS.put(AbstractState.class, SimpleAbstractState.class); DEFAULT_PARAMETERS.put(SimpleAbstractState.class, new Class[] { MonolithicHeap.class, Interval.class }); + DEFAULT_PARAMETERS.put(ContextBasedAnalysis.class, new Class[] { RecursionFreeToken.class }); } private LiSAFactory() { // this class is just a static holder } + @SuppressWarnings("unchecked") private static T construct(Class component, Class[] argTypes, Object[] params) throws AnalysisSetupException { + if (ContextSensitivityToken.class.isAssignableFrom(component)) { + // tokens use the getSingleton() pattern for construction + try { + Method method = component.getMethod("getSingleton"); + if (!Modifier.isStatic(method.getModifiers())) + throw new AnalysisSetupException( + "Unable to instantiate " + component.getSimpleName() + ": getSingleton() is not static"); + return (T) method.invoke(null); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) { + throw new AnalysisSetupException("Unable to instantiate " + component.getSimpleName(), e); + } + } + try { Constructor constructor = component.getConstructor(argTypes); return constructor.newInstance(params); diff --git a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java index f08b54972..f525aefea 100644 --- a/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java +++ b/lisa/lisa-core/src/main/java/it/unive/lisa/LiSARunner.java @@ -194,10 +194,13 @@ private void analyze(Collection allCFGs, FileManager fileManager) { private void inferTypes(FileManager fileManager, Program program, Collection allCFGs) { T typesState = this.typeState.top(); InterproceduralAnalysis typesInterproc; + CallGraph typesCg; try { + typesCg = getInstance(callGraph.getClass()); typesInterproc = getInstance(interproc.getClass()); - typesInterproc.init(program, callGraph); - } catch (AnalysisSetupException | InterproceduralAnalysisException e) { + typesCg.init(program); + typesInterproc.init(program, typesCg); + } catch (AnalysisSetupException | InterproceduralAnalysisException | CallGraphConstructionException e) { throw new AnalysisExecutionException("Unable to initialize type inference", e); } diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java index 857278dfa..ff43f0ac3 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/checks/semantic/CheckToolWithAnalysisResults.java @@ -6,9 +6,11 @@ import it.unive.lisa.analysis.value.ValueDomain; import it.unive.lisa.checks.syntactic.CheckTool; import it.unive.lisa.interprocedural.callgraph.CallGraph; +import it.unive.lisa.interprocedural.callgraph.CallResolutionException; import it.unive.lisa.program.cfg.CFG; import it.unive.lisa.program.cfg.CodeMember; import it.unive.lisa.program.cfg.statement.call.Call; +import it.unive.lisa.program.cfg.statement.call.UnresolvedCall; import java.util.Collection; import java.util.Map; @@ -106,4 +108,21 @@ public Collection getCallees(CodeMember cm) { public Collection getCallSites(CodeMember cm) { return callgraph.getCallSites(cm); } + + /** + * Yields the resolved version of the given call, according to the + * {@link CallGraph} that has been built during the analysis. Yields + * {@code null} if the call cannot be resolved (i.e. an exception happened). + * + * @param call the call to resolve + * + * @return the resolved version of the given call, or {@code null} + */ + public Call getResolvedVersion(UnresolvedCall call) { + try { + return callgraph.resolve(call); + } catch (CallResolutionException e) { + return null; + } + } } diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java index f29925433..c1c925162 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/BaseCallGraph.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -39,6 +40,8 @@ public abstract class BaseCallGraph extends Graph> callsites = new HashMap<>(); + private final Map resolvedCache = new IdentityHashMap<>(); + @Override public final void init(Program program) throws CallGraphConstructionException { this.program = program; @@ -62,6 +65,10 @@ public void registerCall(CFGCall call) { @Override public final Call resolve(UnresolvedCall call) throws CallResolutionException { + Call cached = resolvedCache.get(call); + if (cached != null) + return cached; + Collection targets = new ArrayList<>(); Collection nativeTargets = new ArrayList<>(); @@ -127,6 +134,8 @@ else if (nativeTargets.isEmpty()) callsites.computeIfAbsent(target, cm -> new HashSet<>()).add(call); } + resolvedCache.put(call, resolved); + return resolved; } From ac2ba727c43c9eb651b2e54d16a11f59996f92c2 Mon Sep 17 00:00:00 2001 From: lucaneg Date: Fri, 29 Oct 2021 17:17:26 +0200 Subject: [PATCH 3/3] Fixing import --- .../java/it/unive/lisa/interprocedural/callgraph/CallGraph.java | 1 + 1 file changed, 1 insertion(+) diff --git a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java index 326823f71..5cbb2f312 100644 --- a/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java +++ b/lisa/lisa-sdk/src/main/java/it/unive/lisa/interprocedural/callgraph/CallGraph.java @@ -2,6 +2,7 @@ import it.unive.lisa.program.Program; import it.unive.lisa.program.cfg.CodeMember; +import it.unive.lisa.program.cfg.statement.call.CFGCall; import it.unive.lisa.program.cfg.statement.call.Call; import it.unive.lisa.program.cfg.statement.call.UnresolvedCall; import java.util.Collection;