Skip to content

Commit bcdbfef

Browse files
authored
Merge pull request #8329 from michaelnebel/csharp/model-generator
C#: Capture Summary models.
2 parents d4b5eed + 21bcaf6 commit bcdbfef

18 files changed

+807
-277
lines changed

config/identical-files.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@
7373
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
7474
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
7575
],
76+
"Model as Data Generation Java/C# - Utils": [
77+
"java/ql/src/utils/model-generator/ModelGeneratorUtils.qll",
78+
"csharp/ql/src/utils/model-generator/ModelGeneratorUtils.qll"
79+
],
80+
"Model as Data Generation Java/C# - SummaryModels": [
81+
"java/ql/src/utils/model-generator/CaptureSummaryModels.qll",
82+
"csharp/ql/src/utils/model-generator/CaptureSummaryModels.qll"
83+
],
7684
"Sign Java/C#": [
7785
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
7886
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
@@ -508,4 +516,4 @@
508516
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
509517
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll"
510518
]
511-
}
519+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,3 +2026,8 @@ abstract class SyntheticField extends string {
20262026
/** Gets the type of this synthetic field. */
20272027
Type getType() { result instanceof ObjectType }
20282028
}
2029+
2030+
/**
2031+
* Holds if the the content `c` is a container.
2032+
*/
2033+
predicate containerContent(DataFlow::Content c) { c instanceof DataFlow::ElementContent }
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* @name Capture summary models.
3+
* @description Finds applicable summary models to be used by other queries.
4+
* @id csharp/utils/model-generator/summary-models
5+
*/
6+
7+
private import CaptureSummaryModels
8+
9+
/**
10+
* Capture fluent APIs that return `this`.
11+
* Example of a fluent API:
12+
* ```csharp
13+
* public class BasicFlow {
14+
* public BasicFlow ReturnThis(object input)
15+
* {
16+
* // some side effect
17+
* return this;
18+
* }
19+
* ```
20+
* Captured Model:
21+
* ```Summaries;BasicFlow;false;ReturnThis;(System.Object);Argument[Qualifier];ReturnValue;value```
22+
* Capture APIs that transfer taint from an input parameter to an output return
23+
* value or parameter.
24+
* Allows a sequence of read steps followed by a sequence of store steps.
25+
*
26+
* Examples:
27+
*
28+
* ```csharp
29+
* public class BasicFlow {
30+
* private string tainted;
31+
*
32+
* public String ReturnField()
33+
* {
34+
* return tainted;
35+
* }
36+
*
37+
* public void AssignFieldToArray(object[] target)
38+
* {
39+
* target[0] = tainted;
40+
* }
41+
* }
42+
* ```
43+
* Captured Models:
44+
* ```
45+
* Summaries;BasicFlow;false;ReturnField;();Argument[Qualifier];ReturnValue;taint |
46+
* Summaries;BasicFlow;false;AssignFieldToArray;(System.Object[]);Argument[Qualifier];Argument[0].Element;taint
47+
* ```
48+
*
49+
* ```csharp
50+
* public class BasicFlow {
51+
* private string tainted;
52+
*
53+
* public void SetField(string s)
54+
* {
55+
* tainted = s;
56+
* }
57+
* }
58+
* ```
59+
* Captured Model:
60+
* ```Summaries;BasicFlow;false;SetField;(System.String);Argument[0];Argument[Qualifier];taint```
61+
*
62+
* ```csharp
63+
* public class BasicFlow {
64+
* public void ReturnSubstring(string s)
65+
* {
66+
* return s.Substring(0, 1);
67+
* }
68+
* }
69+
* ```
70+
* Captured Model:
71+
* ```Summaries;BasicFlow;false;ReturnSubstring;(System.String);Argument[0];ReturnValue;taint```
72+
*
73+
* ```csharp
74+
* public class BasicFlow {
75+
* public void AssignToArray(int data, int[] target)
76+
* {
77+
* target[0] = data;
78+
* }
79+
* }
80+
* ```
81+
* Captured Model:
82+
* ```Summaries;BasicFlow;false;AssignToArray;(System.Int32,System.Int32[]);Argument[0];Argument[1].Element;taint```
83+
*/
84+
private string captureFlow(TargetApi api) {
85+
result = captureQualifierFlow(api) or
86+
result = captureThroughFlow(api)
87+
}
88+
89+
from TargetApi api, string flow
90+
where flow = captureFlow(api)
91+
select flow order by flow
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* Provides classes and predicates related to capturing summary models
3+
* of the Standard or a 3rd party library.
4+
*/
5+
6+
import CaptureSummaryModelsSpecific
7+
8+
/**
9+
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
10+
*/
11+
string captureQualifierFlow(TargetApi api) {
12+
exists(ReturnNodeExt ret |
13+
api = returnNodeEnclosingCallable(ret) and
14+
isOwnInstanceAccessNode(ret)
15+
) and
16+
result = asValueModel(api, qualifierString(), "ReturnValue")
17+
}
18+
19+
/**
20+
* A FlowState representing a tainted read.
21+
*/
22+
private class TaintRead extends DataFlow::FlowState {
23+
TaintRead() { this = "TaintRead" }
24+
}
25+
26+
/**
27+
* A FlowState representing a tainted write.
28+
*/
29+
private class TaintStore extends DataFlow::FlowState {
30+
TaintStore() { this = "TaintStore" }
31+
}
32+
33+
/**
34+
* A TaintTracking Configuration used for tracking flow through APIs.
35+
* The sources are the parameters of an API and the sinks are the return values (excluding `this`) and parameters.
36+
*
37+
* This can be used to generate Flow summaries for APIs from parameter to return.
38+
*/
39+
class ThroughFlowConfig extends TaintTracking::Configuration {
40+
ThroughFlowConfig() { this = "ThroughFlowConfig" }
41+
42+
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
43+
source instanceof DataFlow::ParameterNode and
44+
source.getEnclosingCallable() instanceof TargetApi and
45+
state instanceof TaintRead
46+
}
47+
48+
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
49+
sink instanceof ReturnNodeExt and
50+
not isOwnInstanceAccessNode(sink) and
51+
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable())) and
52+
(state instanceof TaintRead or state instanceof TaintStore)
53+
}
54+
55+
override predicate isAdditionalTaintStep(
56+
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
57+
DataFlow::FlowState state2
58+
) {
59+
exists(TypedContent tc |
60+
store(node1, tc, node2, _) and
61+
isRelevantContent(tc.getContent()) and
62+
(state1 instanceof TaintRead or state1 instanceof TaintStore) and
63+
state2 instanceof TaintStore
64+
)
65+
or
66+
exists(DataFlow::Content c |
67+
readStep(node1, c, node2) and
68+
isRelevantContent(c) and
69+
state1 instanceof TaintRead and
70+
state2 instanceof TaintRead
71+
)
72+
}
73+
74+
override predicate isSanitizer(DataFlow::Node n) {
75+
exists(Type t | t = n.getType() and not isRelevantType(t))
76+
}
77+
78+
override DataFlow::FlowFeature getAFeature() {
79+
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
80+
}
81+
}
82+
83+
/**
84+
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
85+
*/
86+
string captureThroughFlow(TargetApi api) {
87+
exists(
88+
ThroughFlowConfig config, DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt, string input,
89+
string output
90+
|
91+
config.hasFlow(p, returnNodeExt) and
92+
returnNodeExt.getEnclosingCallable() = api and
93+
input = parameterNodeAsInput(p) and
94+
output = returnNodeAsOutput(returnNodeExt) and
95+
input != output and
96+
result = asTaintModel(api, input, output)
97+
)
98+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Provides predicates related to capturing summary models of the Standard or a 3rd party library.
3+
*/
4+
5+
import csharp
6+
import semmle.code.csharp.dataflow.TaintTracking
7+
import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
8+
import semmle.code.csharp.dataflow.internal.DataFlowPrivate
9+
import ModelGeneratorUtils
10+
11+
Callable returnNodeEnclosingCallable(ReturnNodeExt ret) { result = getNodeEnclosingCallable(ret) }
12+
13+
predicate isOwnInstanceAccessNode(ReturnNode node) { node.asExpr() instanceof ThisAccess }
14+
15+
string qualifierString() { result = "Argument[Qualifier]" }
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import ModelGeneratorUtilsSpecific
2+
3+
/**
4+
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
5+
*/
6+
predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
7+
exists(DataFlow::Content f |
8+
readStep(node1, f, node2) and
9+
if f instanceof DataFlow::FieldContent
10+
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
11+
else
12+
if f instanceof DataFlow::SyntheticFieldContent
13+
then isRelevantType(f.(DataFlow::SyntheticFieldContent).getField().getType())
14+
else any()
15+
)
16+
or
17+
exists(DataFlow::Content f | storeStep(node1, f, node2) | containerContent(f))
18+
}
19+
20+
/**
21+
* Holds if content `c` is either a field or synthetic field of a relevant type
22+
* or a container like content.
23+
*/
24+
predicate isRelevantContent(DataFlow::Content c) {
25+
isRelevantType(c.(DataFlow::FieldContent).getField().getType()) or
26+
isRelevantType(c.(DataFlow::SyntheticFieldContent).getField().getType()) or
27+
containerContent(c)
28+
}
29+
30+
/**
31+
* Gets the summary model for `api` with `input`, `output` and `kind`.
32+
*/
33+
bindingset[input, output, kind]
34+
string asSummaryModel(TargetApi api, string input, string output, string kind) {
35+
result =
36+
asPartialModel(api) + input + ";" //
37+
+ output + ";" //
38+
+ kind
39+
}
40+
41+
/**
42+
* Gets the value summary model for `api` with `input` and `output`.
43+
*/
44+
bindingset[input, output]
45+
string asValueModel(TargetApi api, string input, string output) {
46+
result = asSummaryModel(api, input, output, "value")
47+
}
48+
49+
/**
50+
* Gets the taint summary model for `api` with `input` and `output`.
51+
*/
52+
bindingset[input, output]
53+
string asTaintModel(TargetApi api, string input, string output) {
54+
result = asSummaryModel(api, input, output, "taint")
55+
}
56+
57+
/**
58+
* Gets the sink model for `api` with `input` and `kind`.
59+
*/
60+
bindingset[input, kind]
61+
string asSinkModel(TargetApi api, string input, string kind) {
62+
result = asPartialModel(api) + input + ";" + kind
63+
}
64+
65+
/**
66+
* Gets the source model for `api` with `output` and `kind`.
67+
*/
68+
bindingset[output, kind]
69+
string asSourceModel(TargetApi api, string output, string kind) {
70+
result = asPartialModel(api) + output + ";" + kind
71+
}

0 commit comments

Comments
 (0)