Skip to content

Commit 9f2a758

Browse files
committed
Add page for Boomerang scope
1 parent e17f753 commit 9f2a758

File tree

5 files changed

+330
-31
lines changed

5 files changed

+330
-31
lines changed

docs/boomerang/allocation_sites.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ If the optional is present, the `AllocVal` is added to the resulting allocation
1111

1212
When performing a backward analysis, Boomerang calls this method on each statement on each data-flow path.
1313
It provides three parameters to the method `getAllocationSite`:
14+
1415
- Method: The current method
1516
- Statement: The current statement that may contain an allocation site
1617
- Val: The current propagated data-flow fact
1718

1819
These parameters necessitate two checks that should be part of each allocation site implementation:
19-
1) Check whether the statement is an assignment
20-
2) Check whether the left operand of the assignment is equal to the propagated data-flow fact
20+
21+
- Check whether the statement is an assignment
22+
- Check whether the left operand of the assignment is equal to the propagated data-flow fact
2123

2224
The first point is relevant because an allocation site is defined as an assignment.
2325
The second aspect is relevant to avoid returning statements that are not relevant to the points-to analysis.

docs/boomerang/boomerang_setup.md

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
# Boomerang Setup
22

3-
Boomerang's purpose is the computation of points-to information for a variable on-demand. Starting at a specific statement, it traverses the program and its data-flow paths backwards until it finds an allocation site for the desired variable. While doing that, it computes relevant alias information.
3+
Boomerang's purpose is the computation of points-to information for a variable on-demand.
4+
Starting at a specific statement, it traverses the program and its data-flow paths backwards until it finds an allocation site for the desired variable.
5+
While doing that, it computes relevant alias information.
46

5-
In the following sections, we give an overview of relevant constructs and API calls. We highly recommend to take a look at the [Examples](./../boomerang/examples.md) to see the best way to combine these constructs.
7+
In the following sections, we give an overview of relevant constructs and API calls.
8+
We highly recommend to take a look at the [Examples](./../boomerang/examples.md) to see the best way to combine these constructs.
69

710
## Backward Queries
811

9-
Boomerang uses *backward queries* to compute relevant points-to information. A **BackwardQuery** consists of a statement `s` and a variable `v`. `s` is the starting statement where the backwards analysis starts and `v` is the data-flow fact to solve for.
12+
Boomerang uses *backward queries* to compute relevant points-to information.
13+
A **BackwardQuery** consists of a statement `s` and a variable `v`. `s` is the starting statement where the backwards analysis starts and `v` is the data-flow fact to solve for.
1014

11-
Backward queries can be easily constructed. However, due to Boomerang's scope implementation, we need to specify the corresponding control-flow graph edge with the starting statement `s` as target (see the [Boomerang Scopes](./../general/boomerang_scope.md)). With that, we can construct a backward query as follows:
15+
Backward queries can be easily constructed.
16+
However, due to Boomerang's scope implementation, we need to specify the corresponding control-flow graph edge with the starting statement `s` as target (see the [Boomerang Scopes](./../general/boomerang_scope.md)).
17+
With that, we can construct a backward query as follows:
1218

1319
```java
1420
public void createBackwardQuery(ControlFlowGraph.Edge, edge, Val fact) {
@@ -18,7 +24,7 @@ public void createBackwardQuery(ControlFlowGraph.Edge, edge, Val fact) {
1824

1925
## Running Boomerang
2026

21-
Boomerang requires a [FrameworkScope](./../general/framework_scopes.md) and a set [options](./../boomerang/options.md). With that, we can solve a backward query as follows:
27+
Boomerang requires a [FrameworkScope](./../general/framework_scopes.md) and a set [Options](./../boomerang/options.md). With that, we can solve a backward query as follows:
2228

2329
```java
2430
public void solveQuery(
@@ -32,24 +38,25 @@ public void solveQuery(
3238

3339
The call to `solve` solves the query and returns a wrapper for the results.
3440

35-
// TODO Use a box
36-
37-
!!! Important: A `Boomerang` instance can be used to solve exactly one query. If you want to solve multiple queries with the same instance, you have to set [allowMultipleQueries]() in the options to `true` and you have to call `unregisterAllListeners()` after each call to `solve`. This may look like this:
38-
39-
```java
40-
public void solveQueries(
41-
Collection<BackwardQuery> queries,
42-
FrameworkScope scope,
43-
BoomerangOptions options) {
44-
Boomerang solver = new Boomerang(scope, options);
41+
!!! Important:
42+
A `Boomerang` instance can be used to solve exactly one query.
43+
If you want to solve multiple queries with the same instance, you have to set [allowMultipleQueries]() in the options to `true` and you have to call `unregisterAllListeners()` after each call to `solve`.
44+
This may look like this:
45+
46+
```java
47+
public void solveQueries(
48+
Collection<BackwardQuery> queries,
49+
FrameworkScope scope,
50+
BoomerangOptions options) {
51+
Boomerang solver = new Boomerang(scope, options);
4552

46-
for (BackwardQuery query : queries) {
47-
BackwardBoomerangResults<NoWeight> results = solver.solve(query);
48-
// <Process or store the results>
49-
solver.unregisterAllListeners();
53+
for (BackwardQuery query : queries) {
54+
BackwardBoomerangResults<NoWeight> results = solver.solve(query);
55+
// <Process or store the results>
56+
solver.unregisterAllListeners();
57+
}
5058
}
51-
}
52-
```
59+
```
5360

5461
## Extracting Allocation Sites
5562

docs/general/boomerang_scope.md

Lines changed: 233 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,235 @@
11
# Boomerang Scope
22

3-
## AnalysisScope
3+
Boomerang defines its own scope that is not related to any static analysis framework.
4+
The scope consists of a set of interfaces and classes that specify relevant information required by Boomerang to perform its analyses.
5+
Currently, we provide a scope implementations for the static analysis frameworks [Soot](https://github.com/soot-oss/soot), [SootUp](https://github.com/soot-oss/sootup) and [Opal](https://github.com/opalj/opal) (see the [FrameworkScopes](framework_scopes.md)).
6+
The scopes contain implementations for all relevant interfaces and objects s.t. Boomerang can be used with those frameworks without the need of additional implementation.
7+
8+
## Dealing with Framework Objects
9+
10+
The Boomerang scope is designed to be as similar as possible to the analysis frameworks.
11+
That is, for most Boomerang scope object there is a corresponding object in the analysis framework that is used to implement the required interface methods.
12+
For example, the Boomerang scope has an abstract class `Method` that expects implementations to override certain methods:
13+
14+
```java
15+
public abstract class Method {
16+
17+
public abstract boolean isStaticInitializer();
18+
}
19+
```
20+
21+
The concrete scope implementations extend this class by delegating the corresponding objects and using them to override the required methods.
22+
For example, for the class `Method`, we have the following (shortened) implementations:
23+
24+
=== "Soot"
25+
```java
26+
public class JimpleMethod extends Method {
27+
// SootMethod is the corresponding method object in Soot
28+
private final SootMethod delegate;
29+
30+
@Override
31+
public boolean isStaticInitializer() {
32+
return delegate.isStaticInitializer();
33+
}
34+
}
35+
```
36+
37+
=== "SootUp"
38+
```java
39+
public class JimpleUpMethod extends Method {
40+
// JavaSootMethod is the corresponding method object in SootUp
41+
private final JavaSootMethod delegate;
42+
43+
@Override
44+
public boolean isStaticInitializer() {
45+
return delegate.isStaticInitializer();
46+
}
47+
```
48+
49+
=== "Opal"
50+
```scala
51+
// The method object from the br package is the corresponding object in Opal
52+
class OpalMethod(val delegate: org.opalj.br.Method) extends Method {
53+
54+
override def isStaticInitializer: Boolean = delegate.isStaticInitializer
55+
}
56+
```
57+
58+
With this setup, one can easily instantiate scope objects from the analysis framework objects and access delegated objects.
59+
The objects from each framework scope can be identified by their name:
60+
61+
- The scope objects for Soot are denoted with the prefix **Jimple** (e.g. `JimpleMethod`, `JimpleStatement` etc.)
62+
- The scope objects for SootUp are denoted with the prefix **JimpleUp** (e.g. `JimpleUpMethod`, `JimpleUpStatement` etc.)
63+
- The scope objects for Opal are denoted with the prefix **Opal** (e.g. `OpalMethod`, `OpalStatement` etc.)
64+
65+
To simplify the process, we provide a `ScopeConverter` for each framework scope.
66+
These utility classes have basic methods to construct scope objects and extract delegated objects.
67+
This concept may be relevant when working with Boomerang's results because Boomerang returns the general Boomerang scope object.
68+
For example, we can work with a `Method` and a `Statement` as follows:
69+
70+
=== "Soot"
71+
```java
72+
// Assumption: 'stmt' is a statement in 'method'
73+
public void scopeObjects(Scene scene, SootMethod sootMethod, Stmt stmt) {
74+
// Create a method and statement from the Boomerang scope for Soot
75+
Method jimpleMethod = SootScopeConverter.createJimpleMethod(sootMethod, scene);
76+
Statement jimpleStatement = SootScopeConverter.createJimpleStatement(stmt, jimpleMethod);
77+
78+
// Extract the delegated objects
79+
SootMethod extractedSootMethod = SootScopeConverter.extractSootMethod(jimpleMethod);
80+
Stmt extractedStmt = SootScopeConverter.extractSootStatement(jimpleStatement);
81+
}
82+
```
83+
84+
=== "SootUp"
85+
```java
86+
// Assumption: 'stmt' is a statement in 'method'
87+
public void scopeObjects(JavaView view, JavaSootMethod sootMethod, Stmt stmt) {
88+
// Create a method and statement from the Boomerang scope for SootUp
89+
Method jimpleUpMethod = SootUpScopeConverter.createJimpleUpMethod(sootMethod, scene);
90+
Statement jimpleUpStatement = SootUpScopeConverter.createJimpleUpStatement(stmt, jimpleUpMethod);
91+
92+
// Extract the delegated objects
93+
JavaSootMethod extractedSootUpMethod = SootUpScopeConverter.extractSootUpMethod(jimpleUpMethod);
94+
Stmt extractedStmt = SootUpScopeConverter.extractSootUpStatement(jimpleUpStatement);
95+
}
96+
```
97+
98+
=== "Opal"
99+
```scala
100+
// Assumption: 'stmt' is a statement in 'method'
101+
def scopeObjects(project: Project[_], method: Method, stmt: Stmt[TacLocal]): Unit = {
102+
// Create a method and statement from the Boomerang scope for Opal
103+
val opalMethod = OpalScopeConverter.createOpalMethod(method, project)
104+
val opalStatement = OpalScopeConverter.createOpalStatement(stmt, opalMethod)
105+
106+
// Extract the delegated objects
107+
val extractedMethod = OpalScopeConverter.extractOpalMethod(opalMethod);
108+
val extractedStmt = OpalScopeConverter.extractOpalStatement(opalStatement);
109+
}
110+
```
111+
112+
## CallGraph
113+
114+
The Boomerang scope contains its own call graph representation that is used to compute data-flows during the analysis.
115+
Each framework scope provides a parser that transforms a generated call graph into the corresponding Boomerang scope representation that is applied when instantiating a [FrameworkScope](framework_scopes.md).
116+
The corresponding call graphs are accessible from the scope instances as follows:
117+
118+
=== "Soot"
119+
```java
120+
SootFrameworkScope scope = new SootFrameworkScope(...);
121+
SootCallGraph callGraph = scope.getCallGraph();
122+
System.out.println("CallGraph has " + callGraph.size() + " edges");
123+
```
124+
125+
=== "SootUp"
126+
```java
127+
SootUpFrameworkScope scope = new SootUpFrameworkScope(...);
128+
SootUpCallGraph callGraph = scope.getCallGraph();
129+
System.out.println("CallGraph has " + callGraph.size() + " edges");
130+
```
131+
132+
=== "Opal"
133+
```scala
134+
val scope = new OpalFrameworkScope(...)
135+
val callGraph = scope.getCallGraph
136+
println(s"CallGraph has ${callGraph.size} edges");
137+
```
138+
139+
## DataFlowScope
140+
141+
The data-flow scope determines the program's scope that is analyzed.
142+
By default, Boomerang computes data-flows along the complete reachable program.
143+
However, in many scenarios, only a subset of the target program is from interest during the analysis.
144+
For example, we are only interested in data-flow paths that belong to the application.
145+
To this end, the data-flow scope allows the exclusion of methods to reduce the data-flows.
146+
147+
A `DataFlowScope` defines two methods `isExcluded(...)` that evaluate whether a method should be excluded from the analysis.
148+
Boomerang calls these methods at each call site and when entering a new method.
149+
If the methods evaluate to `true`, Boomerang skips the methods and steps over corresponding call site.
150+
For example, we can define a `DataFlowScope` that excludes non-application classes and methods with the name `callSite` as follows:
151+
152+
```java
153+
public class ExtendedDataFlowScope implements DataFlowScope {
154+
155+
@Override
156+
public boolean isExcluded(Method method) {
157+
// Exclude methods from non-application classes
158+
if (!method.getDeclaringClass().isApplicationClass()) return true;
159+
// Exclude methods with the name 'callSite'
160+
if (method.getName().equals("callSite")) return true;
161+
162+
// All other methods should be analyzed
163+
return false;
164+
}
165+
166+
@Override
167+
public boolean isExcluded(DeclaredMethod declaredMethod) {
168+
// Exclude call sites from non-application classes
169+
if (!method.getDeclaringClass().isApplicationClass()) return true;
170+
// Exclude call sites with the name 'callSite'
171+
if (method.getName().equals("callSite")) return true;
172+
173+
// All other call sites should be analyzed
174+
return false;
175+
}
176+
}
177+
```
178+
179+
// TODO Example
180+
181+
!!! Important
182+
You should always make sure that potential allocation sites are considered when excluding call sites.
183+
In the example above, we exclude the call to `System.getProperty` because the class `java.lang.System` is not an application class.
184+
At this point, the data-flow stops because Boomerang cannot compute the returned value.
185+
To deal with such cases, we provide a solution when defining the [AllocationSite](./../boomerang/allocation_sites.md#allocation-site-with-dataflowscope)
186+
187+
## Queries
188+
189+
## AnalysisScope
190+
191+
Boomerang provides an `AnalysisScope` to compute initial queries along the complete reachable program.
192+
It traverses the call graph starting at the entry points that are defined in the [FrameworkScopes](framework_scopes.md) while respecting their `DataFlowScopes`.
193+
194+
The `AnalysisScope` calls a method `generateSeed` on each reachable control-flow graph edge where we can decide whether a query should be generated.
195+
For example, we may be interested in the backward analysis of the first parameter of calls to a method `sink`.
196+
Then, we can implement an `AnalysisScope` as follows:
197+
198+
```java
199+
public class SinkAnalysisScope {
200+
201+
public SinkAnalysisScope(FrameworkScope scope) {
202+
super(scope);
203+
}
204+
205+
@Override
206+
protected Collection<? extends Query> generate(ControlFlowGraph.Edge edge) {
207+
// Backward solve means that the current statement is the target
208+
Statement stmt = edge.getTarget();
209+
if (stmt.containsInvokeExpr()) {
210+
InvokeExpr invokeExpr = stmt.getInvokeExpr();
211+
DeclaredMethod declaredMethod = invokeExpr.getDeclaredMethod();
212+
213+
if (declaredMethod.getName().equals("sink") && invokeExpr.getArgs().size > 0) {
214+
Val arg = invokeExpr.getArg(0);
215+
216+
// Create the query to analyze the first parameter from the call to 'sink'
217+
BackwardQuery query = BackwardQuery.make(edge, arg);
218+
return Collections.singleton(query);
219+
}
220+
}
221+
222+
return Collections.emptySet();
223+
}
224+
}
225+
```
226+
227+
We can use this implementation to compute relevant queries across the complete program as follows (see [FrameworkScopes](framework_scopes.md) on how to initialize a framework scope):
228+
229+
```java
230+
FrameworkScope scope = ...;
231+
AnalysisScope analysisScope = new SinkAnalysisScope(scope);
232+
Collection<Query> queries = analysisScope.computeSeeds();
233+
```
234+
235+
The collection `queries` contains all queries for a statement `sink(v, ...)` that can be solved with the [Boomerang Solver](./../boomerang/boomerang_setup.md).

0 commit comments

Comments
 (0)