Skip to content

Commit 06ed917

Browse files
committed
C#: Add read and store steps for delegate calls.
1 parent 25b3d76 commit 06ed917

File tree

4 files changed

+97
-11
lines changed

4 files changed

+97
-11
lines changed

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ private import semmle.code.csharp.Unification
1313
private import semmle.code.csharp.controlflow.Guards
1414
private import semmle.code.csharp.dispatch.Dispatch
1515
private import semmle.code.csharp.frameworks.EntityFramework
16+
private import semmle.code.csharp.frameworks.system.linq.Expressions
1617
private import semmle.code.csharp.frameworks.NHibernate
1718
private import semmle.code.csharp.frameworks.Razor
1819
private import semmle.code.csharp.frameworks.system.Collections
@@ -1146,7 +1147,15 @@ private module Cached {
11461147
TPrimaryConstructorParameterContent(Parameter p) {
11471148
p.getCallable() instanceof PrimaryConstructor
11481149
} or
1149-
TCapturedVariableContent(VariableCapture::CapturedVariable v)
1150+
TCapturedVariableContent(VariableCapture::CapturedVariable v) or
1151+
TDelegateParameterContent(Parameter p, int i) {
1152+
i =
1153+
[0 .. p.getType()
1154+
.getUnboundDeclaration()
1155+
.(SystemLinqExpressions::DelegateExtType)
1156+
.getDelegateType()
1157+
.getNumberOfParameters() - 1]
1158+
}
11501159

11511160
cached
11521161
newtype TContentSet =
@@ -2273,6 +2282,25 @@ private predicate recordProperty(RecordType t, ContentSet c, string name) {
22732282
)
22742283
}
22752284

2285+
// node2 needs to be repsentation of the callable. In this case the parameter itself.
2286+
// node1 needs to be the argument position in the call.
2287+
// private predicate storeStepDelegateCall(Node node1, ContentSet c, Node node2) {
2288+
// exists(DelegateCall call, Parameter p, int i |
2289+
// node1.asExpr() = call.getArgument(i) and
2290+
// node2.asExpr() = call.getExpr() and
2291+
// call.getExpr() = p.getAnAccess() and
2292+
// c.isDelegateParameter(p, i)
2293+
// )
2294+
// }
2295+
private predicate storeStepDelegateCall(Node node1, ContentSet c, Node node2) {
2296+
exists(DelegateCall call, Parameter p, int i |
2297+
node1.asExpr() = call.getArgument(i) and
2298+
node2.asExpr() = call and
2299+
call.getExpr() = p.getAnAccess() and
2300+
c.isDelegateParameter(p, i)
2301+
)
2302+
}
2303+
22762304
/**
22772305
* Holds if data can flow from `node1` to `node2` via an assignment to
22782306
* content `c`.
@@ -2305,6 +2333,8 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
23052333
or
23062334
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
23072335
node2.(FlowSummaryNode).getSummaryNode())
2336+
or
2337+
storeStepDelegateCall(node1, c, node2)
23082338
}
23092339

23102340
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -2425,6 +2455,17 @@ private predicate readContentStep(Node node1, Content c, Node node2) {
24252455
VariableCapture::readStep(node1, c, node2)
24262456
}
24272457

2458+
// TODO: Make comment.
2459+
// Consider putting this into readContentStep.
2460+
private predicate readStepDelegateCall(Node node1, ContentSet c, Node node2) {
2461+
exists(DelegateCall call, Parameter p |
2462+
node1.asExpr() = call.getExpr() and
2463+
node2.asExpr() = call and
2464+
call.getExpr() = p.getAnAccess() and
2465+
c.isDelegateParameter(p, _)
2466+
)
2467+
}
2468+
24282469
/**
24292470
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
24302471
*/
@@ -2443,6 +2484,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
24432484
or
24442485
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
24452486
node2.(FlowSummaryNode).getSummaryNode())
2487+
or
2488+
readStepDelegateCall(node1, c, node2)
24462489
}
24472490

24482491
private predicate clearsCont(Node n, Content c) {

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ private import DataFlowDispatch
33
private import DataFlowPrivate
44
private import semmle.code.csharp.controlflow.Guards
55
private import semmle.code.csharp.Unification
6+
private import semmle.code.csharp.frameworks.system.linq.Expressions
67

78
/**
89
* An element, viewed as a node in a data flow graph. Either an expression
@@ -238,6 +239,32 @@ class PropertyContent extends Content, TPropertyContent {
238239
override Location getLocation() { result = p.getLocation() }
239240
}
240241

242+
/** A reference to a parameter with delegate type. */
243+
// TODO: We also need to account for parameter positions.
244+
class DelegateParameterContent extends Content, TDelegateParameterContent {
245+
private Parameter p;
246+
private int i;
247+
248+
DelegateParameterContent() { this = TDelegateParameterContent(p, i) }
249+
250+
/** Gets the underlying parameter. */
251+
Parameter getParameter() { result = p }
252+
253+
Type getType() {
254+
result =
255+
p.getType()
256+
.(SystemLinqExpressions::DelegateExtType)
257+
.getDelegateType()
258+
.getParameter(i)
259+
.getType()
260+
}
261+
262+
// TODO: Also print the index.
263+
override string toString() { result = "delegate parameter " + p.getName() + " position " + i }
264+
265+
override Location getLocation() { result = p.getLocation() }
266+
}
267+
241268
/**
242269
* A reference to a synthetic field corresponding to a
243270
* primary constructor parameter.
@@ -299,6 +326,10 @@ class ContentSet extends TContentSet {
299326
*/
300327
predicate isProperty(Property p) { this = TPropertyContentSet(p) }
301328

329+
predicate isDelegateParameter(Parameter p, int i) {
330+
this.isSingleton(TDelegateParameterContent(p, i))
331+
}
332+
302333
/** Holds if this content set represents the field `f`. */
303334
predicate isField(Field f) { this.isSingleton(TFieldContent(f)) }
304335

csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,23 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
2626
Callable getAsExprEnclosingCallable() { result = this.asExpr().getEnclosingCallable() }
2727
}
2828

29+
class DelegateCallNode extends CS::DataFlow::Node {
30+
DelegateCallNode() { this.asExpr() instanceof CS::DelegateCall }
31+
}
32+
2933
/**
3034
* Holds if any of the parameters of `api` are `System.Func<>`.
3135
*/
32-
private predicate isHigherOrder(Callable api) {
33-
exists(Type t | t = api.getAParameter().getType().getUnboundDeclaration() |
34-
t instanceof SystemLinqExpressions::DelegateExtType
35-
)
36-
}
36+
private predicate isHigherOrder(Callable api) { none() }
3737

3838
private predicate irrelevantAccessor(CS::Accessor a) {
3939
a.getDeclaration().(CS::Property).isReadWrite()
4040
}
4141

4242
private predicate isUninterestingForModels(Callable api) {
43-
api.getDeclaringType().getNamespace().getFullName() = ""
44-
or
43+
// TODO Comment this back in.
44+
// api.getDeclaringType().getNamespace().getFullName() = ""
45+
// or
4546
api instanceof CS::ConversionOperator
4647
or
4748
api instanceof Util::MainMethod
@@ -175,7 +176,8 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
175176
*/
176177
private CS::Type getUnderlyingContType(DataFlow::Content c) {
177178
result = c.(DataFlow::FieldContent).getField().getType() or
178-
result = c.(DataFlow::SyntheticFieldContent).getField().getType()
179+
result = c.(DataFlow::SyntheticFieldContent).getField().getType() or
180+
result = c.(DataFlow::DelegateParameterContent).getType()
179181
}
180182

181183
Type getUnderlyingContentType(DataFlow::ContentSet c) {
@@ -338,6 +340,8 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
338340
result = "Property[" + name + "]"
339341
)
340342
or
343+
exists(CS::Parameter p, int i | c.isDelegateParameter(p, i) and result = "Parameter[" + i + "]")
344+
or
341345
result = "SyntheticField[" + getSyntheticName(c) + "]"
342346
or
343347
c.isElement() and

shared/mad/codeql/mad/modelgenerator/internal/ModelGeneratorImpl.qll

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ signature module ModelGeneratorInputSig<LocationSig Location, InputSig<Location>
6161
Parameter asParameter();
6262
}
6363

64+
class DelegateCallNode extends Lang::Node {
65+
/**
66+
* Gets the enclosing callable of this node.
67+
*/
68+
Callable getEnclosingCallable();
69+
}
70+
6471
/**
6572
* A class of callables that are potentially relevant for generating summary or
6673
* neutral models.
@@ -536,7 +543,8 @@ module MakeModelGenerator<
536543
}
537544

538545
predicate isSink(DataFlow::Node sink) {
539-
sink.(ReturnNodeExt).getEnclosingCallable() instanceof DataFlowSummaryTargetApi
546+
sink.(ReturnNodeExt).getEnclosingCallable() instanceof DataFlowSummaryTargetApi or
547+
sink.(DelegateCallNode).getEnclosingCallable() instanceof DataFlowSummaryTargetApi
540548
}
541549

542550
predicate isAdditionalFlowStep = isAdditionalContentFlowStep/2;
@@ -652,7 +660,7 @@ module MakeModelGenerator<
652660
pragma[nomagic]
653661
private predicate apiContentFlow(
654662
ContentDataFlowSummaryTargetApi api, DataFlow::ParameterNode p,
655-
PropagateContentFlow::AccessPath reads, ReturnNodeExt returnNodeExt,
663+
PropagateContentFlow::AccessPath reads, NodeExtended returnNodeExt,
656664
PropagateContentFlow::AccessPath stores, boolean preservesValue
657665
) {
658666
PropagateContentFlow::flow(p, reads, returnNodeExt, stores, preservesValue) and

0 commit comments

Comments
 (0)