@@ -8,6 +8,7 @@ private import semmle.code.csharp.dispatch.Dispatch
88private import semmle.code.csharp.dispatch.RuntimeCallable
99private import semmle.code.csharp.frameworks.system.Collections
1010private import semmle.code.csharp.frameworks.system.collections.Generic
11+ private import semmle.code.csharp.internal.Location
1112
1213/**
1314 * Gets a source declaration of callable `c` that has a body and is
@@ -24,6 +25,21 @@ newtype TReturnKind =
2425 TOutReturnKind ( int i ) { i = any ( Parameter p | p .isOut ( ) ) .getPosition ( ) } or
2526 TRefReturnKind ( int i ) { i = any ( Parameter p | p .isRef ( ) ) .getPosition ( ) }
2627
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+
2743cached
2844private module Cached {
2945 /**
@@ -33,7 +49,18 @@ private module Cached {
3349 */
3450 cached
3551 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
3764 TSummarizedCallable ( FlowSummary:: SummarizedCallable sc ) or
3865 TFieldOrPropertyCallable ( FieldOrProperty f ) or
3966 TCapturedVariableCallable ( LocalScopeVariable v ) { v .isCaptured ( ) }
@@ -82,17 +109,23 @@ private module DispatchImpl {
82109 */
83110 predicate mayBenefitFromCallContext ( DataFlowCall call ) { mayBenefitFromCallContext ( call , _) }
84111
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+
85118 /**
86119 * Gets a viable dispatch target of `call` in the context `ctx`. This is
87120 * restricted to those `call`s for which a context might make a difference.
88121 */
89122 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 )
94127 or
95- exists ( Callable c , DataFlowCallable encl |
128+ exists ( DataFlowCallable encl |
96129 result .asSummarizedCallable ( ) = c and
97130 mayBenefitFromCallContext ( call , encl ) and
98131 encl = ctx .getARuntimeTarget ( ) and
@@ -159,7 +192,69 @@ class RefReturnKind extends OutRefReturnKind, TRefReturnKind {
159192/** A callable used for data flow. */
160193class DataFlowCallable extends TDataFlowCallable {
161194 /** 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+ }
163258
164259 /** Gets the underlying summarized callable, if any. */
165260 FlowSummary:: SummarizedCallable asSummarizedCallable ( ) { this = TSummarizedCallable ( result ) }
@@ -171,7 +266,7 @@ class DataFlowCallable extends TDataFlowCallable {
171266
172267 /** Gets the underlying callable. */
173268 Callable getUnderlyingCallable ( ) {
174- result = this .asCallable ( ) or result = this .asSummarizedCallable ( )
269+ result = this .asCallable ( _ ) or result = this .asSummarizedCallable ( )
175270 }
176271
177272 /** Gets a textual representation of this dataflow callable. */
@@ -185,7 +280,9 @@ class DataFlowCallable extends TDataFlowCallable {
185280
186281 /** Get the location of this dataflow callable. */
187282 Location getLocation ( ) {
188- result = this .getUnderlyingCallable ( ) .getLocation ( )
283+ this = TCallable ( _, result )
284+ or
285+ result = this .asSummarizedCallable ( ) .getLocation ( )
189286 or
190287 result = this .asFieldOrProperty ( ) .getLocation ( )
191288 or
@@ -236,6 +333,26 @@ abstract class DataFlowCall extends TDataFlowCall {
236333 }
237334}
238335
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+
239356/** A non-delegate C# call relevant for data flow. */
240357class NonDelegateDataFlowCall extends DataFlowCall , TNonDelegateCall {
241358 private ControlFlow:: Nodes:: ElementNode cfn ;
@@ -246,25 +363,63 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
246363 /** Gets the underlying call. */
247364 DispatchCall getDispatchCall ( ) { result = dc }
248365
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 ( ) {
252371 // Only use summarized callables with generated summaries in case
253372 // 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
257375 not (
258- sc .applyGeneratedModel ( ) and
259- dc . getADynamicTarget ( ) . getUnboundDeclaration ( ) . getFile ( ) . fromSource ( )
376+ result .applyGeneratedModel ( ) and
377+ this . hasSourceTarget ( )
260378 )
261379 |
262380 static = false
263381 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 )
265401 )
266402 }
267403
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+
268423 /** Gets a static or dynamic target of this call. */
269424 Callable getATarget ( boolean static ) {
270425 result = dc .getADynamicTarget ( ) .getUnboundDeclaration ( ) and static = false
@@ -276,9 +431,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
276431
277432 override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
278433
279- override DataFlowCallable getEnclosingCallable ( ) {
280- result .asCallable ( ) = cfn .getEnclosingCallable ( )
281- }
434+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
282435
283436 override string toString ( ) { result = cfn .toString ( ) }
284437
@@ -306,9 +459,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
306459
307460 override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
308461
309- override DataFlowCallable getEnclosingCallable ( ) {
310- result .asCallable ( ) = cfn .getEnclosingCallable ( )
311- }
462+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
312463
313464 override string toString ( ) { result = cfn .toString ( ) }
314465
0 commit comments