Skip to content

Commit 524d154

Browse files
author
Marcus Huewe
committed
Improve the on the fly call graph code a bit
Note that the goal of this commit is to make the testcases pass. That is, it is perfectly possible that this commit just fixes the symptoms instead of the root causes. For instance, it is unclear (to me!) why the use of a LinkedHashMultimap instead of a HashMultimap makes a difference in the WeightedPAutomaton class. This whole commit is basically trial and error in combination with reading code and doing "educated" guesses (that's also one reason why this commit message is pretty concise). Big FAT warning: the on the fly call graph option is an experimental feature.
1 parent 0c00d2b commit 524d154

23 files changed

+546
-286
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
framework: [ Soot, SootUp, Opal ]
18+
enableOnTheFlyCallGraph: [ false, true ]
1819
runs-on: ubuntu-latest
1920
steps:
2021
- name: Checkout source code
@@ -26,4 +27,4 @@ jobs:
2627
java-package: jdk
2728
java-version: 11
2829
- name: Build with Maven
29-
run: mvn -B clean verify -DtestSetup=${{ matrix.framework }}
30+
run: mvn -B clean verify -DtestSetup=${{ matrix.framework }} -DenableOnTheFlyCallGraph=${{ matrix.enableOnTheFlyCallGraph }}

WPDS/src/main/java/wpds/impl/StackListener.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public StackListener(WeightedPAutomaton<N, D, W> weightedPAutomaton, D state, N
3838
public void onOutTransitionAdded(
3939
Transition<N, D> t, W w, WeightedPAutomaton<N, D, W> weightedPAutomaton) {
4040
if (t.getLabel().equals(aut.epsilon())) return;
41+
/*
42+
* XXX: Does this always work, if the BoomerangOptions.onTheFlyCallGraph
43+
* option is active? For now, the testcases seem to pass - reconsider this
44+
* (that is, try to use an InitialStateListener).
45+
*/
4146
if (this.aut.getInitialStates().contains(t.getTarget())) {
4247
if (t.getLabel().equals(source)) {
4348
anyContext(source);

WPDS/src/main/java/wpds/impl/WeightedPAutomaton.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import com.google.common.base.Joiner;
1818
import com.google.common.base.Stopwatch;
1919
import com.google.common.collect.HashBasedTable;
20-
import com.google.common.collect.HashMultimap;
20+
import com.google.common.collect.LinkedHashMultimap;
2121
import com.google.common.collect.Lists;
2222
import com.google.common.collect.Maps;
2323
import com.google.common.collect.Multimap;
@@ -42,6 +42,7 @@
4242
import pathexpression.RegEx;
4343
import wpds.interfaces.ForwardDFSEpsilonVisitor;
4444
import wpds.interfaces.ForwardDFSVisitor;
45+
import wpds.interfaces.InitialStateListener;
4546
import wpds.interfaces.ReachabilityListener;
4647
import wpds.interfaces.State;
4748
import wpds.interfaces.WPAStateListener;
@@ -57,13 +58,14 @@ public abstract class WeightedPAutomaton<N extends Location, D extends State, W
5758
protected Set<Transition<N, D>> transitions = new LinkedHashSet<>();
5859
// set F in paper [Reps2003]
5960
protected Set<D> finalState = new LinkedHashSet<>();
60-
protected Multimap<D, D> initialStatesToSource = HashMultimap.create();
61+
protected Multimap<D, D> initialStatesToSource = LinkedHashMultimap.create();
6162
// set P in paper [Reps2003]
6263
protected Set<D> states = new LinkedHashSet<>();
63-
private final Multimap<D, Transition<N, D>> transitionsOutOf = HashMultimap.create();
64-
private final Multimap<D, Transition<N, D>> transitionsInto = HashMultimap.create();
64+
private final Multimap<D, Transition<N, D>> transitionsOutOf = LinkedHashMultimap.create();
65+
private final Multimap<D, Transition<N, D>> transitionsInto = LinkedHashMultimap.create();
6566
private final Set<WPAUpdateListener<N, D, W>> listeners = new LinkedHashSet<>();
66-
private final Multimap<D, WPAStateListener<N, D, W>> stateListeners = HashMultimap.create();
67+
private final Set<InitialStateListener<D>> initialStateListeners = new LinkedHashSet<>();
68+
private final Multimap<D, WPAStateListener<N, D, W>> stateListeners = LinkedHashMultimap.create();
6769
private final Map<D, ForwardDFSVisitor<N, D, W>> stateToDFS = Maps.newHashMap();
6870
private final Map<D, ForwardDFSVisitor<N, D, W>> stateToEpsilonDFS = Maps.newHashMap();
6971
private final Set<WeightedPAutomaton<N, D, W>> nestedAutomatons = new LinkedHashSet<>();
@@ -371,6 +373,15 @@ public void registerListener(WPAUpdateListener<N, D, W> listener) {
371373
}
372374
}
373375

376+
public void registerListener(InitialStateListener<D> listener) {
377+
if (!initialStateListeners.add(listener)) {
378+
return;
379+
}
380+
for (D initialState : Lists.newArrayList(getInitialStates())) {
381+
listener.onInitialStateAdded(initialState);
382+
}
383+
}
384+
374385
private static int count = 0;
375386

376387
private void increaseListenerCount(WPAStateListener<N, D, W> l) {
@@ -825,7 +836,13 @@ public boolean addUnbalancedState(D state, D parent) {
825836
}
826837

827838
public boolean addInitialState(D state) {
828-
return initialStatesToSource.put(state, state);
839+
if (!initialStatesToSource.get(state).add(state)) {
840+
return false;
841+
}
842+
for (InitialStateListener<D> listener : Lists.newArrayList(initialStateListeners)) {
843+
listener.onInitialStateAdded(state);
844+
}
845+
return true;
829846
}
830847

831848
public void unregisterAllListeners() {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* *****************************************************************************
3+
* Copyright (c) 2018 Fraunhofer IEM, Paderborn, Germany
4+
* <p>
5+
* This program and the accompanying materials are made available under the
6+
* terms of the Eclipse Public License 2.0 which is available at
7+
* http://www.eclipse.org/legal/epl-2.0.
8+
* <p>
9+
* SPDX-License-Identifier: EPL-2.0
10+
* <p>
11+
* Contributors:
12+
* Johannes Spaeth - initial API and implementation
13+
* *****************************************************************************
14+
*/
15+
package wpds.interfaces;
16+
17+
public interface InitialStateListener<D extends State> {
18+
void onInitialStateAdded(D initialState);
19+
}

boomerangPDS/src/main/java/boomerang/WeightedBoomerang.java

Lines changed: 94 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -267,21 +267,24 @@ public void getPredecessor(Statement pred) {
267267
BackwardQuery bwq =
268268
BackwardQuery.make(new Edge(pred, stmt), stmt.getInvokeExpr().getArg(0));
269269
backwardSolve(bwq);
270-
for (ForwardQuery q : Lists.newArrayList(queryToSolvers.keySet())) {
271-
if (queryToSolvers.get(q).getReachedStates().contains(bwq.asNode())) {
272-
AllocVal v = q.getAllocVal();
273-
if (v.getAllocVal().isStringConstant()) {
274-
String key = v.getAllocVal().getStringValue();
275-
backwardSolverIns.propagate(
276-
node,
277-
new PushNode<>(
278-
new Edge(pred, stmt),
279-
stmt.getInvokeExpr().getBase(),
280-
StringBasedField.getInstance(key),
281-
PDSSystem.FIELDS));
282-
}
283-
}
284-
}
270+
WeightedBoomerang.this.registerSolverCreationListener(
271+
(q, solver) ->
272+
solver.registerListener(
273+
n -> {
274+
if (n.equals(bwq.asNode()) && q instanceof ForwardQuery) {
275+
AllocVal v = ((ForwardQuery) q).getAllocVal();
276+
if (v.getAllocVal().isStringConstant()) {
277+
String key = v.getAllocVal().getStringValue();
278+
backwardSolverIns.propagate(
279+
node,
280+
new PushNode<>(
281+
new Edge(pred, stmt),
282+
stmt.getInvokeExpr().getBase(),
283+
StringBasedField.getInstance(key),
284+
PDSSystem.FIELDS));
285+
}
286+
}
287+
}));
285288
}
286289
});
287290
}
@@ -296,21 +299,25 @@ public void getPredecessor(Statement pred) {
296299
BackwardQuery bwq =
297300
BackwardQuery.make(new Edge(pred, stmt), stmt.getInvokeExpr().getArg(0));
298301
backwardSolve(bwq);
299-
for (ForwardQuery q : Lists.newArrayList(queryToSolvers.keySet())) {
300-
if (queryToSolvers.get(q).getReachedStates().contains(bwq.asNode())) {
301-
AllocVal v = q.getAllocVal();
302-
303-
if (v.getAllocVal().isStringConstant()) {
304-
String key = v.getAllocVal().getStringValue();
305-
NodeWithLocation<Edge, Val, Field> succNode =
306-
new NodeWithLocation<>(
307-
new Edge(pred, stmt),
308-
stmt.getInvokeExpr().getArg(1),
309-
StringBasedField.getInstance(key));
310-
backwardSolverIns.propagate(node, new PopNode<>(succNode, PDSSystem.FIELDS));
311-
}
312-
}
313-
}
302+
WeightedBoomerang.this.registerSolverCreationListener(
303+
(q, solver) ->
304+
solver.registerListener(
305+
n -> {
306+
if (n.equals(bwq.asNode()) && q instanceof ForwardQuery) {
307+
AllocVal v = ((ForwardQuery) q).getAllocVal();
308+
309+
if (v.getAllocVal().isStringConstant()) {
310+
String key = v.getAllocVal().getStringValue();
311+
NodeWithLocation<Edge, Val, Field> succNode =
312+
new NodeWithLocation<>(
313+
new Edge(pred, stmt),
314+
stmt.getInvokeExpr().getArg(1),
315+
StringBasedField.getInstance(key));
316+
backwardSolverIns.propagate(
317+
node, new PopNode<>(succNode, PDSSystem.FIELDS));
318+
}
319+
}
320+
}));
314321
}
315322
});
316323
}
@@ -325,55 +332,60 @@ protected void handleMapsForward(ForwardBoomerangSolver<W> solver, Node<Edge, Va
325332
if (stmt.getInvokeExpr().getBase().equals(node.fact())) {
326333
BackwardQuery bwq = BackwardQuery.make(node.stmt(), stmt.getInvokeExpr().getArg(0));
327334
backwardSolve(bwq);
328-
cfg.addSuccsOfListener(
329-
new SuccessorListener(stmt) {
330-
@Override
331-
public void getSuccessor(Statement succ) {
332-
for (ForwardQuery q : Lists.newArrayList(queryToSolvers.keySet())) {
333-
if (queryToSolvers.get(q).getReachedStates().contains(bwq.asNode())) {
334-
AllocVal v = q.getAllocVal();
335-
336-
if (v.getAllocVal().isStringConstant()) {
337-
String key = v.getAllocVal().getStringValue();
338-
NodeWithLocation<Edge, Val, Field> succNode =
339-
new NodeWithLocation<>(
340-
new Edge(stmt, succ),
341-
stmt.getLeftOp(),
342-
StringBasedField.getInstance(key));
343-
solver.propagate(node, new PopNode<>(succNode, PDSSystem.FIELDS));
344-
}
345-
}
346-
}
347-
}
348-
});
335+
registerSolverCreationListener(
336+
(q, solverForKeyArg) ->
337+
solverForKeyArg.registerListener(
338+
n ->
339+
cfg.addSuccsOfListener(
340+
new SuccessorListener(stmt) {
341+
@Override
342+
public void getSuccessor(Statement succ) {
343+
if (n.equals(bwq.asNode()) && q instanceof ForwardQuery) {
344+
AllocVal v = ((ForwardQuery) q).getAllocVal();
345+
346+
if (v.getAllocVal().isStringConstant()) {
347+
String key = v.getAllocVal().getStringValue();
348+
NodeWithLocation<Edge, Val, Field> succNode =
349+
new NodeWithLocation<>(
350+
new Edge(stmt, succ),
351+
stmt.getLeftOp(),
352+
StringBasedField.getInstance(key));
353+
solver.propagate(
354+
node, new PopNode<>(succNode, PDSSystem.FIELDS));
355+
}
356+
}
357+
}
358+
})));
349359
}
350360
}
351361
if (stmt.getInvokeExpr().getDeclaredMethod().toMethodWrapper().equals(MAP_PUT_SIGNATURE)) {
352362
if (stmt.getInvokeExpr().getArg(1).equals(node.fact())) {
353363

354364
BackwardQuery bwq = BackwardQuery.make(node.stmt(), stmt.getInvokeExpr().getArg(0));
355365
backwardSolve(bwq);
356-
cfg.addSuccsOfListener(
357-
new SuccessorListener(stmt) {
358-
@Override
359-
public void getSuccessor(Statement succ) {
360-
for (ForwardQuery q : Lists.newArrayList(queryToSolvers.keySet())) {
361-
if (queryToSolvers.get(q).getReachedStates().contains(bwq.asNode())) {
362-
AllocVal v = q.getAllocVal();
363-
if (v.getAllocVal().isStringConstant()) {
364-
String key = v.getAllocVal().getStringValue();
365-
solver.propagate(
366-
node,
367-
new PushNode<>(
368-
new Edge(stmt, succ),
369-
stmt.getInvokeExpr().getBase(),
370-
StringBasedField.getInstance(key),
371-
PDSSystem.FIELDS));
372-
}
373-
}
374-
}
375-
}
376-
});
366+
registerSolverCreationListener(
367+
(q, solverForKeyArg) ->
368+
solverForKeyArg.registerListener(
369+
n ->
370+
cfg.addSuccsOfListener(
371+
new SuccessorListener(stmt) {
372+
@Override
373+
public void getSuccessor(Statement succ) {
374+
if (n.equals(bwq.asNode()) && q instanceof ForwardQuery) {
375+
AllocVal v = ((ForwardQuery) q).getAllocVal();
376+
if (v.getAllocVal().isStringConstant()) {
377+
String key = v.getAllocVal().getStringValue();
378+
solver.propagate(
379+
node,
380+
new PushNode<>(
381+
new Edge(stmt, succ),
382+
stmt.getInvokeExpr().getBase(),
383+
StringBasedField.getInstance(key),
384+
PDSSystem.FIELDS));
385+
}
386+
}
387+
}
388+
})));
377389
}
378390
}
379391
}
@@ -988,10 +1000,14 @@ public ForwardBoomerangResults<W> solve(ForwardQuery query) {
9881000
}
9891001

9901002
public BackwardBoomerangResults<W> solve(BackwardQuery query) {
991-
return solve(query, true);
1003+
return solve(query, true, true);
9921004
}
9931005

9941006
public BackwardBoomerangResults<W> solve(BackwardQuery query, boolean timing) {
1007+
return solve(query, timing, true);
1008+
}
1009+
1010+
public BackwardBoomerangResults<W> solve(BackwardQuery query, boolean timing, boolean fallback) {
9951011
if (!options.allowMultipleQueries() && solving) {
9961012
throw new RuntimeException(
9971013
"One cannot re-use the same Boomerang solver for more than one query, unless option allowMultipleQueries is enabled. If allowMultipleQueries is enabled, ensure to call unregisterAllListeners() on this instance upon termination of all queries.");
@@ -1005,6 +1021,9 @@ public BackwardBoomerangResults<W> solve(BackwardQuery query, boolean timing) {
10051021
queryGraph.addRoot(query);
10061022
LOGGER.trace("Starting backward analysis of: {}", query);
10071023
backwardSolve(query);
1024+
if (fallback) {
1025+
icfg.computeFallback();
1026+
}
10081027
} catch (BoomerangTimeoutException e) {
10091028
timedout = true;
10101029
LOGGER.trace("Timeout ({}) of query: {} ", analysisWatch, query);
@@ -1037,6 +1056,7 @@ public BackwardBoomerangResults<W> solveUnderScope(
10371056
LOGGER.trace("Starting backward analysis of: {}", query);
10381057
backwardSolve(query);
10391058
queryGraph.addEdge(parentQuery, triggeringNode, query);
1059+
icfg.computeFallback();
10401060
this.debugOutput();
10411061
} catch (BoomerangTimeoutException e) {
10421062
timedout = true;
@@ -1064,6 +1084,7 @@ public ForwardBoomerangResults<W> solveUnderScope(
10641084
LOGGER.trace("Starting forward analysis of: {}", query);
10651085
forwardSolve(query);
10661086
queryGraph.addEdge(parentQuery, triggeringNode, query);
1087+
icfg.computeFallback();
10671088
LOGGER.trace(
10681089
"Query terminated in {} ({}), visited methods {}",
10691090
analysisWatch,

boomerangPDS/src/main/java/boomerang/callgraph/BackwardsObservableICFG.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void computeFallback() {
7777
}
7878

7979
@Override
80-
public void addEdges(Edge e) {
81-
this.delegate.addEdges(e);
80+
public void addEdge(Edge e) {
81+
this.delegate.addEdge(e);
8282
}
8383
}

0 commit comments

Comments
 (0)