@@ -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
@@ -236,6 +333,26 @@ abstract class DataFlowCall extends TDataFlowCall {
236
333
}
237
334
}
238
335
336
+ private predicate relevantFolder ( Folder f ) {
337
+ exists ( NonDelegateDataFlowCall call , Location l | f = l .getFile ( ) .getParentContainer ( ) |
338
+ l = call .getLocation ( ) and
339
+ call .getARuntimeTargetCandidate ( _, _) .isMultiBodied ( )
340
+ or
341
+ call .getARuntimeTargetCandidate ( l , _) .isMultiBodied ( )
342
+ )
343
+ }
344
+
345
+ private predicate adjacentFolders ( Folder f1 , Folder f2 ) {
346
+ f1 = f2 .getParentContainer ( )
347
+ or
348
+ f2 = f1 .getParentContainer ( )
349
+ }
350
+
351
+ bindingset [ f1, f2]
352
+ pragma [ inline_late]
353
+ private predicate folderDist ( Folder f1 , Folder f2 , int i ) =
354
+ shortestDistances( relevantFolder / 1 , adjacentFolders / 2 ) ( f1 , f2 , i )
355
+
239
356
/** A non-delegate C# call relevant for data flow. */
240
357
class NonDelegateDataFlowCall extends DataFlowCall , TNonDelegateCall {
241
358
private ControlFlow:: Nodes:: ElementNode cfn ;
@@ -246,25 +363,63 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
246
363
/** Gets the underlying call. */
247
364
DispatchCall getDispatchCall ( ) { result = dc }
248
365
249
- override DataFlowCallable getARuntimeTarget ( ) {
250
- result .asCallable ( ) = getCallableForDataFlow ( dc .getADynamicTarget ( ) )
251
- or
366
+ pragma [ nomagic]
367
+ private predicate hasSourceTarget ( ) { dc .getADynamicTarget ( ) .fromSource ( ) }
368
+
369
+ pragma [ nomagic]
370
+ private FlowSummary:: SummarizedCallable getASummarizedCallableTarget ( ) {
252
371
// Only use summarized callables with generated summaries in case
253
372
// we are not able to dispatch to a source declaration.
254
- exists ( FlowSummary:: SummarizedCallable sc , boolean static |
255
- result .asSummarizedCallable ( ) = sc and
256
- sc = this .getATarget ( static ) and
373
+ exists ( boolean static |
374
+ result = this .getATarget ( static ) and
257
375
not (
258
- sc .applyGeneratedModel ( ) and
259
- dc . getADynamicTarget ( ) . getUnboundDeclaration ( ) . getFile ( ) . fromSource ( )
376
+ result .applyGeneratedModel ( ) and
377
+ this . hasSourceTarget ( )
260
378
)
261
379
|
262
380
static = false
263
381
or
264
- static = true and not sc instanceof RuntimeCallable
382
+ static = true and not result instanceof RuntimeCallable
383
+ )
384
+ }
385
+
386
+ pragma [ nomagic]
387
+ DataFlowCallable getARuntimeTargetCandidate ( Location l , Callable c ) {
388
+ c = result .asCallable ( l ) and
389
+ c = getCallableForDataFlow ( dc .getADynamicTarget ( ) )
390
+ }
391
+
392
+ pragma [ nomagic]
393
+ private DataFlowCallable getAMultiBodiedRuntimeTargetCandidate ( Callable c , int distance ) {
394
+ result .isMultiBodied ( ) and
395
+ exists ( Location l | result = this .getARuntimeTargetCandidate ( l , c ) |
396
+ inSameFile ( l , this .getLocation ( ) ) and
397
+ distance = - 1
398
+ or
399
+ folderDist ( l .getFile ( ) .getParentContainer ( ) ,
400
+ this .getLocation ( ) .getFile ( ) .getParentContainer ( ) , distance )
265
401
)
266
402
}
267
403
404
+ pragma [ nomagic]
405
+ override DataFlowCallable getARuntimeTarget ( ) {
406
+ // For calls to multi-bodied methods, we restrict the viable targets to those
407
+ // that are closest to the call site, measured by file-system distance.
408
+ exists ( Callable c |
409
+ result =
410
+ min ( DataFlowCallable cand , int distance |
411
+ cand = this .getAMultiBodiedRuntimeTargetCandidate ( c , distance )
412
+ |
413
+ cand order by distance
414
+ )
415
+ )
416
+ or
417
+ result = this .getARuntimeTargetCandidate ( _, _) and
418
+ not result .isMultiBodied ( )
419
+ or
420
+ result .asSummarizedCallable ( ) = this .getASummarizedCallableTarget ( )
421
+ }
422
+
268
423
/** Gets a static or dynamic target of this call. */
269
424
Callable getATarget ( boolean static ) {
270
425
result = dc .getADynamicTarget ( ) .getUnboundDeclaration ( ) and static = false
@@ -276,9 +431,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
276
431
277
432
override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
278
433
279
- override DataFlowCallable getEnclosingCallable ( ) {
280
- result .asCallable ( ) = cfn .getEnclosingCallable ( )
281
- }
434
+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
282
435
283
436
override string toString ( ) { result = cfn .toString ( ) }
284
437
@@ -306,9 +459,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
306
459
307
460
override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
308
461
309
- override DataFlowCallable getEnclosingCallable ( ) {
310
- result .asCallable ( ) = cfn .getEnclosingCallable ( )
311
- }
462
+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
312
463
313
464
override string toString ( ) { result = cfn .toString ( ) }
314
465
0 commit comments