Skip to content

Commit 4183fbe

Browse files
authored
Merge pull request github#14295 from hvitved/csharp/lambda-type-flow
C#: Improve lambda dispatch using type flow
2 parents 2d87d76 + ae06040 commit 4183fbe

File tree

11 files changed

+5765
-72
lines changed

11 files changed

+5765
-72
lines changed

csharp/ql/lib/ext/System.Collections.Generic.model.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ extensions:
4747
- ["System.Collections.Generic", "List<>", False, "FindAll", "(System.Predicate<T>)", "", "Argument[this].Element", "ReturnValue", "value", "manual"]
4848
- ["System.Collections.Generic", "List<>", False, "FindLast", "(System.Predicate<T>)", "", "Argument[this].Element", "Argument[0].Parameter[0]", "value", "manual"]
4949
- ["System.Collections.Generic", "List<>", False, "FindLast", "(System.Predicate<T>)", "", "Argument[this].Element", "ReturnValue", "value", "manual"]
50+
- ["System.Collections.Generic", "List<>", False, "ForEach", "(System.Action<T>)", "", "Argument[this].Element", "Argument[0].Parameter[0]", "value", "manual"]
5051
- ["System.Collections.Generic", "List<>", False, "GetEnumerator", "()", "", "Argument[this].Element", "ReturnValue.Property[System.Collections.Generic.List<>+Enumerator.Current]", "value", "manual"]
5152
- ["System.Collections.Generic", "List<>", False, "GetRange", "(System.Int32,System.Int32)", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"]
5253
- ["System.Collections.Generic", "List<>", False, "InsertRange", "(System.Int32,System.Collections.Generic.IEnumerable<T>)", "", "Argument[1].Element", "Argument[this].Element", "value", "manual"]

csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import csharp
44
private import dotnet
55
private import internal.FlowSummaryImpl as Impl
66
private import internal.DataFlowDispatch as DataFlowDispatch
7+
private import Impl::Public::SummaryComponent as SummaryComponentInternal
78

89
class ParameterPosition = DataFlowDispatch::ParameterPosition;
910

@@ -18,8 +19,6 @@ class SummaryComponent = Impl::Public::SummaryComponent;
1819

1920
/** Provides predicates for constructing summary components. */
2021
module SummaryComponent {
21-
private import Impl::Public::SummaryComponent as SummaryComponentInternal
22-
2322
predicate content = SummaryComponentInternal::content/1;
2423

2524
/** Gets a summary component for parameter `i`. */
@@ -155,3 +154,45 @@ private class RecordConstructorFlowRequiredSummaryComponentStack extends Require
155154
)
156155
}
157156
}
157+
158+
class Provenance = Impl::Public::Provenance;
159+
160+
private import semmle.code.csharp.frameworks.system.linq.Expressions
161+
162+
private SummaryComponent delegateSelf() {
163+
exists(ArgumentPosition pos |
164+
result = SummaryComponentInternal::parameter(pos) and
165+
pos.isDelegateSelf()
166+
)
167+
}
168+
169+
private predicate mayInvokeCallback(Callable c, int n) {
170+
c.getParameter(n).getType() instanceof SystemLinqExpressions::DelegateExtType and
171+
not c.fromSource()
172+
}
173+
174+
private class SummarizedCallableWithCallback extends SummarizedCallable {
175+
private int pos;
176+
177+
SummarizedCallableWithCallback() { mayInvokeCallback(this, pos) }
178+
179+
override predicate propagatesFlow(
180+
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
181+
) {
182+
input = SummaryComponentStack::argument(pos) and
183+
output = SummaryComponentStack::push(delegateSelf(), input) and
184+
preservesValue = true
185+
}
186+
187+
override predicate hasProvenance(Provenance provenance) { provenance = "hq-generated" }
188+
}
189+
190+
private class RequiredComponentStackForCallback extends RequiredSummaryComponentStack {
191+
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
192+
exists(int pos |
193+
mayInvokeCallback(_, pos) and
194+
head = delegateSelf() and
195+
tail = SummaryComponentStack::argument(pos)
196+
)
197+
}
198+
}

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,15 @@ private module Cached {
136136
newtype TParameterPosition =
137137
TPositionalParameterPosition(int i) { i = any(Parameter p).getPosition() } or
138138
TThisParameterPosition() or
139-
TImplicitCapturedParameterPosition(LocalScopeVariable v) { capturedWithFlowIn(v) }
139+
TImplicitCapturedParameterPosition(LocalScopeVariable v) { capturedWithFlowIn(v) } or
140+
TDelegateSelfParameterPosition()
140141

141142
cached
142143
newtype TArgumentPosition =
143144
TPositionalArgumentPosition(int i) { i = any(Parameter p).getPosition() } or
144145
TQualifierArgumentPosition() or
145-
TImplicitCapturedArgumentPosition(LocalScopeVariable v) { capturedWithFlowIn(v) }
146+
TImplicitCapturedArgumentPosition(LocalScopeVariable v) { capturedWithFlowIn(v) } or
147+
TDelegateSelfArgumentPosition()
146148
}
147149

148150
import Cached
@@ -480,6 +482,14 @@ class ParameterPosition extends TParameterPosition {
480482
this = TImplicitCapturedParameterPosition(v)
481483
}
482484

485+
/**
486+
* Holds if this position represents a reference to a delegate itself.
487+
*
488+
* Used for tracking flow through captured variables and for improving
489+
* delegate dispatch.
490+
*/
491+
predicate isDelegateSelf() { this = TDelegateSelfParameterPosition() }
492+
483493
/** Gets a textual representation of this position. */
484494
string toString() {
485495
result = "position " + this.getPosition()
@@ -489,6 +499,9 @@ class ParameterPosition extends TParameterPosition {
489499
exists(LocalScopeVariable v |
490500
this.isImplicitCapturedParameterPosition(v) and result = "captured " + v
491501
)
502+
or
503+
this.isDelegateSelf() and
504+
result = "delegate self"
492505
}
493506
}
494507

@@ -505,6 +518,14 @@ class ArgumentPosition extends TArgumentPosition {
505518
this = TImplicitCapturedArgumentPosition(v)
506519
}
507520

521+
/**
522+
* Holds if this position represents a reference to a delegate itself.
523+
*
524+
* Used for tracking flow through captured variables and for improving
525+
* delegate dispatch.
526+
*/
527+
predicate isDelegateSelf() { this = TDelegateSelfArgumentPosition() }
528+
508529
/** Gets a textual representation of this position. */
509530
string toString() {
510531
result = "position " + this.getPosition()
@@ -514,6 +535,9 @@ class ArgumentPosition extends TArgumentPosition {
514535
exists(LocalScopeVariable v |
515536
this.isImplicitCapturedArgumentPosition(v) and result = "captured " + v
516537
)
538+
or
539+
this.isDelegateSelf() and
540+
result = "delegate self"
517541
}
518542
}
519543

@@ -527,4 +551,6 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
527551
ppos.isImplicitCapturedParameterPosition(v) and
528552
apos.isImplicitCapturedArgumentPosition(v)
529553
)
554+
or
555+
ppos.isDelegateSelf() and apos.isDelegateSelf()
530556
}

0 commit comments

Comments
 (0)