10
10
private import DataFlowImplCommon
11
11
private import DataFlowImplSpecific:: Private
12
12
import DataFlowImplSpecific:: Public
13
+ import DataFlowImplCommonPublic
13
14
14
15
/**
15
16
* A configuration of interprocedural data flow analysis. This defines
@@ -94,6 +95,22 @@ abstract class Configuration extends string {
94
95
*/
95
96
int fieldFlowBranchLimit ( ) { result = 2 }
96
97
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
+
97
114
/**
98
115
* Holds if data may flow from `source` to `sink` for this configuration.
99
116
*/
@@ -349,7 +366,8 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
349
366
not outBarrier ( node1 , config ) and
350
367
not inBarrier ( node2 , config ) and
351
368
not fullBarrier ( node1 , config ) and
352
- not fullBarrier ( node2 , config )
369
+ not fullBarrier ( node2 , config ) and
370
+ not config .getAFeature ( ) instanceof FeatureEqualSourceSinkCallContext
353
371
)
354
372
}
355
373
@@ -365,7 +383,8 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
365
383
not outBarrier ( node1 , config ) and
366
384
not inBarrier ( node2 , config ) and
367
385
not fullBarrier ( node1 , config ) and
368
- not fullBarrier ( node2 , config )
386
+ not fullBarrier ( node2 , config ) and
387
+ not config .getAFeature ( ) instanceof FeatureEqualSourceSinkCallContext
369
388
)
370
389
}
371
390
@@ -401,6 +420,20 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
401
420
*/
402
421
private predicate useFieldFlow ( Configuration config ) { config .fieldFlowBranchLimit ( ) >= 1 }
403
422
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
+
404
437
private module Stage1 {
405
438
class ApApprox = Unit ;
406
439
@@ -421,7 +454,7 @@ private module Stage1 {
421
454
not fullBarrier ( node , config ) and
422
455
(
423
456
sourceNode ( node , config ) and
424
- cc = false
457
+ if hasSourceCallCtx ( config ) then cc = true else cc = false
425
458
or
426
459
exists ( NodeEx mid |
427
460
fwdFlow ( mid , cc , config ) and
@@ -551,7 +584,7 @@ private module Stage1 {
551
584
private predicate revFlow0 ( NodeEx node , boolean toReturn , Configuration config ) {
552
585
fwdFlow ( node , config ) and
553
586
sinkNode ( node , config ) and
554
- toReturn = false
587
+ if hasSinkCallCtx ( config ) then toReturn = true else toReturn = false
555
588
or
556
589
exists ( NodeEx mid |
557
590
localFlowStep ( node , mid , config ) and
@@ -937,6 +970,8 @@ private module Stage2 {
937
970
938
971
Cc ccNone ( ) { result instanceof CallContextAny }
939
972
973
+ CcCall ccSomeCall ( ) { result instanceof CallContextSomeCall }
974
+
940
975
private class LocalCc = Unit ;
941
976
942
977
bindingset [ call, c, outercc]
@@ -1004,7 +1039,7 @@ private module Stage2 {
1004
1039
predicate fwdFlow ( NodeEx node , Cc cc , ApOption argAp , Ap ap , Configuration config ) {
1005
1040
flowCand ( node , _, config ) and
1006
1041
sourceNode ( node , config ) and
1007
- cc = ccNone ( ) and
1042
+ ( if hasSourceCallCtx ( config ) then cc = ccSomeCall ( ) else cc = ccNone ( ) ) and
1008
1043
argAp = apNone ( ) and
1009
1044
ap = getApNil ( node )
1010
1045
or
@@ -1215,7 +1250,7 @@ private module Stage2 {
1215
1250
) {
1216
1251
fwdFlow ( node , _, _, ap , config ) and
1217
1252
sinkNode ( node , config ) and
1218
- toReturn = false and
1253
+ ( if hasSinkCallCtx ( config ) then toReturn = true else toReturn = false ) and
1219
1254
returnAp = apNone ( ) and
1220
1255
ap instanceof ApNil
1221
1256
or
@@ -1616,6 +1651,8 @@ private module Stage3 {
1616
1651
1617
1652
Cc ccNone ( ) { result = false }
1618
1653
1654
+ CcCall ccSomeCall ( ) { result = true }
1655
+
1619
1656
private class LocalCc = Unit ;
1620
1657
1621
1658
bindingset [ call, c, outercc]
@@ -1697,7 +1734,7 @@ private module Stage3 {
1697
1734
private predicate fwdFlow0 ( NodeEx node , Cc cc , ApOption argAp , Ap ap , Configuration config ) {
1698
1735
flowCand ( node , _, config ) and
1699
1736
sourceNode ( node , config ) and
1700
- cc = ccNone ( ) and
1737
+ ( if hasSourceCallCtx ( config ) then cc = ccSomeCall ( ) else cc = ccNone ( ) ) and
1701
1738
argAp = apNone ( ) and
1702
1739
ap = getApNil ( node )
1703
1740
or
@@ -1908,7 +1945,7 @@ private module Stage3 {
1908
1945
) {
1909
1946
fwdFlow ( node , _, _, ap , config ) and
1910
1947
sinkNode ( node , config ) and
1911
- toReturn = false and
1948
+ ( if hasSinkCallCtx ( config ) then toReturn = true else toReturn = false ) and
1912
1949
returnAp = apNone ( ) and
1913
1950
ap instanceof ApNil
1914
1951
or
@@ -2366,6 +2403,8 @@ private module Stage4 {
2366
2403
2367
2404
Cc ccNone ( ) { result instanceof CallContextAny }
2368
2405
2406
+ CcCall ccSomeCall ( ) { result instanceof CallContextSomeCall }
2407
+
2369
2408
private class LocalCc = LocalCallContext ;
2370
2409
2371
2410
bindingset [ call, c, outercc]
@@ -2461,7 +2500,7 @@ private module Stage4 {
2461
2500
private predicate fwdFlow0 ( NodeEx node , Cc cc , ApOption argAp , Ap ap , Configuration config ) {
2462
2501
flowCand ( node , _, config ) and
2463
2502
sourceNode ( node , config ) and
2464
- cc = ccNone ( ) and
2503
+ ( if hasSourceCallCtx ( config ) then cc = ccSomeCall ( ) else cc = ccNone ( ) ) and
2465
2504
argAp = apNone ( ) and
2466
2505
ap = getApNil ( node )
2467
2506
or
@@ -2672,7 +2711,7 @@ private module Stage4 {
2672
2711
) {
2673
2712
fwdFlow ( node , _, _, ap , config ) and
2674
2713
sinkNode ( node , config ) and
2675
- toReturn = false and
2714
+ ( if hasSinkCallCtx ( config ) then toReturn = true else toReturn = false ) and
2676
2715
returnAp = apNone ( ) and
2677
2716
ap instanceof ApNil
2678
2717
or
@@ -3064,7 +3103,11 @@ private newtype TPathNode =
3064
3103
// A PathNode is introduced by a source ...
3065
3104
Stage4:: revFlow ( node , config ) and
3066
3105
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
3068
3111
sc instanceof SummaryCtxNone and
3069
3112
ap = TAccessPathNil ( node .getDataFlowType ( ) )
3070
3113
or
@@ -3076,17 +3119,10 @@ private newtype TPathNode =
3076
3119
)
3077
3120
} or
3078
3121
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 ( )
3090
3126
)
3091
3127
}
3092
3128
@@ -3403,22 +3439,46 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
3403
3439
// an intermediate step to another intermediate node
3404
3440
result = this .getSuccMid ( )
3405
3441
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 ( )
3414
3444
}
3415
3445
3416
3446
override predicate isSource ( ) {
3417
3447
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
3419
3453
sc instanceof SummaryCtxNone and
3420
3454
ap instanceof AccessPathNil
3421
3455
}
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
+ }
3422
3482
}
3423
3483
3424
3484
/**
@@ -3613,7 +3673,11 @@ private predicate pathIntoCallable(
3613
3673
sc = TSummaryCtxSome ( p , ap )
3614
3674
or
3615
3675
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
3617
3681
)
3618
3682
|
3619
3683
if recordDataFlowCallSite ( call , callable )
0 commit comments