Skip to content

Commit 32ed987

Browse files
committed
Rust: Add new MaD format based on QL-computed canonical paths
1 parent 58cd7b0 commit 32ed987

File tree

5 files changed

+187
-56
lines changed

5 files changed

+187
-56
lines changed

rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ module Input implements InputSig<Location, RustDataFlow> {
3131
crate = ""
3232
)
3333
}
34+
35+
/** Holds if the associated call resolves to `path`. */
36+
final predicate callResolvesTo(string path) {
37+
path = this.getCall().getStaticTarget().(Addressable).getCanonicalPath()
38+
}
3439
}
3540

3641
abstract class SourceBase extends SourceSinkBase { }

rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll

Lines changed: 125 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,19 @@
44
* The extensible relations have the following columns:
55
*
66
* - Sources:
7-
* `crate; path; output; kind; provenance`
7+
* `path; output; kind; provenance`
88
* - Sinks:
9-
* `crate; path; input; kind; provenance`
9+
* `path; input; kind; provenance`
1010
* - Summaries:
11-
* `crate; path; input; output; kind; provenance`
11+
* `path; input; output; kind; provenance`
1212
*
1313
* The interpretation of a row is similar to API-graphs with a left-to-right
1414
* reading.
1515
*
16-
* 1. The `crate` column selects a crate.
17-
* 2. The `path` column selects a function with the given canonical path within
18-
* the crate.
19-
* 3. The `input` column specifies how data enters the element selected by the
20-
* first 2 columns, and the `output` column specifies how data leaves the
21-
* element selected by the first 2 columns. Both `input` and `output` are
16+
* 1. The `path` column selects a function with the given canonical path.
17+
* 2. The `input` column specifies how data enters the element selected by the
18+
* first column, and the `output` column specifies how data leaves the
19+
* element selected by the first column. Both `input` and `output` are
2220
* `.`-separated lists of "access path tokens" to resolve, starting at the
2321
* selected function.
2422
*
@@ -34,12 +32,12 @@
3432
* - `Field[t(i)]`: position `i` inside the variant/struct with canonical path `v`, for example
3533
* `Field[core::option::Option::Some(0)]`.
3634
* - `Field[i]`: the `i`th element of a tuple.
37-
* 4. The `kind` column is a tag that can be referenced from QL to determine to
35+
* 3. The `kind` column is a tag that can be referenced from QL to determine to
3836
* which classes the interpreted elements should be added. For example, for
3937
* sources `"remote"` indicates a default remote flow source, and for summaries
4038
* `"taint"` indicates a default additional taint step and `"value"` indicates a
4139
* globally applicable value-preserving step.
42-
* 5. The `provenance` column is mainly used internally, and should be set to `"manual"` for
40+
* 4. The `provenance` column is mainly used internally, and should be set to `"manual"` for
4341
* all custom models.
4442
*/
4543

@@ -59,11 +57,26 @@ private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprB
5957
* For more information on the `kind` parameter, see
6058
* https://github.com/github/codeql/blob/main/docs/codeql/reusables/threat-model-description.rst.
6159
*/
60+
// TODO: Remove once all models have been moved to `sourceModelNew`.
6261
extensible predicate sourceModel(
6362
string crate, string path, string output, string kind, string provenance,
6463
QlBuiltins::ExtensionId madId
6564
);
6665

66+
/**
67+
* Holds if in a call to the function with canonical path `path`, the value referred
68+
* to by `output` is a flow source of the given `kind`.
69+
*
70+
* `output = "ReturnValue"` simply means the result of the call itself.
71+
*
72+
* For more information on the `kind` parameter, see
73+
* https://github.com/github/codeql/blob/main/docs/codeql/reusables/threat-model-description.rst.
74+
*/
75+
// TODO: Rename to `sourceModel` once all models have been moved to this new format.
76+
extensible predicate sourceModelNew(
77+
string path, string output, string kind, string provenance, QlBuiltins::ExtensionId madId
78+
);
79+
6780
/**
6881
* Holds if in a call to the function with canonical path `path`, defined in the
6982
* crate `crate`, the value referred to by `input` is a flow sink of the given
@@ -75,11 +88,27 @@ extensible predicate sourceModel(
7588
*
7689
* - `sql-injection`: a flow sink for SQL injection.
7790
*/
91+
// TODO: Remove once all models have been moved to `sinkModelNew`.
7892
extensible predicate sinkModel(
7993
string crate, string path, string input, string kind, string provenance,
8094
QlBuiltins::ExtensionId madId
8195
);
8296

97+
/**
98+
* Holds if in a call to the function with canonical path `path`, the value referred
99+
* to by `input` is a flow sink of the given `kind`.
100+
*
101+
* For example, `input = Argument[0]` means the first argument of the call.
102+
*
103+
* The following kinds are supported:
104+
*
105+
* - `sql-injection`: a flow sink for SQL injection.
106+
*/
107+
// TODO: Rename to `sinkModel` once all models have been moved to this new format.
108+
extensible predicate sinkModelNew(
109+
string path, string input, string kind, string provenance, QlBuiltins::ExtensionId madId
110+
);
111+
83112
/**
84113
* Holds if in a call to the function with canonical path `path`, defined in the
85114
* crate `crate`, the value referred to by `input` can flow to the value referred
@@ -88,11 +117,25 @@ extensible predicate sinkModel(
88117
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving
89118
* steps, respectively.
90119
*/
120+
// TODO: Remove once all models have been moved to `summaryModelNew`.
91121
extensible predicate summaryModel(
92122
string crate, string path, string input, string output, string kind, string provenance,
93123
QlBuiltins::ExtensionId madId
94124
);
95125

126+
/**
127+
* Holds if in a call to the function with canonical path `path`, the value referred
128+
* to by `input` can flow to the value referred to by `output`.
129+
*
130+
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving
131+
* steps, respectively.
132+
*/
133+
// TODO: Rename to `summaryModel` once all models have been moved to this new format.
134+
extensible predicate summaryModelNew(
135+
string path, string input, string output, string kind, string provenance,
136+
QlBuiltins::ExtensionId madId
137+
);
138+
96139
/**
97140
* Holds if the given extension tuple `madId` should pretty-print as `model`.
98141
*
@@ -104,15 +147,30 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
104147
model = "Source: " + crate + "; " + path + "; " + output + "; " + kind
105148
)
106149
or
150+
exists(string path, string output, string kind |
151+
sourceModelNew(path, kind, output, _, madId) and
152+
model = "Source: " + path + "; " + output + "; " + kind
153+
)
154+
or
107155
exists(string crate, string path, string input, string kind |
108156
sinkModel(crate, path, kind, input, _, madId) and
109157
model = "Sink: " + crate + "; " + path + "; " + input + "; " + kind
110158
)
111159
or
160+
exists(string path, string input, string kind |
161+
sinkModelNew(path, kind, input, _, madId) and
162+
model = "Sink: " + path + "; " + input + "; " + kind
163+
)
164+
or
112165
exists(string type, string path, string input, string output, string kind |
113166
summaryModel(type, path, input, output, kind, _, madId) and
114167
model = "Summary: " + type + "; " + path + "; " + input + "; " + output + "; " + kind
115168
)
169+
or
170+
exists(string path, string input, string output, string kind |
171+
summaryModelNew(path, input, output, kind, _, madId) and
172+
model = "Summary: " + path + "; " + input + "; " + output + "; " + kind
173+
)
116174
}
117175

118176
private class SummarizedCallableFromModel extends SummarizedCallable::Range {
@@ -145,6 +203,30 @@ private class SummarizedCallableFromModel extends SummarizedCallable::Range {
145203
}
146204
}
147205

206+
private class SummarizedCallableFromModelNew extends SummarizedCallable::Range {
207+
private string path;
208+
209+
SummarizedCallableFromModelNew() {
210+
summaryModelNew(path, _, _, _, _, _) and
211+
this.getCanonicalPath() = path
212+
}
213+
214+
override predicate propagatesFlow(
215+
string input, string output, boolean preservesValue, string model
216+
) {
217+
exists(string kind, QlBuiltins::ExtensionId madId |
218+
summaryModelNew(path, input, output, kind, _, madId) and
219+
model = "MaD:" + madId.toString()
220+
|
221+
kind = "value" and
222+
preservesValue = true
223+
or
224+
kind = "taint" and
225+
preservesValue = false
226+
)
227+
}
228+
}
229+
148230
private class FlowSourceFromModel extends FlowSource::Range {
149231
private string crate;
150232
private string path;
@@ -162,6 +244,22 @@ private class FlowSourceFromModel extends FlowSource::Range {
162244
}
163245
}
164246

247+
private class FlowSourceFromModelNew extends FlowSource::Range {
248+
private string path;
249+
250+
FlowSourceFromModelNew() {
251+
sourceModelNew(path, _, _, _, _) and
252+
this.callResolvesTo(path)
253+
}
254+
255+
override predicate isSource(string output, string kind, Provenance provenance, string model) {
256+
exists(QlBuiltins::ExtensionId madId |
257+
sourceModelNew(path, output, kind, provenance, madId) and
258+
model = "MaD:" + madId.toString()
259+
)
260+
}
261+
}
262+
165263
private class FlowSinkFromModel extends FlowSink::Range {
166264
private string crate;
167265
private string path;
@@ -178,3 +276,19 @@ private class FlowSinkFromModel extends FlowSink::Range {
178276
)
179277
}
180278
}
279+
280+
private class FlowSinkFromModelNew extends FlowSink::Range {
281+
private string path;
282+
283+
FlowSinkFromModelNew() {
284+
sinkModelNew(path, _, _, _, _) and
285+
this.callResolvesTo(path)
286+
}
287+
288+
override predicate isSink(string input, string kind, Provenance provenance, string model) {
289+
exists(QlBuiltins::ExtensionId madId |
290+
sinkModelNew(path, input, kind, provenance, madId) and
291+
model = "MaD:" + madId.toString()
292+
)
293+
}
294+
}

rust/ql/lib/codeql/rust/dataflow/internal/empty.model.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@ extensions:
55
pack: codeql/rust-all
66
extensible: sourceModel
77
data: []
8+
- addsTo:
9+
pack: codeql/rust-all
10+
extensible: sourceModelNew
11+
data: []
812

913
- addsTo:
1014
pack: codeql/rust-all
1115
extensible: sinkModel
1216
data: []
17+
- addsTo:
18+
pack: codeql/rust-all
19+
extensible: sinkModelNew
20+
data: []
1321

1422
- addsTo:
1523
pack: codeql/rust-all
1624
extensible: summaryModel
1725
data: []
26+
- addsTo:
27+
pack: codeql/rust-all
28+
extensible: summaryModelNew
29+
data: []

rust/ql/test/library-tests/dataflow/models/models.expected

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
models
2-
| 1 | Sink: repo::test; <crate::MyFieldEnum>::sink; test-sink; Argument[self].Field[main::MyFieldEnum::D::field_d] |
3-
| 2 | Sink: repo::test; crate::enum_sink; test-sink; Argument[0].Field[main::MyFieldEnum::C::field_c] |
4-
| 3 | Sink: repo::test; crate::simple_sink; test-sink; Argument[0] |
5-
| 4 | Source: repo::test; <crate::MyFieldEnum>::source; test-source; ReturnValue.Field[main::MyFieldEnum::C::field_c] |
6-
| 5 | Source: repo::test; crate::arg_source; test-source; Argument[0] |
7-
| 6 | Source: repo::test; crate::enum_source; test-source; ReturnValue.Field[main::MyFieldEnum::D::field_d] |
8-
| 7 | Source: repo::test; crate::simple_source; test-source; ReturnValue |
9-
| 8 | Summary: repo::test; crate::apply; Argument[0]; Argument[1].Parameter[0]; value |
10-
| 9 | Summary: repo::test; crate::apply; Argument[1].ReturnValue; ReturnValue; value |
11-
| 10 | Summary: repo::test; crate::coerce; Argument[0]; ReturnValue; taint |
12-
| 11 | Summary: repo::test; crate::get_array_element; Argument[0].Element; ReturnValue; value |
13-
| 12 | Summary: repo::test; crate::get_async_number; Argument[0]; ReturnValue.Future; value |
14-
| 13 | Summary: repo::test; crate::get_struct_field; Argument[0].Field[main::MyStruct::field1]; ReturnValue; value |
15-
| 14 | Summary: repo::test; crate::get_tuple_element; Argument[0].Field[0]; ReturnValue; value |
16-
| 15 | Summary: repo::test; crate::get_var_field; Argument[0].Field[main::MyFieldEnum::C::field_c]; ReturnValue; value |
17-
| 16 | Summary: repo::test; crate::get_var_pos; Argument[0].Field[main::MyPosEnum::A(0)]; ReturnValue; value |
18-
| 17 | Summary: repo::test; crate::set_array_element; Argument[0]; ReturnValue.Element; value |
19-
| 18 | Summary: repo::test; crate::set_struct_field; Argument[0]; ReturnValue.Field[main::MyStruct::field2]; value |
20-
| 19 | Summary: repo::test; crate::set_tuple_element; Argument[0]; ReturnValue.Field[1]; value |
21-
| 20 | Summary: repo::test; crate::set_var_field; Argument[0]; ReturnValue.Field[main::MyFieldEnum::D::field_d]; value |
22-
| 21 | Summary: repo::test; crate::set_var_pos; Argument[0]; ReturnValue.Field[main::MyPosEnum::B(0)]; value |
2+
| 1 | Sink: <main::MyFieldEnum>::sink; test-sink; Argument[self].Field[main::MyFieldEnum::D::field_d] |
3+
| 2 | Sink: main::enum_sink; test-sink; Argument[0].Field[main::MyFieldEnum::C::field_c] |
4+
| 3 | Sink: main::simple_sink; test-sink; Argument[0] |
5+
| 4 | Source: <main::MyFieldEnum>::source; test-source; ReturnValue.Field[main::MyFieldEnum::C::field_c] |
6+
| 5 | Source: main::arg_source; test-source; Argument[0] |
7+
| 6 | Source: main::enum_source; test-source; ReturnValue.Field[main::MyFieldEnum::D::field_d] |
8+
| 7 | Source: main::simple_source; test-source; ReturnValue |
9+
| 8 | Summary: main::apply; Argument[0]; Argument[1].Parameter[0]; value |
10+
| 9 | Summary: main::apply; Argument[1].ReturnValue; ReturnValue; value |
11+
| 10 | Summary: main::coerce; Argument[0]; ReturnValue; taint |
12+
| 11 | Summary: main::get_array_element; Argument[0].Element; ReturnValue; value |
13+
| 12 | Summary: main::get_async_number; Argument[0]; ReturnValue.Future; value |
14+
| 13 | Summary: main::get_struct_field; Argument[0].Field[main::MyStruct::field1]; ReturnValue; value |
15+
| 14 | Summary: main::get_tuple_element; Argument[0].Field[0]; ReturnValue; value |
16+
| 15 | Summary: main::get_var_field; Argument[0].Field[main::MyFieldEnum::C::field_c]; ReturnValue; value |
17+
| 16 | Summary: main::get_var_pos; Argument[0].Field[main::MyPosEnum::A(0)]; ReturnValue; value |
18+
| 17 | Summary: main::set_array_element; Argument[0]; ReturnValue.Element; value |
19+
| 18 | Summary: main::set_struct_field; Argument[0]; ReturnValue.Field[main::MyStruct::field2]; value |
20+
| 19 | Summary: main::set_tuple_element; Argument[0]; ReturnValue.Field[1]; value |
21+
| 20 | Summary: main::set_var_field; Argument[0]; ReturnValue.Field[main::MyFieldEnum::D::field_d]; value |
22+
| 21 | Summary: main::set_var_pos; Argument[0]; ReturnValue.Field[main::MyPosEnum::B(0)]; value |
2323
edges
2424
| main.rs:15:9:15:9 | s | main.rs:16:19:16:19 | s | provenance | |
2525
| main.rs:15:9:15:9 | s | main.rs:16:19:16:19 | s | provenance | |
Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
extensions:
22
- addsTo:
33
pack: codeql/rust-all
4-
extensible: sourceModel
4+
extensible: sourceModelNew
55
data:
6-
- ["repo::test", "crate::simple_source", "ReturnValue", "test-source", "manual"]
7-
- ["repo::test", "crate::enum_source", "ReturnValue.Field[main::MyFieldEnum::D::field_d]", "test-source", "manual"]
8-
- ["repo::test", "<crate::MyFieldEnum>::source", "ReturnValue.Field[main::MyFieldEnum::C::field_c]", "test-source", "manual"]
9-
- ["repo::test", "crate::arg_source", "Argument[0]", "test-source", "manual"]
6+
- ["main::simple_source", "ReturnValue", "test-source", "manual"]
7+
- ["main::enum_source", "ReturnValue.Field[main::MyFieldEnum::D::field_d]", "test-source", "manual"]
8+
- ["<main::MyFieldEnum>::source", "ReturnValue.Field[main::MyFieldEnum::C::field_c]", "test-source", "manual"]
9+
- ["main::arg_source", "Argument[0]", "test-source", "manual"]
1010
- addsTo:
1111
pack: codeql/rust-all
12-
extensible: sinkModel
12+
extensible: sinkModelNew
1313
data:
14-
- ["repo::test", "crate::simple_sink", "Argument[0]", "test-sink", "manual"]
15-
- ["repo::test", "crate::enum_sink", "Argument[0].Field[main::MyFieldEnum::C::field_c]", "test-sink", "manual"]
16-
- ["repo::test", "<crate::MyFieldEnum>::sink", "Argument[self].Field[main::MyFieldEnum::D::field_d]", "test-sink", "manual"]
14+
- ["main::simple_sink", "Argument[0]", "test-sink", "manual"]
15+
- ["main::enum_sink", "Argument[0].Field[main::MyFieldEnum::C::field_c]", "test-sink", "manual"]
16+
- ["<main::MyFieldEnum>::sink", "Argument[self].Field[main::MyFieldEnum::D::field_d]", "test-sink", "manual"]
1717
- addsTo:
1818
pack: codeql/rust-all
19-
extensible: summaryModel
19+
extensible: summaryModelNew
2020
data:
21-
- ["repo::test", "crate::coerce", "Argument[0]", "ReturnValue", "taint", "manual"]
22-
- ["repo::test", "crate::get_var_pos", "Argument[0].Field[main::MyPosEnum::A(0)]", "ReturnValue", "value", "manual"]
23-
- ["repo::test", "crate::set_var_pos", "Argument[0]", "ReturnValue.Field[main::MyPosEnum::B(0)]", "value", "manual"]
24-
- ["repo::test", "crate::get_var_field", "Argument[0].Field[main::MyFieldEnum::C::field_c]", "ReturnValue", "value", "manual"]
25-
- ["repo::test", "crate::set_var_field", "Argument[0]", "ReturnValue.Field[main::MyFieldEnum::D::field_d]", "value", "manual"]
26-
- ["repo::test", "crate::get_struct_field", "Argument[0].Field[main::MyStruct::field1]", "ReturnValue", "value", "manual"]
27-
- ["repo::test", "crate::set_struct_field", "Argument[0]", "ReturnValue.Field[main::MyStruct::field2]", "value", "manual"]
28-
- ["repo::test", "crate::get_array_element", "Argument[0].Element", "ReturnValue", "value", "manual"]
29-
- ["repo::test", "crate::set_array_element", "Argument[0]", "ReturnValue.Element", "value", "manual"]
30-
- ["repo::test", "crate::get_tuple_element", "Argument[0].Field[0]", "ReturnValue", "value", "manual"]
31-
- ["repo::test", "crate::set_tuple_element", "Argument[0]", "ReturnValue.Field[1]", "value", "manual"]
32-
- ["repo::test", "crate::apply", "Argument[0]", "Argument[1].Parameter[0]", "value", "manual"]
33-
- ["repo::test", "crate::apply", "Argument[1].ReturnValue", "ReturnValue", "value", "manual"]
34-
- ["repo::test", "crate::get_async_number", "Argument[0]", "ReturnValue.Future", "value", "manual"]
21+
- ["main::coerce", "Argument[0]", "ReturnValue", "taint", "manual"]
22+
- ["main::get_var_pos", "Argument[0].Field[main::MyPosEnum::A(0)]", "ReturnValue", "value", "manual"]
23+
- ["main::set_var_pos", "Argument[0]", "ReturnValue.Field[main::MyPosEnum::B(0)]", "value", "manual"]
24+
- ["main::get_var_field", "Argument[0].Field[main::MyFieldEnum::C::field_c]", "ReturnValue", "value", "manual"]
25+
- ["main::set_var_field", "Argument[0]", "ReturnValue.Field[main::MyFieldEnum::D::field_d]", "value", "manual"]
26+
- ["main::get_struct_field", "Argument[0].Field[main::MyStruct::field1]", "ReturnValue", "value", "manual"]
27+
- ["main::set_struct_field", "Argument[0]", "ReturnValue.Field[main::MyStruct::field2]", "value", "manual"]
28+
- ["main::get_array_element", "Argument[0].Element", "ReturnValue", "value", "manual"]
29+
- ["main::set_array_element", "Argument[0]", "ReturnValue.Element", "value", "manual"]
30+
- ["main::get_tuple_element", "Argument[0].Field[0]", "ReturnValue", "value", "manual"]
31+
- ["main::set_tuple_element", "Argument[0]", "ReturnValue.Field[1]", "value", "manual"]
32+
- ["main::apply", "Argument[0]", "Argument[1].Parameter[0]", "value", "manual"]
33+
- ["main::apply", "Argument[1].ReturnValue", "ReturnValue", "value", "manual"]
34+
- ["main::get_async_number", "Argument[0]", "ReturnValue.Future", "value", "manual"]

0 commit comments

Comments
 (0)