Skip to content

Commit b6206d7

Browse files
committed
Data flow: Introduce ReturnKind
1 parent 42f2c88 commit b6206d7

21 files changed

+2161
-843
lines changed

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

Lines changed: 107 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ private predicate localFlowStep(Node node1, Node node2, boolean preservesValue,
153153
*/
154154
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
155155

156+
pragma[noinline]
157+
private ReturnKind viableReturnKind(DataFlowCall call, ReturnPosition pos) {
158+
viableImpl(call) = pos.getCallable() and
159+
result = pos.getKind()
160+
}
161+
156162
/**
157163
* Holds if `node` is reachable from a source in the given configuration
158164
* ignoring call contexts.
@@ -193,20 +199,21 @@ private predicate nodeCandFwd1(Node node, boolean stored, Configuration config)
193199
// flow into a callable
194200
exists(Node arg |
195201
nodeCandFwd1(arg, stored, config) and
196-
viableParamArg(node, arg)
202+
viableParamArg(_, node, arg)
197203
)
198204
or
199205
// flow out of an argument
200206
exists(PostUpdateNode mid, ParameterNode p |
201207
nodeCandFwd1(mid, stored, config) and
202208
parameterValueFlowsToUpdate(p, mid) and
203-
viableParamArg(p, node.(PostUpdateNode).getPreUpdateNode())
209+
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
204210
)
205211
or
206212
// flow out of a callable
207-
exists(ReturnNode ret |
213+
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
208214
nodeCandFwd1(ret, stored, config) and
209-
node = getAViableOutNode(ret.getPosition())
215+
kind = viableReturnKind(call, getReturnPosition(ret)) and
216+
node = getAnOutNode(call, kind)
210217
)
211218
)
212219
}
@@ -268,21 +275,22 @@ private predicate nodeCand1(Node node, boolean stored, Configuration config) {
268275
or
269276
// flow into a callable
270277
exists(Node param |
271-
viableParamArg(param, node) and
278+
viableParamArg(_, param, node) and
272279
nodeCand1(param, stored, config)
273280
)
274281
or
275282
// flow out of an argument
276283
exists(PostUpdateNode mid, ParameterNode p |
277284
parameterValueFlowsToUpdate(p, node) and
278-
viableParamArg(p, mid.getPreUpdateNode()) and
285+
viableParamArg(_, p, mid.getPreUpdateNode()) and
279286
nodeCand1(mid, stored, config)
280287
)
281288
or
282289
// flow out of a callable
283-
exists(Node out |
290+
exists(DataFlowCall call, ReturnKind kind, OutNode out |
284291
nodeCand1(out, stored, config) and
285-
out = getAViableOutNode(node.(ReturnNode).getPosition())
292+
kind = viableReturnKind(call, getReturnPosition(node)) and
293+
out = getAnOutNode(call, kind)
286294
)
287295
)
288296
}
@@ -314,7 +322,12 @@ private predicate simpleParameterFlow(
314322
nodeCand1(node, false, config) and
315323
p = node and
316324
t = getErasedRepr(node.getType()) and
317-
not parameterValueFlowsThrough(p, _)
325+
exists(ReturnNode ret, CallContextCall cc |
326+
returnNodeGetEnclosingCallable(ret) = p.getEnclosingCallable() and
327+
cc = getAValidCallContextForParameter(p)
328+
|
329+
not parameterValueFlowsThrough(p, ret.getKind(), cc)
330+
)
318331
or
319332
nodeCand1(node, false, unbind(config)) and
320333
exists(Node mid |
@@ -341,7 +354,7 @@ private predicate simpleParameterFlow(
341354
nodeCand1(node, false, config) and
342355
exists(Node arg |
343356
simpleParameterFlow(p, arg, t, config) and
344-
argumentValueFlowsThrough(arg, node) and
357+
argumentValueFlowsThrough(arg, node, _) and
345358
compatibleTypes(t, node.getType())
346359
)
347360
or
@@ -353,21 +366,31 @@ private predicate simpleParameterFlow(
353366
)
354367
}
355368

369+
pragma[noinline]
370+
private predicate simpleArgumentFlowsThrough0(
371+
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
372+
) {
373+
exists(ParameterNode p, ReturnNode ret | simpleParameterFlow(p, ret, t, config) |
374+
kind = ret.getKind() and
375+
viableParamArg(call, p, arg)
376+
)
377+
}
378+
356379
/**
357-
* Holds if data can flow from `arg` through the `call` taking simple call
358-
* contexts into consideration and that this is part of a path from a source
359-
* to a sink. This is restricted to paths through the `call` that does not
380+
* Holds if data can flow from `arg` through a call to `out`, taking simple
381+
* call contexts into consideration, and that this is part of a path from a
382+
* source to a sink. This is restricted to paths through calla that do not
360383
* necessarily preserve the value of `arg` by making use of at least one
361384
* additional step from the configuration.
362385
*/
363386
private predicate simpleArgumentFlowsThrough(
364387
ArgumentNode arg, Node out, DataFlowType t, Configuration config
365388
) {
366-
exists(ParameterNode param, ReturnNode ret |
389+
exists(DataFlowCall call, ReturnKind kind |
367390
nodeCand1(arg, false, unbind(config)) and
368391
nodeCand1(out, false, unbind(config)) and
369-
viableParamArgOut(param, arg, ret.getPosition(), out) and
370-
simpleParameterFlow(param, ret, t, config)
392+
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and
393+
out = getAnOutNode(call, kind)
371394
)
372395
}
373396

@@ -379,7 +402,7 @@ private predicate flowThroughCallableCand1(
379402
) {
380403
simpleArgumentFlowsThrough(node1, node2, _, config) and preservesValue = false
381404
or
382-
argumentValueFlowsThrough(node1, node2) and preservesValue = true
405+
argumentValueFlowsThrough(node1, node2, _) and preservesValue = true
383406
}
384407

385408
/**
@@ -405,11 +428,15 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
405428
// flow out of an argument
406429
exists(ParameterNode p |
407430
parameterValueFlowsToUpdate(p, node1) and
408-
viableParamArg(p, node2.(PostUpdateNode).getPreUpdateNode())
431+
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
409432
)
410433
or
411434
// flow out of a callable
412-
node2 = getAViableOutNode(node1.(ReturnNode).getPosition())
435+
exists(DataFlowCall call, ReturnKind kind |
436+
kind = viableReturnKind(call, getReturnPosition(node1))
437+
|
438+
node2 = getAnOutNode(call, kind)
439+
)
413440
)
414441
}
415442

@@ -418,7 +445,7 @@ private predicate flowOutOfCallableCand1(Node node1, Node node2, Configuration c
418445
* path from a source to a sink.
419446
*/
420447
private predicate flowIntoCallableCand1(Node node1, Node node2, Configuration config) {
421-
viableParamArg(node2, node1) and
448+
viableParamArg(_, node2, node1) and
422449
nodeCand1(node1, _, unbind(config)) and
423450
nodeCand1(node2, _, config)
424451
}
@@ -1438,7 +1465,9 @@ private predicate flowStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
14381465
or
14391466
flowOutOfCallable(mid, node, cc) and ap = mid.getAp()
14401467
or
1441-
flowThroughCallable(mid, node, ap, cc)
1468+
flowThroughCallable(mid, node, cc) and ap = TNil(getErasedRepr(node.getType()))
1469+
or
1470+
valueFlowThroughCallable(mid, node, cc) and ap = mid.getAp()
14421471
}
14431472

14441473
private predicate contentReadStep(PathNodeMid mid, Node node, AccessPath ap) {
@@ -1458,33 +1487,37 @@ private predicate contentStoreStep(
14581487
cc = mid.getCallContext()
14591488
}
14601489

1461-
/**
1462-
* Holds if data may flow from `mid` to a return at position `pos` in the
1463-
* context `innercc`, and the path did not flow through a parameter.
1464-
*/
14651490
private predicate flowOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallContext innercc) {
1466-
pos.getAReturnNode() = mid.getNode() and
1491+
pos = getReturnPosition(mid.getNode()) and
14671492
innercc = mid.getCallContext() and
14681493
not innercc instanceof CallContextCall
14691494
}
14701495

1471-
/**
1472-
* Holds if data may flow from `mid` to `out`. The last step of this path
1473-
* is a return from a callable and is recorded by `cc`, if needed.
1474-
*/
14751496
pragma[noinline]
1476-
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
1477-
exists(ReturnPosition pos, DataFlowCallable c, DataFlowCall call, CallContext innercc |
1497+
private predicate flowOutOfCallable1(
1498+
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
1499+
) {
1500+
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
14781501
flowOutOfCallable0(mid, pos, innercc) and
1479-
out = getAViableOutNode(pos) and
14801502
c = pos.getCallable() and
1481-
call = out.getCall() and
1503+
kind = pos.getKind() and
14821504
resolveReturn(innercc, c, call)
14831505
|
14841506
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
14851507
)
14861508
}
14871509

1510+
/**
1511+
* Holds if data may flow from `mid` to `out`. The last step of this path
1512+
* is a return from a callable and is recorded by `cc`, if needed.
1513+
*/
1514+
pragma[noinline]
1515+
private predicate flowOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
1516+
exists(ReturnKind kind, DataFlowCall call | flowOutOfCallable1(mid, call, kind, cc) |
1517+
out = getAnOutNode(call, kind)
1518+
)
1519+
}
1520+
14881521
private predicate flowOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
14891522
exists(
14901523
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
@@ -1560,20 +1593,35 @@ private predicate flowIntoCallable(
15601593
)
15611594
}
15621595

1563-
/** Holds if data may flow from `p` to a return at position `pos`. */
1596+
/** Holds if data may flow from `p` to a return of kind `kind`. */
15641597
pragma[nomagic]
15651598
private predicate paramFlowsThrough(
1566-
ParameterNode p, ReturnPosition pos, AccessPath ap, CallContextCall cc, Configuration config
1599+
ParameterNode p, ReturnKind kind, CallContextCall cc, Configuration config
15671600
) {
1568-
exists(PathNodeMid mid |
1569-
mid.getNode() = pos.getAReturnNode() and
1601+
exists(PathNodeMid mid, ReturnNode ret |
1602+
mid.getNode() = ret and
1603+
kind = ret.getKind() and
15701604
cc = mid.getCallContext() and
1571-
ap = mid.getAp() and
1572-
config = mid.getConfiguration()
1605+
config = mid.getConfiguration() and
1606+
mid.getAp() instanceof AccessPathNil
15731607
|
15741608
cc = TSomeCall(p, true)
15751609
or
1576-
exists(int i | cc = TSpecificCall(_, i, true) | p.isParameterOf(pos.getCallable(), i))
1610+
exists(int i | cc = TSpecificCall(_, i, true) |
1611+
p.isParameterOf(returnNodeGetEnclosingCallable(ret), i)
1612+
)
1613+
)
1614+
}
1615+
1616+
pragma[noinline]
1617+
private predicate flowThroughCallable0(
1618+
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
1619+
) {
1620+
exists(ParameterNode p, CallContext innercc |
1621+
flowIntoCallable(mid, p, cc, innercc, call) and
1622+
paramFlowsThrough(p, kind, innercc, unbind(mid.getConfiguration())) and
1623+
not parameterValueFlowsThrough(p, kind, innercc) and
1624+
mid.getAp() instanceof AccessPathNil
15771625
)
15781626
}
15791627

@@ -1582,11 +1630,24 @@ private predicate paramFlowsThrough(
15821630
* The context `cc` is restored to its value prior to entering the callable.
15831631
*/
15841632
pragma[noinline]
1585-
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, AccessPath ap, CallContext cc) {
1586-
exists(ParameterNode p, ReturnPosition pos, CallContext innercc |
1587-
flowIntoCallable(mid, p, cc, innercc, out.getCall()) and
1588-
paramFlowsThrough(p, pos, ap, innercc, unbind(mid.getConfiguration())) and
1589-
out = getAViableOutNode(pos)
1633+
private predicate flowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
1634+
exists(DataFlowCall call, ReturnKind kind | flowThroughCallable0(call, mid, kind, cc) |
1635+
out = getAnOutNode(call, kind)
1636+
)
1637+
}
1638+
1639+
pragma[noinline]
1640+
private predicate valueFlowThroughCallable0(
1641+
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc
1642+
) {
1643+
exists(ParameterNode p, CallContext innercc | flowIntoCallable(mid, p, cc, innercc, call) |
1644+
parameterValueFlowsThrough(p, kind, innercc)
1645+
)
1646+
}
1647+
1648+
private predicate valueFlowThroughCallable(PathNodeMid mid, OutNode out, CallContext cc) {
1649+
exists(ReturnKind kind | valueFlowThroughCallable0(out.getCall(), mid, kind, cc) |
1650+
out = getAnOutNode(_, kind)
15901651
)
15911652
}
15921653

0 commit comments

Comments
 (0)