Skip to content

Commit c2da2a8

Browse files
committed
C#: Initial implementation of Type based summarymodel generation.
1 parent 6074f22 commit c2da2a8

10 files changed

+184
-36
lines changed

csharp/ql/src/utils/model-generator/CaptureDiscardedSummaryModels.ql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
* @id csharp/utils/model-generator/discarded-summary-models
55
*/
66

7-
private import semmle.code.csharp.dataflow.ExternalFlow
8-
private import internal.CaptureModels
9-
private import internal.CaptureSummaryFlow
7+
import semmle.code.csharp.dataflow.ExternalFlow
8+
import internal.CaptureModels
9+
import internal.CaptureSummaryFlow
1010

11-
from TargetApi api, string flow
11+
from DataFlowTargetApi api, string flow
1212
where flow = captureFlow(api) and hasSummary(api, false)
1313
select flow order by flow

csharp/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
* @tags model-generator
77
*/
88

9-
private import semmle.code.csharp.dataflow.ExternalFlow
10-
private import internal.CaptureModels
11-
private import internal.CaptureSummaryFlow
9+
import semmle.code.csharp.dataflow.ExternalFlow
10+
import internal.CaptureModels
11+
import internal.CaptureSummaryFlow
1212

13-
from TargetApi api, string noflow
14-
where noflow = captureNoFlow(api) and not hasSummary(api, false)
13+
from DataFlowTargetApi api, string noflow
14+
where
15+
noflow = captureNoFlow(api) and
16+
not hasSummary(api, false)
1517
select noflow order by noflow

csharp/ql/src/utils/model-generator/CaptureSinkModels.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* @tags model-generator
77
*/
88

9-
private import internal.CaptureModels
9+
import internal.CaptureModels
1010

11-
from TargetApi api, string sink
11+
from DataFlowTargetApi api, string sink
1212
where sink = captureSink(api)
1313
select sink order by sink

csharp/ql/src/utils/model-generator/CaptureSourceModels.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* @tags model-generator
77
*/
88

9-
private import internal.CaptureModels
9+
import internal.CaptureModels
1010

11-
from TargetApi api, string source
11+
from DataFlowTargetApi api, string source
1212
where source = captureSource(api)
1313
select source order by source

csharp/ql/src/utils/model-generator/CaptureSummaryModels.ql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
* @tags model-generator
77
*/
88

9-
private import semmle.code.csharp.dataflow.ExternalFlow
10-
private import internal.CaptureModels
11-
private import internal.CaptureSummaryFlow
9+
import semmle.code.csharp.dataflow.ExternalFlow
10+
import internal.CaptureModels
11+
import internal.CaptureSummaryFlow
1212

13-
from TargetApi api, string flow
13+
from DataFlowTargetApi api, string flow
1414
where flow = captureFlow(api) and not hasSummary(api, false)
1515
select flow order by flow
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @name Capture Theorems for Free summary models.
3+
* @description Finds applicable summary models to be used by other queries.
4+
* @kind diagnostic
5+
* @id cs/utils/model-generator/summary-models-theorems-for-free
6+
* @tags model-generator
7+
*/
8+
9+
import semmle.code.csharp.dataflow.ExternalFlow
10+
import internal.CaptureTheoremsForFreeSummaryModels
11+
12+
from TheoremTargetApi api, string flow
13+
where flow = captureFlow(api)
14+
select flow order by flow

csharp/ql/src/utils/model-generator/internal/CaptureModels.qll

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
private import CaptureModelsSpecific
77

8-
class TargetApi = TargetApiSpecific;
8+
class DataFlowTargetApi extends TargetApiSpecific {
9+
DataFlowTargetApi() { isRelevantForFlowModels(this) }
10+
}
911

1012
/**
1113
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
@@ -40,37 +42,39 @@ private predicate isRelevantContent(DataFlow::Content c) {
4042
* Gets the summary model for `api` with `input`, `output` and `kind`.
4143
*/
4244
bindingset[input, output, kind]
43-
private string asSummaryModel(TargetApi api, string input, string output, string kind) {
45+
private string asSummaryModel(TargetApiSpecific api, string input, string output, string kind) {
4446
result =
4547
asPartialModel(api) + input + ";" //
4648
+ output + ";" //
4749
+ kind + ";" //
4850
+ "generated"
4951
}
5052

51-
string asNegativeSummaryModel(TargetApi api) { result = asPartialNegativeModel(api) + "generated" }
53+
string asNegativeSummaryModel(TargetApiSpecific api) {
54+
result = asPartialNegativeModel(api) + "generated"
55+
}
5256

5357
/**
5458
* Gets the value summary model for `api` with `input` and `output`.
5559
*/
5660
bindingset[input, output]
57-
private string asValueModel(TargetApi api, string input, string output) {
61+
private string asValueModel(TargetApiSpecific api, string input, string output) {
5862
result = asSummaryModel(api, input, output, "value")
5963
}
6064

6165
/**
6266
* Gets the taint summary model for `api` with `input` and `output`.
6367
*/
6468
bindingset[input, output]
65-
private string asTaintModel(TargetApi api, string input, string output) {
69+
string asTaintModel(TargetApiSpecific api, string input, string output) {
6670
result = asSummaryModel(api, input, output, "taint")
6771
}
6872

6973
/**
7074
* Gets the sink model for `api` with `input` and `kind`.
7175
*/
7276
bindingset[input, kind]
73-
private string asSinkModel(TargetApi api, string input, string kind) {
77+
private string asSinkModel(TargetApiSpecific api, string input, string kind) {
7478
result =
7579
asPartialModel(api) + input + ";" //
7680
+ kind + ";" //
@@ -81,7 +85,7 @@ private string asSinkModel(TargetApi api, string input, string kind) {
8185
* Gets the source model for `api` with `output` and `kind`.
8286
*/
8387
bindingset[output, kind]
84-
private string asSourceModel(TargetApi api, string output, string kind) {
88+
private string asSourceModel(TargetApiSpecific api, string output, string kind) {
8589
result =
8690
asPartialModel(api) + output + ";" //
8791
+ kind + ";" //
@@ -91,7 +95,7 @@ private string asSourceModel(TargetApi api, string output, string kind) {
9195
/**
9296
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
9397
*/
94-
string captureQualifierFlow(TargetApi api) {
98+
string captureQualifierFlow(TargetApiSpecific api) {
9599
exists(DataFlowImplCommon::ReturnNodeExt ret |
96100
api = returnNodeEnclosingCallable(ret) and
97101
isOwnInstanceAccessNode(ret)
@@ -140,7 +144,7 @@ private class ThroughFlowConfig extends TaintTracking::Configuration {
140144

141145
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
142146
source instanceof DataFlow::ParameterNode and
143-
source.getEnclosingCallable() instanceof TargetApi and
147+
source.getEnclosingCallable() instanceof DataFlowTargetApi and
144148
state.(TaintRead).getStep() = 0
145149
}
146150

@@ -184,7 +188,7 @@ private class ThroughFlowConfig extends TaintTracking::Configuration {
184188
/**
185189
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
186190
*/
187-
string captureThroughFlow(TargetApi api) {
191+
string captureThroughFlow(DataFlowTargetApi api) {
188192
exists(
189193
ThroughFlowConfig config, DataFlow::ParameterNode p,
190194
DataFlowImplCommon::ReturnNodeExt returnNodeExt, string input, string output
@@ -211,7 +215,7 @@ private class FromSourceConfiguration extends TaintTracking::Configuration {
211215
override predicate isSource(DataFlow::Node source) { ExternalFlow::sourceNode(source, _) }
212216

213217
override predicate isSink(DataFlow::Node sink) {
214-
exists(TargetApi c |
218+
exists(DataFlowTargetApi c |
215219
sink instanceof DataFlowImplCommon::ReturnNodeExt and
216220
sink.getEnclosingCallable() = c
217221
)
@@ -229,7 +233,7 @@ private class FromSourceConfiguration extends TaintTracking::Configuration {
229233
/**
230234
* Gets the source model(s) of `api`, if there is flow from an existing known source to the return of `api`.
231235
*/
232-
string captureSource(TargetApi api) {
236+
string captureSource(DataFlowTargetApi api) {
233237
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
234238
config.hasFlow(source, sink) and
235239
ExternalFlow::sourceNode(source, kind) and
@@ -259,7 +263,7 @@ private class PropagateToSinkConfiguration extends PropagateToSinkConfigurationS
259263
/**
260264
* Gets the sink model(s) of `api`, if there is flow from a parameter to an existing known sink.
261265
*/
262-
string captureSink(TargetApi api) {
266+
string captureSink(DataFlowTargetApi api) {
263267
exists(DataFlow::Node src, DataFlow::Node sink, PropagateToSinkConfiguration config, string kind |
264268
config.hasFlow(src, sink) and
265269
ExternalFlow::sinkNode(sink, kind) and

csharp/ql/src/utils/model-generator/internal/CaptureModelsSpecific.qll

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,21 @@ private predicate isRelevantForModels(CS::Callable api) {
3636
api.getDeclaringType().getNamespace().getQualifiedName() != "" and
3737
not api instanceof CS::ConversionOperator and
3838
not api instanceof Util::MainMethod and
39-
not isHigherOrder(api) and
4039
not api instanceof CS::Destructor
4140
}
4241

42+
/**
43+
* Holds if it is relevant to generate models for `api` based on data flow analysis.
44+
*/
45+
predicate isRelevantForFlowModels(CS::Callable api) {
46+
isRelevantForModels(api) and not isHigherOrder(api)
47+
}
48+
49+
/**
50+
* Holds if it is relevant to generate models for `api` based on Theorems for Free.
51+
*/
52+
predicate isRelevantForTheoremModels = isRelevantForModels/1;
53+
4354
/**
4455
* A class of callables that are relevant generating summary, source and sinks models for.
4556
*
@@ -49,8 +60,7 @@ private predicate isRelevantForModels(CS::Callable api) {
4960
class TargetApiSpecific extends DotNet::Callable {
5061
TargetApiSpecific() {
5162
this.fromSource() and
52-
this.isUnboundDeclaration() and
53-
isRelevantForModels(this)
63+
this.isUnboundDeclaration()
5464
}
5565
}
5666

@@ -100,7 +110,7 @@ predicate isRelevantType(CS::Type t) {
100110
*/
101111
string qualifierString() { result = "Argument[this]" }
102112

103-
private string parameterAccess(CS::Parameter p) {
113+
string parameterAccess(CS::Parameter p) {
104114
if Collections::isCollectionType(p.getType())
105115
then result = "Argument[" + p.getPosition() + "].Element"
106116
else result = "Argument[" + p.getPosition() + "]"

csharp/ql/src/utils/model-generator/internal/CaptureSummaryFlow.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private import CaptureModels
7575
* Captured Model:
7676
* ```Summaries;BasicFlow;false;AssignToArray;(System.Int32,System.Int32[]);Argument[0];Argument[1].Element;taint```
7777
*/
78-
string captureFlow(TargetApi api) {
78+
string captureFlow(DataFlowTargetApi api) {
7979
result = captureQualifierFlow(api) or
8080
result = captureThroughFlow(api)
8181
}
@@ -84,7 +84,7 @@ string captureFlow(TargetApi api) {
8484
* Gets the negative summary for `api`, if any.
8585
* A negative summary is generated, if there does not exist any positive flow.
8686
*/
87-
string captureNoFlow(TargetApi api) {
87+
string captureNoFlow(DataFlowTargetApi api) {
8888
not exists(captureFlow(api)) and
8989
result = asNegativeSummaryModel(api)
9090
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
private import csharp
2+
private import semmle.code.csharp.commons.Collections as Collections
3+
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
4+
private import semmle.code.csharp.frameworks.system.linq.Expressions
5+
private import CaptureModelsSpecific as Specific
6+
private import CaptureModels
7+
8+
/**
9+
* A class of callables that are relevant generating summaries for based
10+
* on the Theorems for Free approach.
11+
*/
12+
class TheoremTargetApi extends Specific::TargetApiSpecific {
13+
TheoremTargetApi() { Specific::isRelevantForTheoremModels(this) }
14+
15+
private predicate isClassTypeParameter(TypeParameter t) {
16+
t = this.getDeclaringType().(UnboundGeneric).getATypeParameter()
17+
}
18+
19+
private predicate isMethodTypeParameter(TypeParameter t) {
20+
t = this.(UnboundGeneric).getATypeParameter()
21+
}
22+
23+
bindingset[t]
24+
private string getAccess(TypeParameter t) {
25+
exists(string access |
26+
if Collections::isCollectionType(this.getDeclaringType())
27+
then access = ".Element"
28+
else access = ""
29+
|
30+
result = Specific::qualifierString() + ".SyntheticField[Arg" + t.getName() + "]" + access
31+
)
32+
}
33+
34+
private predicate returns(TypeParameter t) { this.getReturnType() = t }
35+
36+
private predicate parameter(TypeParameter t, Parameter p) {
37+
p = this.getAParameter() and
38+
p.getType() = t
39+
}
40+
41+
/**
42+
* Gets the string representation of a summary for `this`, where this has a signature like
43+
* this : T -> unit
44+
* where T is type parameter for the class declaring `this`.
45+
*/
46+
private string getSetterSummary() {
47+
exists(TypeParameter t, Parameter p |
48+
this.isClassTypeParameter(t) and
49+
this.getReturnType() instanceof VoidType and
50+
this.parameter(t, p)
51+
|
52+
result = asTaintModel(this, Specific::parameterAccess(p), this.getAccess(t))
53+
)
54+
}
55+
56+
/**
57+
* Gets the string representation of a summary for `this`, where this has a signature like
58+
* this : unit -> T
59+
* where T is type parameter for the class declaring `this`.
60+
*/
61+
private string getGetterSummary() {
62+
exists(TypeParameter t |
63+
this.isClassTypeParameter(t) and
64+
this.returns(t) and
65+
not this.parameter(t, _)
66+
|
67+
result = asTaintModel(this, this.getAccess(t), "ReturnValue")
68+
)
69+
}
70+
71+
/**
72+
* Gets the string representation of a summary for `this`, where this has a signature like
73+
* this : T -> T
74+
*/
75+
private string getTransformSummary() {
76+
exists(TypeParameter t, Parameter p |
77+
(this.isClassTypeParameter(t) or this.isMethodTypeParameter(t)) and
78+
this.returns(t) and
79+
this.parameter(t, p)
80+
|
81+
result = asTaintModel(this, Specific::parameterAccess(p), "ReturnValue")
82+
)
83+
}
84+
85+
/**
86+
* Gets the string representation of a summary for `this`, where this has a signature like
87+
* this : (T -> V1) -> V2
88+
* where T is type parameter for the class declaring `this`.
89+
*/
90+
private string getApplySummary() {
91+
exists(TypeParameter t, Parameter p1, Parameter p2 |
92+
this.isClassTypeParameter(t) and
93+
p1 = this.getAParameter() and
94+
p2 = p1.getType().(SystemLinqExpressions::DelegateExtType).getDelegateType().getAParameter() and
95+
p2.getType() = t
96+
|
97+
result =
98+
asTaintModel(this, this.getAccess(t),
99+
Specific::parameterAccess(p1) + ".Parameter[" + p2.getPosition() + "]")
100+
)
101+
}
102+
103+
/**
104+
* Gets the string representation of all summaries based on the Theorems for Free approach.
105+
*/
106+
string getSummaries() {
107+
result =
108+
[
109+
this.getSetterSummary(), this.getGetterSummary(), this.getTransformSummary(),
110+
this.getApplySummary()
111+
]
112+
}
113+
}
114+
115+
/**
116+
* Returns the Theorems for Free summaries for `api`.
117+
*/
118+
string captureFlow(TheoremTargetApi api) { result = api.getSummaries() }

0 commit comments

Comments
 (0)