Skip to content

Commit f2f020f

Browse files
authored
Merge pull request github#3610 from hvitved/csharp/dataflow/call-sensitivity
C#: Add call-sensitivity to data-flow call resolution
2 parents 71665a0 + ca86bb8 commit f2f020f

File tree

9 files changed

+426
-260
lines changed

9 files changed

+426
-260
lines changed

csharp/ql/src/semmle/code/csharp/Unification.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ module Unification {
685685
private import Cached
686686

687687
/**
688-
* Holds if types `t1` and `t2` are unifiable. That is, is it possible to replace
688+
* Holds if types `t1` and `t2` are unifiable. That is, it is possible to replace
689689
* all type parameters in `t1` and `t2` with some (other) types to make the two
690690
* substituted terms equal.
691691
*
@@ -722,7 +722,7 @@ module Unification {
722722
}
723723

724724
/**
725-
* Holds if type `t1` subsumes type `t2`. That is, is it possible to replace all
725+
* Holds if type `t1` subsumes type `t2`. That is, it is possible to replace all
726726
* type parameters in `t1` with some (other) types to make the two types equal.
727727
*
728728
* The same limitations that apply to the predicate `unifiable()` apply to this

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,23 +117,52 @@ private module Cached {
117117
result = call.(DelegateDataFlowCall).getARuntimeTarget(cc)
118118
}
119119

120+
/**
121+
* Holds if the set of viable implementations that can be called by `call`
122+
* might be improved by knowing the call context. This is the case if the
123+
* call is a delegate call, or if the qualifier accesses a parameter of
124+
* the enclosing callable `c` (including the implicit `this` parameter).
125+
*/
126+
private predicate mayBenefitFromCallContext(DataFlowCall call, Callable c) {
127+
c = call.getEnclosingCallable() and
128+
(
129+
exists(CallContext cc | exists(viableDelegateCallable(call, cc)) |
130+
not cc instanceof EmptyCallContext
131+
)
132+
or
133+
call.(NonDelegateDataFlowCall).getDispatchCall().mayBenefitFromCallContext()
134+
)
135+
}
136+
120137
/**
121138
* Holds if the call context `ctx` reduces the set of viable run-time
122139
* targets of call `call` in `c`.
123140
*/
124141
cached
125142
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
126-
c = viableImpl(ctx) and
127-
c = call.getEnclosingCallable() and
128-
exists(CallContext cc | exists(viableDelegateCallable(call, cc)) |
129-
not cc instanceof EmptyCallContext
143+
exists(int tgts, int ctxtgts |
144+
mayBenefitFromCallContext(call, c) and
145+
c = viableCallable(ctx) and
146+
ctxtgts = count(viableImplInCallContext(call, ctx)) and
147+
tgts = strictcount(viableImpl(call)) and
148+
ctxtgts < tgts
130149
)
131150
}
132151

152+
/**
153+
* Gets a viable dispatch target of `call` in the context `ctx`. This is
154+
* restricted to those `call`s for which a context might make a difference.
155+
*/
133156
private DotNet::Callable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
134157
exists(ArgumentCallContext cc | result = viableDelegateCallable(call, cc) |
135158
cc.isArgument(ctx.getExpr(), _)
136159
)
160+
or
161+
result =
162+
call
163+
.(NonDelegateDataFlowCall)
164+
.getDispatchCall()
165+
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
137166
}
138167

139168
/**
@@ -155,9 +184,10 @@ private module Cached {
155184
cached
156185
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
157186
exists(int tgts, int ctxtgts |
187+
mayBenefitFromCallContext(call, _) and
158188
c = viableImpl(call) and
159-
ctxtgts = strictcount(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
160-
tgts = strictcount(DataFlowCall ctx | viableImpl(ctx) = call.getEnclosingCallable()) and
189+
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
190+
tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
161191
ctxtgts < tgts
162192
)
163193
}
@@ -278,6 +308,9 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
278308

279309
NonDelegateDataFlowCall() { this = TNonDelegateCall(cfn, dc) }
280310

311+
/** Gets the underlying call. */
312+
DispatchCall getDispatchCall() { result = dc }
313+
281314
override DotNet::Callable getARuntimeTarget() {
282315
result = getCallableForDataFlow(dc.getADynamicTarget())
283316
}

0 commit comments

Comments
 (0)