@@ -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
@@ -256,6 +353,26 @@ abstract class DataFlowCall extends TDataFlowCall {
256353 }
257354}
258355
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+
259376/** A non-delegate C# call relevant for data flow. */
260377class NonDelegateDataFlowCall extends DataFlowCall , TNonDelegateCall {
261378 private ControlFlow:: Nodes:: ElementNode cfn ;
@@ -266,25 +383,63 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
266383 /** Gets the underlying call. */
267384 DispatchCall getDispatchCall ( ) { result = dc }
268385
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 ( ) {
272391 // Only use summarized callables with generated summaries in case
273392 // 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
277395 not (
278- sc .applyGeneratedModel ( ) and
279- dc . getADynamicTarget ( ) . getUnboundDeclaration ( ) . getFile ( ) . fromSource ( )
396+ result .applyGeneratedModel ( ) and
397+ this . hasSourceTarget ( )
280398 )
281399 |
282400 static = false
283401 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 )
285421 )
286422 }
287423
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+
288443 /** Gets a static or dynamic target of this call. */
289444 Callable getATarget ( boolean static ) {
290445 result = dc .getADynamicTarget ( ) .getUnboundDeclaration ( ) and static = false
@@ -296,9 +451,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
296451
297452 override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
298453
299- override DataFlowCallable getEnclosingCallable ( ) {
300- result .asCallable ( ) = cfn .getEnclosingCallable ( )
301- }
454+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
302455
303456 override string toString ( ) { result = cfn .toString ( ) }
304457
@@ -326,9 +479,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
326479
327480 override DataFlow:: ExprNode getNode ( ) { result .getControlFlowNode ( ) = cfn }
328481
329- override DataFlowCallable getEnclosingCallable ( ) {
330- result .asCallable ( ) = cfn .getEnclosingCallable ( )
331- }
482+ override DataFlowCallable getEnclosingCallable ( ) { result .getAControlFlowNode ( ) = cfn }
332483
333484 override string toString ( ) { result = cfn .toString ( ) }
334485
0 commit comments