Skip to content

Commit 4f5d6e3

Browse files
committed
Java: Adopt shared SSA data-flow integration (WIP)
1 parent dfda6e8 commit 4f5d6e3

File tree

20 files changed

+935
-956
lines changed

20 files changed

+935
-956
lines changed

java/ql/lib/semmle/code/java/dataflow/SSA.qll

Lines changed: 51 additions & 822 deletions
Large diffs are not rendered by default.

java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
5555
}
5656

5757
cached
58-
private module SsaImpl {
58+
private module BaseSsaImpl {
5959
/** Gets the destination variable of an update of a tracked variable. */
6060
cached
6161
BaseSsaSourceVariable getDestVar(VariableUpdate upd) {
@@ -436,7 +436,7 @@ private module SsaImpl {
436436
}
437437
}
438438

439-
private import SsaImpl
439+
private import BaseSsaImpl
440440
private import SsaDefReaches
441441
import SsaPublic
442442

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import FlowSummaryImpl as FlowSummaryImpl
1010
private import DataFlowImplCommon as DataFlowImplCommon
1111
private import semmle.code.java.controlflow.Guards
1212
private import semmle.code.java.dataflow.RangeUtils
13+
private import SsaImpl as SsaImpl
1314

1415
/** Gets a string for approximating the name of a field. */
1516
string approximateFieldContent(FieldContent fc) { result = fc.getField().getName().prefix(1) }
@@ -21,6 +22,30 @@ private predicate deadcode(Expr e) {
2122
)
2223
}
2324

25+
module SsaFlow {
26+
module Impl = SsaImpl::DataFlowIntegration;
27+
28+
Impl::Node asNode(Node n) {
29+
n = TSsaNode(result)
30+
or
31+
result.(Impl::ExprNode).getExpr() = n.asExpr()
32+
or
33+
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
34+
or
35+
TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n
36+
}
37+
38+
predicate localFlowStep(
39+
SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep
40+
) {
41+
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
42+
}
43+
44+
predicate localMustFlowStep(SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
45+
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
46+
}
47+
}
48+
2449
cached
2550
private module Cached {
2651
cached
@@ -31,6 +56,7 @@ private module Cached {
3156
not e.getType() instanceof VoidType and
3257
not e.getParent*() instanceof Annotation
3358
} or
59+
TSsaNode(SsaFlow::Impl::SsaNode node) or
3460
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
3561
TImplicitVarargsArray(Call c) {
3662
c.getCallee().isVarargs() and
@@ -137,6 +163,8 @@ module Public {
137163
result = this.(FieldValueNode).getField().getType()
138164
or
139165
result instanceof TypeObject and this instanceof AdditionalNode
166+
or
167+
result = this.(SsaNode).getDefinitionExt().getSourceVariable().getType()
140168
}
141169

142170
/** Gets the callable in which this node occurs. */
@@ -198,6 +226,18 @@ module Public {
198226
abstract predicate isParameterOf(DataFlowCallable c, int pos);
199227
}
200228

229+
class SsaNode extends Node, TSsaNode {
230+
private SsaFlow::Impl::SsaNode node;
231+
232+
SsaNode() { this = TSsaNode(node) }
233+
234+
SsaImpl::Impl::DefinitionExt getDefinitionExt() { result = node.getDefinitionExt() }
235+
236+
override Location getLocation() { result = node.getLocation() }
237+
238+
override string toString() { result = node.toString() }
239+
}
240+
201241
/**
202242
* A parameter, viewed as a node in a data flow graph.
203243
*/
@@ -398,7 +438,8 @@ module Private {
398438
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
399439
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
400440
result.asFieldScope() = n.(FieldValueNode).getField() or
401-
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable()
441+
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable() or
442+
result.asCallable() = n.(SsaNode).getDefinitionExt().getBasicBlock().getEnclosingCallable()
402443
}
403444

404445
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,11 @@ predicate forceHighPrecision(Content c) {
610610
}
611611

612612
/** Holds if `n` should be hidden from path explanations. */
613-
predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode }
613+
predicate nodeIsHidden(Node n) {
614+
n instanceof FlowSummaryNode
615+
or
616+
n instanceof SsaNode
617+
}
614618

615619
class LambdaCallKind = Method; // the "apply" method in the functional interface
616620

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private import semmle.code.java.dataflow.FlowSummary
1212
private import semmle.code.java.dataflow.InstanceAccess
1313
private import FlowSummaryImpl as FlowSummaryImpl
1414
private import TaintTrackingUtil as TaintTrackingUtil
15+
private import SsaImpl as SsaImpl
1516
private import DataFlowNodes
1617
import DataFlowNodes::Public
1718

@@ -99,6 +100,10 @@ predicate hasNonlocalValue(FieldRead fr) {
99100
)
100101
}
101102

103+
private predicate capturedVariableRead(Node n) {
104+
n.asExpr().(VarRead).getVariable() instanceof CapturedVariable
105+
}
106+
102107
cached
103108
private module Cached {
104109
/**
@@ -108,7 +113,7 @@ private module Cached {
108113
predicate localFlowStep(Node node1, Node node2) {
109114
simpleLocalFlowStep0(node1, node2, _)
110115
or
111-
adjacentUseUse(node1.asExpr(), node2.asExpr())
116+
SsaFlow::localFlowStep(_, node1, node2, _)
112117
or
113118
// Simple flow through library code is included in the exposed local
114119
// step relation, even though flow is technically inter-procedural
@@ -125,13 +130,31 @@ private module Cached {
125130
predicate simpleLocalFlowStep(Node node1, Node node2, string model) {
126131
simpleLocalFlowStep0(node1, node2, model)
127132
or
133+
exists(boolean isUseStep |
134+
SsaFlow::localFlowStep(_, node1, node2, isUseStep) and
135+
not capturedVariableRead(node2) and
136+
model = ""
137+
|
138+
isUseStep = false
139+
or
140+
not exists(FieldRead fr |
141+
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
142+
) and
143+
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _)
144+
)
145+
or
128146
any(AdditionalValueStep a).step(node1, node2) and
129147
pragma[only_bind_out](node1.getEnclosingCallable()) =
130148
pragma[only_bind_out](node2.getEnclosingCallable()) and
131149
model = "AdditionalValueStep" and
132150
// prevent recursive call
133151
(any(AdditionalValueStep a).step(_, _) implies any())
134152
}
153+
154+
cached
155+
Node getABarrierNode(Guard guard, SsaVariable def, boolean branch) {
156+
SsaFlow::asNode(result) = SsaImpl::DataFlowIntegration::getABarrierNode(guard, def, branch)
157+
}
135158
}
136159

137160
/**
@@ -147,14 +170,7 @@ predicate localMustFlowStep(Node node1, Node node2) {
147170
node2.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getEnclosingCallable()
148171
)
149172
or
150-
exists(SsaImplicitInit init |
151-
init.isParameterDefinition(node1.asParameter()) and init.getAUse() = node2.asExpr()
152-
)
153-
or
154-
exists(SsaExplicitUpdate upd |
155-
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() and
156-
upd.getAUse() = node2.asExpr()
157-
)
173+
SsaFlow::localMustFlowStep(_, node1, node2)
158174
or
159175
node2.asExpr().(CastingExpr).getExpr() = node1.asExpr()
160176
or
@@ -169,10 +185,6 @@ predicate localMustFlowStep(Node node1, Node node2) {
169185

170186
import Cached
171187

172-
private predicate capturedVariableRead(Node n) {
173-
n.asExpr().(VarRead).getVariable() instanceof CapturedVariable
174-
}
175-
176188
/**
177189
* Holds if there is a data flow step from `e1` to `e2` that only steps from
178190
* child to parent in the AST.
@@ -214,34 +226,8 @@ predicate simpleAstFlowStep(Expr e1, Expr e2) {
214226
private predicate simpleLocalFlowStep0(Node node1, Node node2, string model) {
215227
(
216228
TaintTrackingUtil::forceCachingInSameStage() and
217-
// Variable flow steps through adjacent def-use and use-use pairs.
218-
exists(SsaExplicitUpdate upd |
219-
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
220-
upd.getDefiningExpr().(AssignOp) = node1.asExpr() or
221-
upd.getDefiningExpr().(RecordBindingVariableExpr) = node1.asExpr()
222-
|
223-
node2.asExpr() = upd.getAFirstUse() and
224-
not capturedVariableRead(node2)
225-
)
226-
or
227-
exists(SsaImplicitInit init |
228-
init.isParameterDefinition(node1.asParameter()) and
229-
node2.asExpr() = init.getAFirstUse() and
230-
not capturedVariableRead(node2)
231-
)
232-
or
233-
adjacentUseUse(node1.asExpr(), node2.asExpr()) and
234-
not exists(FieldRead fr |
235-
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
236-
) and
237-
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _) and
238-
not capturedVariableRead(node2)
239-
or
240229
ThisFlow::adjacentThisRefs(node1, node2)
241230
or
242-
adjacentUseUse(node1.(PostUpdateNode).getPreUpdateNode().asExpr(), node2.asExpr()) and
243-
not capturedVariableRead(node2)
244-
or
245231
ThisFlow::adjacentThisRefs(node1.(PostUpdateNode).getPreUpdateNode(), node2)
246232
or
247233
simpleAstFlowStep(node1.asExpr(), node2.asExpr())
@@ -401,13 +387,16 @@ signature predicate guardChecksSig(Guard g, Expr e, boolean branch);
401387
* in data flow and taint tracking.
402388
*/
403389
module BarrierGuard<guardChecksSig/3 guardChecks> {
390+
pragma[nomagic]
391+
private predicate guardChecksSsaDef(Guard g, SsaVariable v, boolean branch) {
392+
guardChecks(g, v.getAUse(), branch)
393+
}
394+
404395
/** Gets a node that is safely guarded by the given guard check. */
405396
Node getABarrierNode() {
406-
exists(Guard g, SsaVariable v, boolean branch, VarRead use |
407-
guardChecks(g, v.getAUse(), branch) and
408-
use = v.getAUse() and
409-
g.controls(use.getBasicBlock(), branch) and
410-
result.asExpr() = use
397+
exists(Guard g, SsaVariable v, boolean branch |
398+
guardChecksSsaDef(g, v, branch) and
399+
result = getABarrierNode(g, v, branch)
411400
)
412401
}
413402
}

0 commit comments

Comments
 (0)