Skip to content

Commit 1fd2449

Browse files
authored
Merge pull request #5052 from tamasvajk/feature/fnptr-df
C#: Add data flow 'getARuntimeTarget' predicate to 'FunctionPointerCall'
2 parents cf860f1 + 64f0dfb commit 1fd2449

File tree

18 files changed

+243
-74
lines changed

18 files changed

+243
-74
lines changed

csharp/ql/src/Dead Code/DeadStoreOfLocal.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ predicate mayEscape(LocalVariable v) {
6363
exists(Callable c, Expr e, Expr succ | c = getACapturingCallableAncestor(v) |
6464
e = getADelegateExpr(c) and
6565
DataFlow::localExprFlow(e, succ) and
66-
not succ = any(DelegateCall dc).getDelegateExpr() and
66+
not succ = any(DelegateCall dc).getExpr() and
6767
not succ = any(Cast cast).getExpr() and
6868
not succ = any(Call call | nonEscapingCall(call)).getAnArgument() and
6969
not succ = any(AssignableDefinition ad | ad.getTarget() instanceof LocalVariable).getSource()

csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Delegate.qll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ private class TranslatedDelegateInvokeCall extends TranslatedCompilerGeneratedCa
9797
)
9898
}
9999

100-
override TranslatedExprBase getQualifier() {
101-
result = getTranslatedExpr(generatedBy.getDelegateExpr())
102-
}
100+
override TranslatedExprBase getQualifier() { result = getTranslatedExpr(generatedBy.getExpr()) }
103101

104102
override Instruction getQualifierResult() { result = getQualifier().getResult() }
105103

csharp/ql/src/semmle/code/csharp/dataflow/CallContext.qll

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ cached
1111
private newtype TCallContext =
1212
TEmptyCallContext() or
1313
TArgNonDelegateCallContext(Expr arg) { exists(DispatchCall dc | arg = dc.getArgument(_)) } or
14-
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) }
14+
TArgDelegateCallContext(DelegateCall dc, int i) { exists(dc.getArgument(i)) } or
15+
TArgFunctionPointerCallContext(FunctionPointerCall fptrc, int i) { exists(fptrc.getArgument(i)) }
1516

1617
/**
1718
* A call context.
@@ -60,12 +61,14 @@ class NonDelegateCallArgumentCallContext extends ArgumentCallContext, TArgNonDel
6061
override Location getLocation() { result = arg.getLocation() }
6162
}
6263

63-
/** An argument of a delegate call. */
64-
class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateCallContext {
65-
DelegateCall dc;
64+
/** An argument of a delegate or function pointer call. */
65+
class DelegateLikeCallArgumentCallContext extends ArgumentCallContext {
66+
DelegateLikeCall dc;
6667
int arg;
6768

68-
DelegateCallArgumentCallContext() { this = TArgDelegateCallContext(dc, arg) }
69+
DelegateLikeCallArgumentCallContext() {
70+
this = TArgDelegateCallContext(dc, arg) or this = TArgFunctionPointerCallContext(dc, arg)
71+
}
6972

7073
override predicate isArgument(Expr call, int i) {
7174
call = dc and
@@ -76,3 +79,11 @@ class DelegateCallArgumentCallContext extends ArgumentCallContext, TArgDelegateC
7679

7780
override Location getLocation() { result = dc.getArgument(arg).getLocation() }
7881
}
82+
83+
/** An argument of a delegate call. */
84+
class DelegateCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
85+
TArgDelegateCallContext { }
86+
87+
/** An argument of a function pointer call. */
88+
class FunctionPointerCallArgumentCallContext extends DelegateLikeCallArgumentCallContext,
89+
TArgFunctionPointerCallContext { }

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private module Cached {
101101
TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) {
102102
cfn.getElement() = dc.getCall()
103103
} or
104-
TExplicitDelegateCall(ControlFlow::Nodes::ElementNode cfn, DelegateCall dc) {
104+
TExplicitDelegateLikeCall(ControlFlow::Nodes::ElementNode cfn, DelegateLikeCall dc) {
105105
cfn.getElement() = dc
106106
} or
107107
TTransitiveCapturedCall(ControlFlow::Nodes::ElementNode cfn, Callable target) {
@@ -308,12 +308,12 @@ abstract class DelegateDataFlowCall extends DataFlowCall {
308308
override DataFlowCallable getARuntimeTarget() { result = this.getARuntimeTarget(_) }
309309
}
310310

311-
/** An explicit delegate call relevant for data flow. */
312-
class ExplicitDelegateDataFlowCall extends DelegateDataFlowCall, TExplicitDelegateCall {
311+
/** An explicit delegate or function pointer call relevant for data flow. */
312+
class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDelegateLikeCall {
313313
private ControlFlow::Nodes::ElementNode cfn;
314-
private DelegateCall dc;
314+
private DelegateLikeCall dc;
315315

316-
ExplicitDelegateDataFlowCall() { this = TExplicitDelegateCall(cfn, dc) }
316+
ExplicitDelegateLikeDataFlowCall() { this = TExplicitDelegateLikeCall(cfn, dc) }
317317

318318
override DataFlowCallable getARuntimeTarget(CallContext::CallContext cc) {
319319
result = getCallableForDataFlow(dc.getARuntimeTarget(cc))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,7 @@ private module OutNodes {
13941394

13951395
private DataFlowCall csharpCall(Expr e, ControlFlow::Node cfn) {
13961396
e = any(DispatchCall dc | result = TNonDelegateCall(cfn, dc)).getCall() or
1397-
result = TExplicitDelegateCall(cfn, e)
1397+
result = TExplicitDelegateLikeCall(cfn, e)
13981398
}
13991399

14001400
/** A valid return type for a method that uses `yield return`. */

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

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ private import semmle.code.csharp.dataflow.FlowSummary
1414
private import semmle.code.csharp.dispatch.Dispatch
1515
private import semmle.code.csharp.frameworks.system.linq.Expressions
1616

17+
/** A source of flow for a delegate or function pointer expression. */
18+
abstract private class DelegateLikeFlowSource extends DataFlow::ExprNode {
19+
/** Gets the callable that is referenced in this delegate or function pointer flow source. */
20+
abstract Callable getCallable();
21+
}
22+
1723
/** A source of flow for a delegate expression. */
18-
private class DelegateFlowSource extends DataFlow::ExprNode {
24+
private class DelegateFlowSource extends DelegateLikeFlowSource {
1925
Callable c;
2026

2127
DelegateFlowSource() {
@@ -27,11 +33,29 @@ private class DelegateFlowSource extends DataFlow::ExprNode {
2733
}
2834

2935
/** Gets the callable that is referenced in this delegate flow source. */
30-
Callable getCallable() { result = c }
36+
override Callable getCallable() { result = c }
3137
}
3238

33-
/** A sink of flow for a delegate expression. */
34-
abstract private class DelegateFlowSink extends DataFlow::Node {
39+
/** A source of flow for a function pointer expression. */
40+
private class FunctionPointerFlowSource extends DelegateLikeFlowSource {
41+
Callable c;
42+
43+
FunctionPointerFlowSource() {
44+
c =
45+
this.getExpr()
46+
.(AddressOfExpr)
47+
.getOperand()
48+
.(CallableAccess)
49+
.getTarget()
50+
.getUnboundDeclaration()
51+
}
52+
53+
/** Gets the callable that is referenced in this function pointer flow source. */
54+
override Callable getCallable() { result = c }
55+
}
56+
57+
/** A sink of flow for a delegate or function pointer expression. */
58+
abstract private class DelegateLikeFlowSink extends DataFlow::Node {
3559
/**
3660
* Gets an actual run-time target of this delegate call in the given call
3761
* context, if any. The call context records the *last* call required to
@@ -85,33 +109,33 @@ abstract private class DelegateFlowSink extends DataFlow::Node {
85109
*/
86110
cached
87111
Callable getARuntimeTarget(CallContext context) {
88-
exists(DelegateFlowSource dfs |
112+
exists(DelegateLikeFlowSource dfs |
89113
flowsFrom(this, dfs, _, context) and
90114
result = dfs.getCallable()
91115
)
92116
}
93117
}
94118

95-
/** A delegate call expression. */
96-
class DelegateCallExpr extends DelegateFlowSink, DataFlow::ExprNode {
97-
DelegateCall dc;
119+
/** A delegate or function pointer call expression. */
120+
class DelegateLikeCallExpr extends DelegateLikeFlowSink, DataFlow::ExprNode {
121+
DelegateLikeCall dc;
98122

99-
DelegateCallExpr() { this.getExpr() = dc.getDelegateExpr() }
123+
DelegateLikeCallExpr() { this.getExpr() = dc.getExpr() }
100124

101-
/** Gets the delegate call that this expression belongs to. */
102-
DelegateCall getDelegateCall() { result = dc }
125+
/** Gets the delegate or function pointer call that this expression belongs to. */
126+
DelegateLikeCall getCall() { result = dc }
103127
}
104128

105129
/** A parameter of delegate type belonging to a callable with a flow summary. */
106-
class SummaryDelegateParameterSink extends DelegateFlowSink, ParameterNode {
130+
class SummaryDelegateParameterSink extends DelegateLikeFlowSink, ParameterNode {
107131
SummaryDelegateParameterSink() {
108132
this.getType() instanceof SystemLinqExpressions::DelegateExtType and
109133
this.isParameterOf(any(SummarizedCallable c), _)
110134
}
111135
}
112136

113137
/** A delegate expression that is added to an event. */
114-
class AddEventSource extends DelegateFlowSink, DataFlow::ExprNode {
138+
class AddEventSource extends DelegateLikeFlowSink, DataFlow::ExprNode {
115139
AddEventExpr ae;
116140

117141
AddEventSource() { this.getExpr() = ae.getRValue() }
@@ -150,7 +174,7 @@ private class NormalReturnNode extends Node {
150174
* records the last call on the path from `node` to `sink`, if any.
151175
*/
152176
private predicate flowsFrom(
153-
DelegateFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
177+
DelegateLikeFlowSink sink, DataFlow::Node node, boolean isReturned, CallContext lastCall
154178
) {
155179
// Base case
156180
sink = node and
@@ -188,7 +212,8 @@ private predicate flowsFrom(
188212
or
189213
// Flow into a callable (delegate call)
190214
exists(
191-
ParameterNode mid, CallContext prevLastCall, DelegateCall call, Callable c, Parameter p, int i
215+
ParameterNode mid, CallContext prevLastCall, DelegateLikeCall call, Callable c, Parameter p,
216+
int i
192217
|
193218
flowsFrom(sink, mid, isReturned, prevLastCall) and
194219
isReturned = false and
@@ -238,14 +263,14 @@ private predicate flowIntoNonDelegateCall(NonDelegateCall call, Expr arg, DotNet
238263
}
239264

240265
pragma[noinline]
241-
private predicate flowIntoDelegateCall(DelegateCall call, Callable c, Expr arg, int i) {
242-
exists(DelegateFlowSource dfs, DelegateCallExpr dce |
266+
private predicate flowIntoDelegateCall(DelegateLikeCall call, Callable c, Expr arg, int i) {
267+
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce |
243268
// the call context is irrelevant because the delegate call
244269
// itself will be the context
245270
flowsFrom(dce, dfs, _, _) and
246271
arg = call.getArgument(i) and
247272
c = dfs.getCallable() and
248-
call = dce.getDelegateCall()
273+
call = dce.getCall()
249274
)
250275
}
251276

@@ -255,11 +280,13 @@ private predicate flowOutOfNonDelegateCall(NonDelegateCall call, NormalReturnNod
255280
}
256281

257282
pragma[noinline]
258-
private predicate flowOutOfDelegateCall(DelegateCall dc, NormalReturnNode ret, CallContext lastCall) {
259-
exists(DelegateFlowSource dfs, DelegateCallExpr dce, Callable c |
283+
private predicate flowOutOfDelegateCall(
284+
DelegateLikeCall dc, NormalReturnNode ret, CallContext lastCall
285+
) {
286+
exists(DelegateLikeFlowSource dfs, DelegateLikeCallExpr dce, Callable c |
260287
flowsFrom(dce, dfs, _, lastCall) and
261288
ret.getEnclosingCallable() = c and
262289
c = dfs.getCallable() and
263-
dc = dce.getDelegateCall()
290+
dc = dce.getCall()
264291
)
265292
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ private module CallGraph {
245245
* a library callable and `e` is a delegate argument.
246246
*/
247247
private predicate delegateCall(Call c, Expr e, boolean libraryDelegateCall) {
248-
c = any(DelegateCall dc | e = dc.getDelegateExpr()) and
248+
c = any(DelegateCall dc | e = dc.getExpr()) and
249249
libraryDelegateCall = false
250250
or
251251
c.getTarget().fromLibrary() and

csharp/ql/src/semmle/code/csharp/exprs/Call.qll

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,46 @@ class MutatorOperatorCall extends OperatorCall {
527527
predicate isPostfix() { mutator_invocation_mode(this, 2) }
528528
}
529529

530+
private class DelegateLikeCall_ = @delegate_invocation_expr or @function_pointer_invocation_expr;
531+
532+
/**
533+
* A function pointer or delegate call.
534+
*/
535+
class DelegateLikeCall extends Call, DelegateLikeCall_ {
536+
override Callable getTarget() { none() }
537+
538+
/**
539+
* Gets a potential run-time target of this delegate or function pointer call in the given
540+
* call context `cc`.
541+
*/
542+
Callable getARuntimeTarget(CallContext::CallContext cc) {
543+
exists(DelegateLikeCallExpr call |
544+
this = call.getCall() and
545+
result = call.getARuntimeTarget(cc)
546+
)
547+
}
548+
549+
/**
550+
* Gets the delegate or function pointer expression of this call. For example, the
551+
* delegate expression of `X()` on line 5 is the access to the field `X` in
552+
*
553+
* ```csharp
554+
* class A {
555+
* Action X = () => { };
556+
*
557+
* void CallX() {
558+
* X();
559+
* }
560+
* }
561+
* ```
562+
*/
563+
Expr getExpr() { result = this.getChild(-1) }
564+
565+
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
566+
567+
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
568+
}
569+
530570
/**
531571
* A delegate call, for example `x()` on line 5 in
532572
*
@@ -540,18 +580,13 @@ class MutatorOperatorCall extends OperatorCall {
540580
* }
541581
* ```
542582
*/
543-
class DelegateCall extends Call, @delegate_invocation_expr {
544-
override Callable getTarget() { none() }
545-
583+
class DelegateCall extends DelegateLikeCall, @delegate_invocation_expr {
546584
/**
547585
* Gets a potential run-time target of this delegate call in the given
548586
* call context `cc`.
549587
*/
550-
Callable getARuntimeTarget(CallContext::CallContext cc) {
551-
exists(DelegateCallExpr call |
552-
this = call.getDelegateCall() and
553-
result = call.getARuntimeTarget(cc)
554-
)
588+
override Callable getARuntimeTarget(CallContext::CallContext cc) {
589+
result = DelegateLikeCall.super.getARuntimeTarget(cc)
555590
or
556591
exists(AddEventSource aes, CallContext::CallContext cc2 |
557592
aes = this.getAnAddEventSource(_) and
@@ -567,7 +602,7 @@ class DelegateCall extends Call, @delegate_invocation_expr {
567602
}
568603

569604
private AddEventSource getAnAddEventSource(Callable enclosingCallable) {
570-
this.getDelegateExpr().(EventAccess).getTarget() = result.getEvent() and
605+
this.getExpr().(EventAccess).getTarget() = result.getEvent() and
571606
enclosingCallable = result.getExpr().getEnclosingCallable()
572607
}
573608

@@ -579,25 +614,12 @@ class DelegateCall extends Call, @delegate_invocation_expr {
579614
exists(Callable c | result = getAnAddEventSource(c) | c != this.getEnclosingCallable())
580615
}
581616

582-
override Callable getARuntimeTarget() { result = getARuntimeTarget(_) }
583-
584-
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
585-
586617
/**
587-
* Gets the delegate expression of this delegate call. For example, the
588-
* delegate expression of `X()` on line 5 is the access to the field `X` in
618+
* DEPRECATED: use `getExpr` instead.
589619
*
590-
* ```csharp
591-
* class A {
592-
* Action X = () => { };
593-
*
594-
* void CallX() {
595-
* X();
596-
* }
597-
* }
598-
* ```
620+
* Gets the delegate expression of this call.
599621
*/
600-
Expr getDelegateExpr() { result = this.getChild(-1) }
622+
deprecated Expr getDelegateExpr() { result = this.getExpr() }
601623

602624
override string toString() { result = "delegate call" }
603625

@@ -615,11 +637,7 @@ class DelegateCall extends Call, @delegate_invocation_expr {
615637
* }
616638
* ```
617639
*/
618-
class FunctionPointerCall extends Call, @function_pointer_invocation_expr {
619-
override Callable getTarget() { none() }
620-
621-
override Expr getRuntimeArgument(int i) { result = getArgument(i) }
622-
640+
class FunctionPointerCall extends DelegateLikeCall, @function_pointer_invocation_expr {
623641
override string toString() { result = "function pointer call" }
624642

625643
override string getAPrimaryQlClass() { result = "FunctionPointerCall" }

csharp/ql/src/semmle/code/csharp/metrics/Coupling.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ predicate depends(ValueOrRefType t, ValueOrRefType u) {
7878
or
7979
exists(DelegateCall dc, DelegateType dt |
8080
dc.getEnclosingCallable().getDeclaringType() = t and
81-
dc.getDelegateExpr().getType() = dt and
81+
dc.getExpr().getType() = dt and
8282
usesType(dt.getUnboundDeclaration(), u)
8383
)
8484
or

0 commit comments

Comments
 (0)