Skip to content

Commit 7d0152f

Browse files
authored
Merge pull request #6932 from aschackmull/dataflow/flow-features
Dataflow: Add support for call context restrictions on sources/sinks.
2 parents 6dd5dad + 6eabb61 commit 7d0152f

33 files changed

+2808
-864
lines changed

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

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
private import DataFlowImplCommon
1111
private import DataFlowImplSpecific::Private
1212
import DataFlowImplSpecific::Public
13+
import DataFlowImplCommonPublic
1314

1415
/**
1516
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
9495
*/
9596
int fieldFlowBranchLimit() { result = 2 }
9697

98+
/**
99+
* Gets a data flow configuration feature to add restrictions to the set of
100+
* valid flow paths.
101+
*
102+
* - `FeatureHasSourceCallContext`:
103+
* Assume that sources have some existing call context to disallow
104+
* conflicting return-flow directly following the source.
105+
* - `FeatureHasSinkCallContext`:
106+
* Assume that sinks have some existing call context to disallow
107+
* conflicting argument-to-parameter flow directly preceding the sink.
108+
* - `FeatureEqualSourceSinkCallContext`:
109+
* Implies both of the above and additionally ensures that the entire flow
110+
* path preserves the call context.
111+
*/
112+
FlowFeature getAFeature() { none() }
113+
97114
/**
98115
* Holds if data may flow from `source` to `sink` for this configuration.
99116
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
349366
not outBarrier(node1, config) and
350367
not inBarrier(node2, config) and
351368
not fullBarrier(node1, config) and
352-
not fullBarrier(node2, config)
369+
not fullBarrier(node2, config) and
370+
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
353371
)
354372
}
355373

@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
365383
not outBarrier(node1, config) and
366384
not inBarrier(node2, config) and
367385
not fullBarrier(node1, config) and
368-
not fullBarrier(node2, config)
386+
not fullBarrier(node2, config) and
387+
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
369388
)
370389
}
371390

@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
401420
*/
402421
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
403422

423+
private predicate hasSourceCallCtx(Configuration config) {
424+
exists(FlowFeature feature | feature = config.getAFeature() |
425+
feature instanceof FeatureHasSourceCallContext or
426+
feature instanceof FeatureEqualSourceSinkCallContext
427+
)
428+
}
429+
430+
private predicate hasSinkCallCtx(Configuration config) {
431+
exists(FlowFeature feature | feature = config.getAFeature() |
432+
feature instanceof FeatureHasSinkCallContext or
433+
feature instanceof FeatureEqualSourceSinkCallContext
434+
)
435+
}
436+
404437
private module Stage1 {
405438
class ApApprox = Unit;
406439

@@ -421,7 +454,7 @@ private module Stage1 {
421454
not fullBarrier(node, config) and
422455
(
423456
sourceNode(node, config) and
424-
cc = false
457+
if hasSourceCallCtx(config) then cc = true else cc = false
425458
or
426459
exists(NodeEx mid |
427460
fwdFlow(mid, cc, config) and
@@ -551,7 +584,7 @@ private module Stage1 {
551584
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
552585
fwdFlow(node, config) and
553586
sinkNode(node, config) and
554-
toReturn = false
587+
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
555588
or
556589
exists(NodeEx mid |
557590
localFlowStep(node, mid, config) and
@@ -937,6 +970,8 @@ private module Stage2 {
937970

938971
Cc ccNone() { result instanceof CallContextAny }
939972

973+
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
974+
940975
private class LocalCc = Unit;
941976

942977
bindingset[call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
10041039
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
10051040
flowCand(node, _, config) and
10061041
sourceNode(node, config) and
1007-
cc = ccNone() and
1042+
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
10081043
argAp = apNone() and
10091044
ap = getApNil(node)
10101045
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
12151250
) {
12161251
fwdFlow(node, _, _, ap, config) and
12171252
sinkNode(node, config) and
1218-
toReturn = false and
1253+
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
12191254
returnAp = apNone() and
12201255
ap instanceof ApNil
12211256
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
16161651

16171652
Cc ccNone() { result = false }
16181653

1654+
CcCall ccSomeCall() { result = true }
1655+
16191656
private class LocalCc = Unit;
16201657

16211658
bindingset[call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
16971734
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
16981735
flowCand(node, _, config) and
16991736
sourceNode(node, config) and
1700-
cc = ccNone() and
1737+
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
17011738
argAp = apNone() and
17021739
ap = getApNil(node)
17031740
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
19081945
) {
19091946
fwdFlow(node, _, _, ap, config) and
19101947
sinkNode(node, config) and
1911-
toReturn = false and
1948+
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
19121949
returnAp = apNone() and
19131950
ap instanceof ApNil
19141951
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
23662403

23672404
Cc ccNone() { result instanceof CallContextAny }
23682405

2406+
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
2407+
23692408
private class LocalCc = LocalCallContext;
23702409

23712410
bindingset[call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
24612500
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
24622501
flowCand(node, _, config) and
24632502
sourceNode(node, config) and
2464-
cc = ccNone() and
2503+
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
24652504
argAp = apNone() and
24662505
ap = getApNil(node)
24672506
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
26722711
) {
26732712
fwdFlow(node, _, _, ap, config) and
26742713
sinkNode(node, config) and
2675-
toReturn = false and
2714+
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
26762715
returnAp = apNone() and
26772716
ap instanceof ApNil
26782717
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
30643103
// A PathNode is introduced by a source ...
30653104
Stage4::revFlow(node, config) and
30663105
sourceNode(node, config) and
3067-
cc instanceof CallContextAny and
3106+
(
3107+
if hasSourceCallCtx(config)
3108+
then cc instanceof CallContextSomeCall
3109+
else cc instanceof CallContextAny
3110+
) and
30683111
sc instanceof SummaryCtxNone and
30693112
ap = TAccessPathNil(node.getDataFlowType())
30703113
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
30763119
)
30773120
} or
30783121
TPathNodeSink(NodeEx node, Configuration config) {
3079-
sinkNode(node, pragma[only_bind_into](config)) and
3080-
Stage4::revFlow(node, pragma[only_bind_into](config)) and
3081-
(
3082-
// A sink that is also a source ...
3083-
sourceNode(node, config)
3084-
or
3085-
// ... or a sink that can be reached from a source
3086-
exists(PathNodeMid mid |
3087-
pathStep(mid, node, _, _, TAccessPathNil(_)) and
3088-
pragma[only_bind_into](config) = mid.getConfiguration()
3089-
)
3122+
exists(PathNodeMid sink |
3123+
sink.isAtSink() and
3124+
node = sink.getNodeEx() and
3125+
config = sink.getConfiguration()
30903126
)
30913127
}
30923128

@@ -3403,22 +3439,46 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
34033439
// an intermediate step to another intermediate node
34043440
result = this.getSuccMid()
34053441
or
3406-
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
3407-
exists(PathNodeMid mid, PathNodeSink sink |
3408-
mid = this.getSuccMid() and
3409-
mid.getNodeEx() = sink.getNodeEx() and
3410-
mid.getAp() instanceof AccessPathNil and
3411-
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
3412-
result = sink
3413-
)
3442+
// a final step to a sink
3443+
result = this.getSuccMid().projectToSink()
34143444
}
34153445

34163446
override predicate isSource() {
34173447
sourceNode(node, config) and
3418-
cc instanceof CallContextAny and
3448+
(
3449+
if hasSourceCallCtx(config)
3450+
then cc instanceof CallContextSomeCall
3451+
else cc instanceof CallContextAny
3452+
) and
34193453
sc instanceof SummaryCtxNone and
34203454
ap instanceof AccessPathNil
34213455
}
3456+
3457+
predicate isAtSink() {
3458+
sinkNode(node, config) and
3459+
ap instanceof AccessPathNil and
3460+
if hasSinkCallCtx(config)
3461+
then
3462+
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
3463+
// is exactly what we need to check. This also implies
3464+
// `sc instanceof SummaryCtxNone`.
3465+
// For `FeatureEqualSourceSinkCallContext` the initial call context was
3466+
// set to `CallContextSomeCall` and jumps are disallowed, so
3467+
// `cc instanceof CallContextNoCall` never holds. On the other hand,
3468+
// in this case there's never any need to enter a call except to identify
3469+
// a summary, so the condition in `pathIntoCallable` enforces this, which
3470+
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
3471+
// in the call context of the source.
3472+
sc instanceof SummaryCtxNone or
3473+
cc instanceof CallContextNoCall
3474+
else any()
3475+
}
3476+
3477+
PathNodeSink projectToSink() {
3478+
this.isAtSink() and
3479+
result.getNodeEx() = node and
3480+
result.getConfiguration() = unbindConf(config)
3481+
}
34223482
}
34233483

34243484
/**
@@ -3613,7 +3673,11 @@ private predicate pathIntoCallable(
36133673
sc = TSummaryCtxSome(p, ap)
36143674
or
36153675
not exists(TSummaryCtxSome(p, ap)) and
3616-
sc = TSummaryCtxNone()
3676+
sc = TSummaryCtxNone() and
3677+
// When the call contexts of source and sink needs to match then there's
3678+
// never any reason to enter a callable except to find a summary. See also
3679+
// the comment in `PathNodeMid::isAtSink`.
3680+
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
36173681
)
36183682
|
36193683
if recordDataFlowCallSite(call, callable)

0 commit comments

Comments
 (0)