@@ -8,6 +8,7 @@ private import semmle.code.csharp.dispatch.Dispatch
8
8
private import semmle.code.csharp.dispatch.RuntimeCallable
9
9
private import semmle.code.csharp.frameworks.system.Collections
10
10
private import semmle.code.csharp.frameworks.system.collections.Generic
11
+ private import semmle.code.csharp.internal.Location
11
12
12
13
/**
13
14
* Gets a source declaration of callable `c` that has a body and is
@@ -24,6 +25,21 @@ newtype TReturnKind =
24
25
TOutReturnKind ( int i ) { i = any ( Parameter p | p .isOut ( ) ) .getPosition ( ) } or
25
26
TRefReturnKind ( int i ) { i = any ( Parameter p | p .isRef ( ) ) .getPosition ( ) }
26
27
28
+ private predicate hasMultipleSourceLocations ( Callable c ) { strictcount ( getASourceLocation ( c ) ) > 1 }
29
+
30
+ private module NearestBodyLocationInput implements NearestLocationInputSig {
31
+ class C = ControlFlowElement ;
32
+
33
+ predicate relevantLocations ( ControlFlowElement body , Location l1 , Location l2 ) {
34
+ exists ( Callable c |
35
+ hasMultipleSourceLocations ( c ) and
36
+ l1 = getASourceLocation ( c ) and
37
+ body = c .getBody ( ) and
38
+ l2 = body .getLocation ( )
39
+ )
40
+ }
41
+ }
42
+
27
43
cached
28
44
private module Cached {
29
45
/**
@@ -33,7 +49,18 @@ private module Cached {
33
49
*/
34
50
cached
35
51
newtype TDataFlowCallable =
36
- TCallable ( Callable c ) { c .isUnboundDeclaration ( ) } or
52
+ TCallable ( Callable c , Location l ) {
53
+ c .isUnboundDeclaration ( ) and
54
+ l = [ c .getLocation ( ) , getASourceLocation ( c ) ] and
55
+ (
56
+ not hasMultipleSourceLocations ( c )
57
+ or
58
+ // when `c` has multiple source locations, only use those with a body;
59
+ // for example, `partial` methods may have multiple source locations but
60
+ // we are only interested in the one with a body
61
+ NearestLocation< NearestBodyLocationInput > :: nearestLocation ( _, l , _)
62
+ )
63
+ } or
37
64
TSummarizedCallable ( FlowSummary:: SummarizedCallable sc ) or
38
65
TFieldOrPropertyCallable ( FieldOrProperty f ) or
39
66
TCapturedVariableCallable ( LocalScopeVariable v ) { v .isCaptured ( ) }
@@ -82,17 +109,23 @@ private module DispatchImpl {
82
109
*/
83
110
predicate mayBenefitFromCallContext ( DataFlowCall call ) { mayBenefitFromCallContext ( call , _) }
84
111
112
+ bindingset [ dc, result ]
113
+ pragma [ inline_late]
114
+ private Callable viableImplInCallContext0 ( DispatchCall dc , NonDelegateDataFlowCall ctx ) {
115
+ result = dc .getADynamicTargetInCallContext ( ctx .getDispatchCall ( ) ) .getUnboundDeclaration ( )
116
+ }
117
+
85
118
/**
86
119
* Gets a viable dispatch target of `call` in the context `ctx`. This is
87
120
* restricted to those `call`s for which a context might make a difference.
88
121
*/
89
122
DataFlowCallable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) {
90
- exists ( DispatchCall dc | dc = call .( NonDelegateDataFlowCall ) .getDispatchCall ( ) |
91
- result . getUnderlyingCallable ( ) =
92
- getCallableForDataFlow ( dc . getADynamicTargetInCallContext ( ctx . ( NonDelegateDataFlowCall )
93
- . getDispatchCall ( ) ) . getUnboundDeclaration ( ) )
123
+ exists ( DispatchCall dc , Callable c | dc = call .( NonDelegateDataFlowCall ) .getDispatchCall ( ) |
124
+ result = call . getARuntimeTarget ( ) and
125
+ getCallableForDataFlow ( c ) = result . asCallable ( _ ) and
126
+ c = viableImplInCallContext0 ( dc , ctx )
94
127
or
95
- exists ( Callable c , DataFlowCallable encl |
128
+ exists ( DataFlowCallable encl |
96
129
result .asSummarizedCallable ( ) = c and
97
130
mayBenefitFromCallContext ( call , encl ) and
98
131
encl = ctx .getARuntimeTarget ( ) and
@@ -159,7 +192,69 @@ class RefReturnKind extends OutRefReturnKind, TRefReturnKind {
159
192
/** A callable used for data flow. */
160
193
class DataFlowCallable extends TDataFlowCallable {
161
194
/** Gets the underlying source code callable, if any. */
162
- Callable asCallable ( ) { this = TCallable ( result ) }
195
+ Callable asCallable ( Location l ) { this = TCallable ( result , l ) }
196
+
197
+ /** Holds if the underlying callable is multi-bodied. */
198
+ pragma [ nomagic]
199
+ predicate isMultiBodied ( ) {
200
+ exists ( Location l1 , Location l2 , DataFlowCallable other |
201
+ this .asCallable ( l1 ) = other .asCallable ( l2 ) and
202
+ l1 != l2
203
+ )
204
+ }
205
+
206
+ pragma [ nomagic]
207
+ private ControlFlow:: Nodes:: ElementNode getAMultiBodyEntryNode ( ControlFlow:: BasicBlock bb , int i ) {
208
+ this .isMultiBodied ( ) and
209
+ exists ( ControlFlowElement body , Location l |
210
+ body = this .asCallable ( l ) .getBody ( ) and
211
+ NearestLocation< NearestBodyLocationInput > :: nearestLocation ( body , l , _) and
212
+ result = body .getAControlFlowEntryNode ( )
213
+ ) and
214
+ bb .getNode ( i ) = result
215
+ }
216
+
217
+ pragma [ nomagic]
218
+ private ControlFlow:: Nodes:: ElementNode getAMultiBodyControlFlowNodePred ( ) {
219
+ result = this .getAMultiBodyEntryNode ( _, _) .getAPredecessor ( )
220
+ or
221
+ result = this .getAMultiBodyControlFlowNodePred ( ) .getAPredecessor ( )
222
+ }
223
+
224
+ pragma [ nomagic]
225
+ private ControlFlow:: Nodes:: ElementNode getAMultiBodyControlFlowNodeSuccSameBasicBlock ( ) {
226
+ exists ( ControlFlow:: BasicBlock bb , int i , int j |
227
+ exists ( this .getAMultiBodyEntryNode ( bb , i ) ) and
228
+ result = bb .getNode ( j ) and
229
+ j > i
230
+ )
231
+ }
232
+
233
+ pragma [ nomagic]
234
+ private ControlFlow:: BasicBlock getAMultiBodyBasicBlockSucc ( ) {
235
+ result = this .getAMultiBodyEntryNode ( _, _) .getBasicBlock ( ) .getASuccessor ( )
236
+ or
237
+ result = this .getAMultiBodyBasicBlockSucc ( ) .getASuccessor ( )
238
+ }
239
+
240
+ pragma [ inline]
241
+ private ControlFlow:: Nodes:: ElementNode getAMultiBodyControlFlowNode ( ) {
242
+ result =
243
+ [
244
+ this .getAMultiBodyEntryNode ( _, _) , this .getAMultiBodyControlFlowNodePred ( ) ,
245
+ this .getAMultiBodyControlFlowNodeSuccSameBasicBlock ( ) ,
246
+ this .getAMultiBodyBasicBlockSucc ( ) .getANode ( )
247
+ ]
248
+ }
249
+
250
+ /** Gets a control flow node belonging to this callable. */
251
+ pragma [ inline]
252
+ ControlFlow:: Node getAControlFlowNode ( ) {
253
+ result = this .getAMultiBodyControlFlowNode ( )
254
+ or
255
+ not this .isMultiBodied ( ) and
256
+ result .getEnclosingCallable ( ) = this .asCallable ( _)
257
+ }
163
258
164
259
/** Gets the underlying summarized callable, if any. */
165
260
FlowSummary:: SummarizedCallable asSummarizedCallable ( ) { this = TSummarizedCallable ( result ) }
@@ -171,7 +266,7 @@ class DataFlowCallable extends TDataFlowCallable {
171
266
172
267
/** Gets the underlying callable. */
173
268
Callable getUnderlyingCallable ( ) {
174
- result = this .asCallable ( ) or result = this .asSummarizedCallable ( )
269
+ result = this .asCallable ( _ ) or result = this .asSummarizedCallable ( )
175
270
}
176
271
177
272
/** Gets a textual representation of this dataflow callable. */
@@ -185,7 +280,9 @@ class DataFlowCallable extends TDataFlowCallable {
185
280
186
281
/** Get the location of this dataflow callable. */
187
282
Location getLocation ( ) {
188
- result = this .getUnderlyingCallable ( ) .getLocation ( )
283
+ this = TCallable ( _, result )
284
+ or
285
+ result = this .asSummarizedCallable ( ) .getLocation ( )
189
286
or
190
287
result = this .asFieldOrProperty ( ) .getLocation ( )
191
288
or
@@ -256,6 +353,26 @@ abstract class DataFlowCall extends TDataFlowCall {
256
353
}
257
354
}
258
355
356
+ private predicate relevantFolder ( Folder f ) {
357
+ exists ( NonDelegateDataFlowCall call , Location l | f = l .getFile ( ) .getParentContainer ( ) |
358
+ l = call .getLocation ( ) and
359
+ call .getARuntimeTargetCandidate ( _, _) .isMultiBodied ( )
360
+ or
361
+ call .getARuntimeTargetCandidate ( l , _) .isMultiBodied ( )
362
+ )
363
+ }
364
+
365
+ private predicate adjacentFolders ( Folder f1 , Folder f2 ) {
366
+ f1 = f2 .getParentContainer ( )
367
+ or
368
+ f2 = f1 .getParentContainer ( )
369
+ }
370
+
371
+ bindingset [ f1, f2]
372
+ pragma [ inline_late]
373
+ private predicate folderDist ( Folder f1 , Folder f2 , int i ) =
374
+ shortestDistances( relevantFolder / 1 , adjacentFolders / 2 ) ( f1 , f2 , i )
375
+
259
376
/** A non-delegate C# call relevant for data flow. */
260
377
class NonDelegateDataFlowCall extends DataFlowCall , TNonDelegateCall {
261
378
private ControlFlow:: Nodes:: ElementNode cfn ;
@@ -266,25 +383,63 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
266
383
/** Gets the underlying call. */
267
384
DispatchCall getDispatchCall ( ) { result = dc }
268
385
269
- override DataFlowCallable getARuntimeTarget ( ) {
270
- result .asCallable ( ) = getCallableForDataFlow ( dc .getADynamicTarget ( ) )
271
- or
386
+ pragma [ nomagic]
387
+ private predicate hasSourceTarget ( ) { dc .getADynamicTarget ( ) .fromSource ( ) }
388
+
389
+ pragma [ nomagic]
390
+ private FlowSummary:: SummarizedCallable getASummarizedCallableTarget ( ) {
272
391
// Only use summarized callables with generated summaries in case
273
392
// we are not able to dispatch to a source declaration.
274
- exists ( FlowSummary:: SummarizedCallable sc , boolean static |
275
- result .asSummarizedCallable ( ) = sc and
276
- sc = this .getATarget ( static ) and
393
+ exists ( boolean static |
394
+ result = this .getATarget ( static ) and
277
395
not (
278
- sc .applyGeneratedModel ( ) and
279
- dc . getADynamicTarget ( ) . getUnboundDeclaration ( ) . getFile ( ) . fromSource ( )
396
+ result .applyGeneratedModel ( ) and
397
+ this . hasSourceTarget ( )
280
398
)
281
399
|
282
400
static = false
283
401
or
284
- static = true and not sc instanceof RuntimeCallable
402
+ static = true and not result instanceof RuntimeCallable
403
+ )
404
+ }
405
+
406
+ pragma [ nomagic]
407
+ DataFlowCallable getARuntimeTargetCandidate ( Location l , Callable c ) {
408
+ c = result .asCallable ( l ) and
409
+ c = getCallableForDataFlow ( dc .getADynamicTarget ( ) )
410
+ }
411
+
412
+ pragma [ nomagic]
413
+ private DataFlowCallable getAMultiBodiedRuntimeTargetCandidate ( Callable c , int distance ) {
414
+ result .isMultiBodied ( ) and
415
+ exists ( Location l | result = this .getARuntimeTargetCandidate ( l , c ) |
416
+ inSameFile ( l , this .getLocation ( ) ) and
417
+ distance = - 1
418
+ or
419
+ folderDist ( l .getFile ( ) .getParentContainer ( ) ,
420
+ this .getLocation ( ) .getFile ( ) .getParentContainer ( ) , distance )
285
421
)
286
422
}
287
423
424
+ pragma [ nomagic]
425
+ override DataFlowCallable getARuntimeTarget ( ) {
426
+ // For calls to multi-bodied methods, we restrict the viable targets to those
427
+ // that are closest to the call site, measured by file-system distance.
428
+ exists ( Callable c |
429
+ result =
430
+ min ( DataFlowCallable cand , int distance |
431
+ cand = this .getAMultiBodiedRuntimeTargetCandidate ( c , distance )
432
+ |
433
+ cand order by distance
434
+ )
435
+ )
436
+ or
437
+ result = this .getARuntimeTargetCandidate ( _, _) and
438
+ not result .isMultiBodied ( )
439
+ or
440
+ result .asSummarizedCallable ( ) = this .getASummarizedCallableTarget ( )
441
+ }
442
+
288
443
/** Gets a static or dynamic target of this call. */
289
444
Callable getATarget ( boolean static ) {
290
445
result = dc .getADynamicTarget ( ) .getUnboundDeclaration ( ) and static = false
@@ -296,9 +451,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
296
451
297
452
override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
298
453
299
- override DataFlowCallable getEnclosingCallable ( ) {
300
- result .asCallable ( ) = cfn .getEnclosingCallable ( )
301
- }
454
+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
302
455
303
456
override string toString ( ) { result = cfn .toString ( ) }
304
457
@@ -326,9 +479,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
326
479
327
480
override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
328
481
329
- override DataFlowCallable getEnclosingCallable ( ) {
330
- result .asCallable ( ) = cfn .getEnclosingCallable ( )
331
- }
482
+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
332
483
333
484
override string toString ( ) { result = cfn .toString ( ) }
334
485
0 commit comments