Skip to content

Commit 1794d6f

Browse files
committed
C#: Add read and store steps for delegate calls.
1 parent 226756e commit 1794d6f

File tree

4 files changed

+144
-5
lines changed

4 files changed

+144
-5
lines changed

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

Lines changed: 49 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,18 @@ 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+
TDelegateCallArgumentContent(Parameter p, int i) {
1152+
i =
1153+
[0 .. p.getType()
1154+
.getUnboundDeclaration()
1155+
.(SystemLinqExpressions::DelegateExtType)
1156+
.getDelegateType()
1157+
.getNumberOfParameters() - 1]
1158+
} or
1159+
TDelegateCallResultContent(Parameter p) {
1160+
p.getType().getUnboundDeclaration() instanceof SystemLinqExpressions::DelegateExtType
1161+
}
11501162

11511163
cached
11521164
newtype TContentSet =
@@ -2273,6 +2285,22 @@ private predicate recordProperty(RecordType t, ContentSet c, string name) {
22732285
)
22742286
}
22752287

2288+
/**
2289+
* Holds if data can flow from `node1` to `node2` via an assignment to
2290+
* the content set `c` of a delegate call.
2291+
*
2292+
* If there is a delegate call f(x), then we store "x" on "f"
2293+
* using a delegate parameter content set.
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.(PostUpdateNode).getPreUpdateNode().asExpr() = call.getExpr() and
2299+
call.getExpr() = p.getAnAccess() and
2300+
c.isDelegateCallArgument(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,22 @@ private predicate readContentStep(Node node1, Content c, Node node2) {
24252455
VariableCapture::readStep(node1, c, node2)
24262456
}
24272457

2458+
/**
2459+
* Holds if data can flow from `node1` to `node2` via an assignment to
2460+
* the content set `c` of a delegate call.
2461+
*
2462+
* If there is a delegate call f(x), then we read the result of the delegate
2463+
* call.
2464+
*/
2465+
private predicate readStepDelegateCall(Node node1, ContentSet c, Node node2) {
2466+
exists(DelegateCall call, Parameter p |
2467+
node1.asExpr() = call.getExpr() and
2468+
node2.asExpr() = call and
2469+
call.getExpr() = p.getAnAccess() and
2470+
c.isDelegateCallResult(p)
2471+
)
2472+
}
2473+
24282474
/**
24292475
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
24302476
*/
@@ -2443,6 +2489,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
24432489
or
24442490
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
24452491
node2.(FlowSummaryNode).getSummaryNode())
2492+
or
2493+
readStepDelegateCall(node1, c, node2)
24462494
}
24472495

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

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

Lines changed: 70 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,61 @@ class PropertyContent extends Content, TPropertyContent {
238239
override Location getLocation() { result = p.getLocation() }
239240
}
240241

242+
/**
243+
* A reference to the index of an argument of a delegate call
244+
* (where the delegate is a parameter)
245+
*/
246+
class DelegateCallArgumentContent extends Content, TDelegateCallArgumentContent {
247+
private Parameter p;
248+
private int i;
249+
250+
DelegateCallArgumentContent() { this = TDelegateCallArgumentContent(p, i) }
251+
252+
/** Gets the underlying parameter. */
253+
Parameter getParameter() { result = p }
254+
255+
/**
256+
* Gets the type of the `i`th parameter of the underlying parameter `p`
257+
* (which is of delegate type).
258+
*/
259+
Type getType() {
260+
result =
261+
p.getType()
262+
.(SystemLinqExpressions::DelegateExtType)
263+
.getDelegateType()
264+
.getParameter(i)
265+
.getType()
266+
}
267+
268+
override string toString() { result = "delegate parameter " + p.getName() + " at position " + i }
269+
270+
override Location getLocation() { result = p.getLocation() }
271+
}
272+
273+
/**
274+
* A reference to the result of a delegate call
275+
* (where the delegate is a parameter)
276+
*/
277+
class DelegateCallResultContent extends Content, TDelegateCallResultContent {
278+
private Parameter p;
279+
280+
DelegateCallResultContent() { this = TDelegateCallResultContent(p) }
281+
282+
/** Gets the underlying parameter. */
283+
Parameter getParameter() { result = p }
284+
285+
/**
286+
* Gets the return type of the underlying parameter `p` (which is of delegate type).
287+
*/
288+
Type getType() {
289+
result = p.getType().(SystemLinqExpressions::DelegateExtType).getDelegateType().getReturnType()
290+
}
291+
292+
override string toString() { result = "delegate parameter " + p.getName() + " result" }
293+
294+
override Location getLocation() { result = p.getLocation() }
295+
}
296+
241297
/**
242298
* A reference to a synthetic field corresponding to a
243299
* primary constructor parameter.
@@ -299,6 +355,20 @@ class ContentSet extends TContentSet {
299355
*/
300356
predicate isProperty(Property p) { this = TPropertyContentSet(p) }
301357

358+
/**
359+
* Holds if this content set represents the `i`th argument of
360+
* the parameter `p` of delegate type in a delegate call.
361+
*/
362+
predicate isDelegateCallArgument(Parameter p, int i) {
363+
this.isSingleton(TDelegateCallArgumentContent(p, i))
364+
}
365+
366+
/**
367+
* Holds if this content set represents the result of a delegate call
368+
* of parameter `p` (which is of delegate type).
369+
*/
370+
predicate isDelegateCallResult(Parameter p) { this.isSingleton(TDelegateCallResultContent(p)) }
371+
302372
/** Holds if this content set represents the field `f`. */
303373
predicate isField(Field f) { this.isSingleton(TFieldContent(f)) }
304374

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
101101
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel())
102102
}
103103

104-
predicate isUninterestingForDataFlowModels(Callable api) { isHigherOrder(api) }
104+
predicate isUninterestingForDataFlowModels(Callable api) { none() }
105+
106+
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { isHigherOrder(api) }
105107

106108
class SourceOrSinkTargetApi extends Callable {
107109
SourceOrSinkTargetApi() { relevant(this) }
@@ -175,7 +177,9 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
175177
*/
176178
private CS::Type getUnderlyingContType(DataFlow::Content c) {
177179
result = c.(DataFlow::FieldContent).getField().getType() or
178-
result = c.(DataFlow::SyntheticFieldContent).getField().getType()
180+
result = c.(DataFlow::SyntheticFieldContent).getField().getType() or
181+
result = c.(DataFlow::DelegateCallArgumentContent).getType() or
182+
result = c.(DataFlow::DelegateCallResultContent).getType()
179183
}
180184

181185
Type getUnderlyingContentType(DataFlow::ContentSet c) {
@@ -342,6 +346,10 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
342346
or
343347
c.isElement() and
344348
result = "Element"
349+
or
350+
exists(int i | c.isDelegateCallArgument(_, i) and result = "Parameter[" + i + "]")
351+
or
352+
c.isDelegateCallResult(_) and result = "ReturnValue"
345353
}
346354

347355
predicate partialModel = ExternalFlow::partialModel/6;

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ signature module ModelGeneratorInputSig<LocationSig Location, InputSig<Location>
223223
*/
224224
predicate isUninterestingForDataFlowModels(Callable api);
225225

226+
/**
227+
* Holds if it is irrelevant to generate models for `api` based on the heuristic
228+
* (non-content) flow analysis.
229+
*
230+
* This serves as an extra filter for the `relevant`
231+
* and `isUninterestingForDataFlowModels` predicates.
232+
*/
233+
predicate isUninterestingForHeuristicDataFlowModels(Callable api);
234+
226235
/**
227236
* Holds if `namespace`, `type`, `extensible`, `name` and `parameters` are string representations
228237
* for the corresponding MaD columns for `api`.
@@ -300,7 +309,7 @@ module MakeModelGenerator<
300309
}
301310
}
302311

303-
string getOutput(ReturnNodeExt node) {
312+
private string getOutput(ReturnNodeExt node) {
304313
result = PrintReturnNodeExt<paramReturnNodeAsOutput/2>::getOutput(node)
305314
}
306315

@@ -432,7 +441,11 @@ module MakeModelGenerator<
432441

433442
predicate isSource(DataFlow::Node source, FlowState state) {
434443
source instanceof DataFlow::ParameterNode and
435-
source.(NodeExtended).getEnclosingCallable() instanceof DataFlowSummaryTargetApi and
444+
exists(Callable c |
445+
c = source.(NodeExtended).getEnclosingCallable() and
446+
c instanceof DataFlowSummaryTargetApi and
447+
not isUninterestingForHeuristicDataFlowModels(c)
448+
) and
436449
state.(TaintRead).getStep() = 0
437450
}
438451

0 commit comments

Comments
 (0)