Skip to content

Commit fe85481

Browse files
committed
C#: Add read and store steps for delegate calls.
1 parent ff80b24 commit fe85481

File tree

4 files changed

+181
-6
lines changed

4 files changed

+181
-6
lines changed

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

Lines changed: 86 additions & 2 deletions
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+
TDelegateCallReturnContent(Parameter p) {
1160+
p.getType().getUnboundDeclaration() instanceof SystemLinqExpressions::DelegateExtType
1161+
}
11501162

11511163
cached
11521164
newtype TContentSet =
@@ -1162,7 +1174,13 @@ private module Cached {
11621174
TPrimaryConstructorParameterApproxContent(string firstChar) {
11631175
firstChar = approximatePrimaryConstructorParameterContent(_)
11641176
} or
1165-
TCapturedVariableContentApprox(VariableCapture::CapturedVariable v)
1177+
TCapturedVariableContentApprox(VariableCapture::CapturedVariable v) or
1178+
TDelegateCallArgumentApproxContent(string firstChar) {
1179+
firstChar = approximateDelegateCallArgumentContent(_)
1180+
} or
1181+
TDelegateCallReturnApproxContent(string firstChar) {
1182+
firstChar = approximateDelegateCallReturnContent(_)
1183+
}
11661184

11671185
pragma[nomagic]
11681186
private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, RelevantGvnType t2) {
@@ -2273,6 +2291,22 @@ private predicate recordProperty(RecordType t, ContentSet c, string name) {
22732291
)
22742292
}
22752293

2294+
/**
2295+
* Holds if data can flow from `node1` to `node2` via an assignment to
2296+
* the content set `c` of a delegate call.
2297+
*
2298+
* If there is a delegate call f(x), then we store "x" on "f"
2299+
* using a delegate parameter content set.
2300+
*/
2301+
private predicate storeStepDelegateCall(Node node1, ContentSet c, Node node2) {
2302+
exists(DelegateCall call, Parameter p, int i |
2303+
node1.asExpr() = call.getArgument(i) and
2304+
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = call.getExpr() and
2305+
call.getExpr() = p.getAnAccess() and
2306+
c.isDelegateCallArgument(p, i)
2307+
)
2308+
}
2309+
22762310
/**
22772311
* Holds if data can flow from `node1` to `node2` via an assignment to
22782312
* content `c`.
@@ -2305,6 +2339,8 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
23052339
or
23062340
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
23072341
node2.(FlowSummaryNode).getSummaryNode())
2342+
or
2343+
storeStepDelegateCall(node1, c, node2)
23082344
}
23092345

23102346
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -2425,6 +2461,22 @@ private predicate readContentStep(Node node1, Content c, Node node2) {
24252461
VariableCapture::readStep(node1, c, node2)
24262462
}
24272463

2464+
/**
2465+
* Holds if data can flow from `node1` to `node2` via an assignment to
2466+
* the content set `c` of a delegate call.
2467+
*
2468+
* If there is a delegate call f(x), then we read the result of the delegate
2469+
* call.
2470+
*/
2471+
private predicate readStepDelegateCall(Node node1, ContentSet c, Node node2) {
2472+
exists(DelegateCall call, Parameter p |
2473+
node1.asExpr() = call.getExpr() and
2474+
node2.asExpr() = call and
2475+
call.getExpr() = p.getAnAccess() and
2476+
c.isDelegateCallReturn(p)
2477+
)
2478+
}
2479+
24282480
/**
24292481
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
24302482
*/
@@ -2443,6 +2495,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
24432495
or
24442496
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
24452497
node2.(FlowSummaryNode).getSummaryNode())
2498+
or
2499+
readStepDelegateCall(node1, c, node2)
24462500
}
24472501

24482502
private predicate clearsCont(Node n, Content c) {
@@ -3037,6 +3091,16 @@ class ContentApprox extends TContentApprox {
30373091
exists(VariableCapture::CapturedVariable v |
30383092
this = TCapturedVariableContentApprox(v) and result = "captured " + v
30393093
)
3094+
or
3095+
exists(string firstChar |
3096+
this = TDelegateCallArgumentApproxContent(firstChar) and
3097+
result = "approximated delegate call argument " + firstChar
3098+
)
3099+
or
3100+
exists(string firstChar |
3101+
this = TDelegateCallReturnApproxContent(firstChar) and
3102+
result = "approximated delegate call return " + firstChar
3103+
)
30403104
}
30413105
}
30423106

@@ -3058,6 +3122,22 @@ private string approximatePrimaryConstructorParameterContent(PrimaryConstructorP
30583122
result = pc.getParameter().getName().prefix(1)
30593123
}
30603124

3125+
private string getApproximateParameterName(Parameter p) {
3126+
exists(string name | name = p.getName() |
3127+
name = "" and result = ""
3128+
or
3129+
result = name.prefix(1)
3130+
)
3131+
}
3132+
3133+
private string approximateDelegateCallArgumentContent(DelegateCallArgumentContent dc) {
3134+
result = getApproximateParameterName(dc.getParameter())
3135+
}
3136+
3137+
private string approximateDelegateCallReturnContent(DelegateCallReturnContent dc) {
3138+
result = getApproximateParameterName(dc.getParameter())
3139+
}
3140+
30613141
/** Gets an approximated value for content `c`. */
30623142
pragma[nomagic]
30633143
ContentApprox getContentApprox(Content c) {
@@ -3073,6 +3153,10 @@ ContentApprox getContentApprox(Content c) {
30733153
TPrimaryConstructorParameterApproxContent(approximatePrimaryConstructorParameterContent(c))
30743154
or
30753155
result = TCapturedVariableContentApprox(VariableCapture::getCapturedVariableContent(c))
3156+
or
3157+
result = TDelegateCallArgumentApproxContent(approximateDelegateCallArgumentContent(c))
3158+
or
3159+
result = TDelegateCallReturnApproxContent(approximateDelegateCallReturnContent(c))
30763160
}
30773161

30783162
/**

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 return of a delegate call
275+
* (where the delegate is a parameter)
276+
*/
277+
class DelegateCallReturnContent extends Content, TDelegateCallReturnContent {
278+
private Parameter p;
279+
280+
DelegateCallReturnContent() { this = TDelegateCallReturnContent(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 return of a delegate call
368+
* of parameter `p` (which is of delegate type).
369+
*/
370+
predicate isDelegateCallReturn(Parameter p) { this.isSingleton(TDelegateCallReturnContent(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::DelegateCallReturnContent).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.isDelegateCallReturn(_) 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)