Skip to content

Commit 033879f

Browse files
authored
Merge pull request #1639 from aschackmull/java/in-out-barriers
Java/C++/C# DataFlow: Add support for in/out barriers on sources and sinks.
2 parents 077f372 + a80cb26 commit 033879f

File tree

19 files changed

+1560
-415
lines changed

19 files changed

+1560
-415
lines changed

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ import DataFlowImplSpecific::Public
2727
* // Optionally override `isAdditionalFlowStep`.
2828
* }
2929
* ```
30+
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
31+
* the edges are those data-flow steps that preserve the value of the node
32+
* along with any additional edges defined by `isAdditionalFlowStep`.
33+
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
34+
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
35+
* and/or out-going edges from those nodes, respectively.
3036
*
3137
* Then, to query whether there is flow between some `source` and `sink`,
3238
* write
@@ -54,11 +60,20 @@ abstract class Configuration extends string {
5460
*/
5561
abstract predicate isSink(Node sink);
5662

57-
/** Holds if data flow through `node` is prohibited. */
63+
/**
64+
* Holds if data flow through `node` is prohibited. This completely removes
65+
* `node` from the data flow graph.
66+
*/
5867
predicate isBarrier(Node node) { none() }
5968

60-
/** Holds if data flow from `node1` to `node2` is prohibited. */
61-
predicate isBarrierEdge(Node node1, Node node2) { none() }
69+
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
70+
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
71+
72+
/** Holds if data flow into `node` is prohibited. */
73+
predicate isBarrierIn(Node node) { none() }
74+
75+
/** Holds if data flow out of `node` is prohibited. */
76+
predicate isBarrierOut(Node node) { none() }
6277

6378
/**
6479
* Holds if the additional flow step from `node1` to `node2` must be taken
@@ -103,6 +118,26 @@ abstract class Configuration extends string {
103118
deprecated predicate hasFlowBackward(Node source, Node sink) { hasFlow(source, sink) }
104119
}
105120

121+
private predicate inBarrier(Node node, Configuration config) {
122+
config.isBarrierIn(node) and
123+
config.isSource(node)
124+
}
125+
126+
private predicate outBarrier(Node node, Configuration config) {
127+
config.isBarrierOut(node) and
128+
config.isSink(node)
129+
}
130+
131+
private predicate fullBarrier(Node node, Configuration config) {
132+
config.isBarrier(node)
133+
or
134+
config.isBarrierIn(node) and
135+
not config.isSource(node)
136+
or
137+
config.isBarrierOut(node) and
138+
not config.isSink(node)
139+
}
140+
106141
private class AdditionalFlowStepSource extends Node {
107142
AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
108143
}
@@ -119,22 +154,46 @@ private predicate isAdditionalFlowStep(
119154
* Holds if data can flow in one local step from `node1` to `node2`.
120155
*/
121156
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
122-
localFlowStep(node1, node2) and not config.isBarrierEdge(node1, node2)
157+
localFlowStep(node1, node2) and
158+
not outBarrier(node1, config) and
159+
not inBarrier(node2, config) and
160+
not fullBarrier(node1, config) and
161+
not fullBarrier(node2, config)
123162
}
124163

125164
/**
126165
* Holds if the additional step from `node1` to `node2` does not jump between callables.
127166
*/
128167
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
129-
isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config)
168+
isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
169+
not outBarrier(node1, config) and
170+
not inBarrier(node2, config) and
171+
not fullBarrier(node1, config) and
172+
not fullBarrier(node2, config)
173+
}
174+
175+
/**
176+
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
177+
*/
178+
private predicate jumpStep(Node node1, Node node2, Configuration config) {
179+
jumpStep(node1, node2) and
180+
not outBarrier(node1, config) and
181+
not inBarrier(node2, config) and
182+
not fullBarrier(node1, config) and
183+
not fullBarrier(node2, config)
130184
}
131185

132186
/**
133187
* Holds if the additional step from `node1` to `node2` jumps between callables.
134188
*/
135189
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
136-
exists(DataFlowCallable callable1 | isAdditionalFlowStep(node1, node2, callable1, config) |
137-
node2.getEnclosingCallable() != callable1
190+
exists(DataFlowCallable callable1 |
191+
isAdditionalFlowStep(node1, node2, callable1, config) and
192+
node2.getEnclosingCallable() != callable1 and
193+
not outBarrier(node1, config) and
194+
not inBarrier(node2, config) and
195+
not fullBarrier(node1, config) and
196+
not fullBarrier(node2, config)
138197
)
139198
}
140199

@@ -154,7 +213,7 @@ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
154213
* ignoring call contexts.
155214
*/
156215
private predicate nodeCandFwd1(Node node, boolean stored, Configuration config) {
157-
not config.isBarrier(node) and
216+
not fullBarrier(node, config) and
158217
(
159218
config.isSource(node) and stored = false
160219
or
@@ -171,7 +230,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
171230
or
172231
exists(Node mid |
173232
nodeCandFwd1(mid, stored, config) and
174-
jumpStep(mid, node)
233+
jumpStep(mid, node, config)
175234
)
176235
or
177236
exists(Node mid |
@@ -185,15 +244,17 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
185244
useFieldFlow(config) and
186245
nodeCandFwd1(mid, _, config) and
187246
store(mid, _, node) and
188-
stored = true
247+
stored = true and
248+
not outBarrier(mid, config)
189249
)
190250
or
191251
// read
192252
exists(Node mid, Content f |
193253
nodeCandFwd1(mid, true, config) and
194254
read(mid, f, node) and
195255
storeCandFwd1(f, unbind(config)) and
196-
(stored = false or stored = true)
256+
(stored = false or stored = true) and
257+
not inBarrier(node, config)
197258
)
198259
or
199260
// flow into a callable
@@ -223,7 +284,7 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
223284
*/
224285
private predicate storeCandFwd1(Content f, Configuration config) {
225286
exists(Node mid, Node node |
226-
not config.isBarrier(node) and
287+
not fullBarrier(node, config) and
227288
useFieldFlow(config) and
228289
nodeCandFwd1(mid, _, config) and
229290
store(mid, f, node)
@@ -257,7 +318,7 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
257318
)
258319
or
259320
exists(Node mid |
260-
jumpStep(node, mid) and
321+
jumpStep(node, mid, config) and
261322
nodeCand1(mid, stored, config)
262323
)
263324
or
@@ -318,6 +379,13 @@ private predicate readCand1(Content f, Configuration config) {
318379
)
319380
}
320381

382+
private predicate throughFlowNodeCand(Node node, Configuration config) {
383+
nodeCand1(node, false, config) and
384+
not fullBarrier(node, config) and
385+
not inBarrier(node, config) and
386+
not outBarrier(node, config)
387+
}
388+
321389
/**
322390
* Holds if there is a path from `p` to `node` in the same callable that is
323391
* part of a path from a source to a sink taking simple call contexts into
@@ -329,7 +397,7 @@ pragma[nomagic]
329397
private predicate simpleParameterFlow(
330398
ParameterNode p, Node node, DataFlowType t, Configuration config
331399
) {
332-
nodeCand1(node, false, config) and
400+
throughFlowNodeCand(node, config) and
333401
p = node and
334402
t = getErasedRepr(node.getType()) and
335403
exists(ReturnNode ret, ReturnKind kind |
@@ -338,37 +406,37 @@ private predicate simpleParameterFlow(
338406
not parameterValueFlowsThrough(p, kind, _)
339407
)
340408
or
341-
nodeCand1(node, false, unbind(config)) and
409+
throughFlowNodeCand(node, unbind(config)) and
342410
exists(Node mid |
343411
simpleParameterFlow(p, mid, t, config) and
344412
localFlowStep(mid, node, config) and
345413
compatibleTypes(t, node.getType())
346414
)
347415
or
348-
nodeCand1(node, false, unbind(config)) and
416+
throughFlowNodeCand(node, unbind(config)) and
349417
exists(Node mid |
350418
simpleParameterFlow(p, mid, _, config) and
351419
additionalLocalFlowStep(mid, node, config) and
352420
t = getErasedRepr(node.getType())
353421
)
354422
or
355-
nodeCand1(node, false, unbind(config)) and
423+
throughFlowNodeCand(node, unbind(config)) and
356424
exists(Node mid |
357425
simpleParameterFlow(p, mid, t, config) and
358426
localStoreReadStep(mid, node) and
359427
compatibleTypes(t, node.getType())
360428
)
361429
or
362430
// value flow through a callable
363-
nodeCand1(node, false, unbind(config)) and
431+
throughFlowNodeCand(node, unbind(config)) and
364432
exists(Node arg |
365433
simpleParameterFlow(p, arg, t, config) and
366434
argumentValueFlowsThrough(arg, node, _) and
367435
compatibleTypes(t, node.getType())
368436
)
369437
or
370438
// flow through a callable
371-
nodeCand1(node, false, unbind(config)) and
439+
throughFlowNodeCand(node, unbind(config)) and
372440
exists(Node arg |
373441
simpleParameterFlow(p, arg, _, config) and
374442
simpleArgumentFlowsThrough(arg, node, t, config)
@@ -380,6 +448,7 @@ private predicate simpleArgumentFlowsThrough0(
380448
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
381449
) {
382450
nodeCand1(arg, false, unbind(config)) and
451+
not outBarrier(arg, config) and
383452
exists(ParameterNode p, ReturnNode ret |
384453
simpleParameterFlow(p, ret, t, config) and
385454
kind = ret.getKind() and
@@ -399,6 +468,7 @@ private predicate simpleArgumentFlowsThrough(
399468
) {
400469
exists(DataFlowCall call, ReturnKind kind |
401470
nodeCand1(out, false, unbind(config)) and
471+
not inBarrier(out, config) and
402472
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
403473
out = getAnOutNode(call, kind)
404474
)
@@ -440,6 +510,8 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
440510
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
441511
nodeCand1(node1, _, unbind(config)) and
442512
nodeCand1(node2, _, config) and
513+
not outBarrier(node1, config) and
514+
not inBarrier(node2, config) and
443515
(
444516
// flow out of an argument
445517
exists(ParameterNode p |
@@ -462,7 +534,9 @@ private predicate flowOutOfCallable(Node node1, Node node2, Configuration config
462534
private predicate flowIntoCallable(Node node1, Node node2, Configuration config) {
463535
viableParamArg(_, node2, node1) and
464536
nodeCand1(node1, _, unbind(config)) and
465-
nodeCand1(node2, _, config)
537+
nodeCand1(node2, _, config) and
538+
not outBarrier(node1, config) and
539+
not inBarrier(node2, config)
466540
}
467541

468542
/**
@@ -546,7 +620,7 @@ private predicate nodeCandFwd2(Node node, boolean fromArg, boolean stored, Confi
546620
or
547621
exists(Node mid |
548622
nodeCandFwd2(mid, _, stored, config) and
549-
jumpStep(mid, node) and
623+
jumpStep(mid, node, config) and
550624
fromArg = false
551625
)
552626
or
@@ -626,7 +700,7 @@ private predicate nodeCand2(Node node, boolean toReturn, boolean stored, Configu
626700
)
627701
or
628702
exists(Node mid |
629-
jumpStep(node, mid) and
703+
jumpStep(node, mid, config) and
630704
nodeCand2(mid, _, stored, config) and
631705
toReturn = false
632706
)
@@ -714,7 +788,7 @@ private predicate localFlowEntry(Node node, Configuration config) {
714788
nodeCand(node, config) and
715789
(
716790
config.isSource(node) or
717-
jumpStep(_, node) or
791+
jumpStep(_, node, config) or
718792
additionalJumpStep(_, node, config) or
719793
node instanceof ParameterNode or
720794
node instanceof OutNode or
@@ -730,7 +804,7 @@ private predicate localFlowEntry(Node node, Configuration config) {
730804
*/
731805
private predicate localFlowExit(Node node, Configuration config) {
732806
exists(Node next | nodeCand(next, config) |
733-
jumpStep(node, next) or
807+
jumpStep(node, next, config) or
734808
additionalJumpStep(node, next, config) or
735809
flowIntoCallable(node, next, config) or
736810
flowOutOfCallable(node, next, config) or
@@ -882,7 +956,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
882956
or
883957
exists(Node mid |
884958
flowCandFwd(mid, _, apf, config) and
885-
jumpStep(mid, node) and
959+
jumpStep(mid, node, config) and
886960
fromArg = false
887961
)
888962
or
@@ -973,7 +1047,7 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
9731047
)
9741048
or
9751049
exists(Node mid |
976-
jumpStep(node, mid) and
1050+
jumpStep(node, mid, config) and
9771051
flowCand(mid, _, apf, config) and
9781052
toReturn = false
9791053
)
@@ -1154,7 +1228,7 @@ private predicate flowFwd0(
11541228
or
11551229
exists(Node mid |
11561230
flowFwd(mid, _, apf, ap, config) and
1157-
jumpStep(mid, node) and
1231+
jumpStep(mid, node, config) and
11581232
fromArg = false
11591233
)
11601234
or
@@ -1263,7 +1337,7 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
12631337
)
12641338
or
12651339
exists(Node mid |
1266-
jumpStep(node, mid) and
1340+
jumpStep(node, mid, config) and
12671341
flow(mid, _, ap, config) and
12681342
toReturn = false
12691343
)
@@ -1518,7 +1592,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
15181592
mid.getAp() instanceof AccessPathNil and
15191593
ap = node.(AccessPathNilNode).getAp()
15201594
or
1521-
jumpStep(mid.getNode(), node) and
1595+
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
15221596
cc instanceof CallContextAny and
15231597
ap = mid.getAp()
15241598
or

0 commit comments

Comments
 (0)