Skip to content

Commit 7d0e44d

Browse files
committed
Rust: Models-as-data for flow summaries
1 parent defa4cc commit 7d0e44d

File tree

11 files changed

+150
-73
lines changed

11 files changed

+150
-73
lines changed

rust/ql/lib/codeql/rust/dataflow/FlowSummary.qll

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,7 @@ private import codeql.rust.elements.internal.CallExprBaseImpl::Impl as CallExprB
77
// import all instances below
88
private module Summaries {
99
private import codeql.rust.Frameworks
10-
11-
// TODO: Use models-as-data when it's available
12-
private class UnwrapSummary extends SummarizedCallable::Range {
13-
UnwrapSummary() { this = "lang:core::_::<crate::option::Option>::unwrap" }
14-
15-
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
16-
input = "Argument[self].Variant[crate::option::Option::Some(0)]" and
17-
output = "ReturnValue" and
18-
preservesValue = true
19-
}
20-
}
10+
private import codeql.rust.dataflow.internal.ModelsAsData
2111
}
2212

2313
/** Provides the `Range` class used to define the extent of `LibraryCallable`. */
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* Defines extensible predicates for contributing library models from data extensions.
3+
*/
4+
5+
private import rust
6+
private import codeql.rust.dataflow.FlowSummary
7+
8+
/**
9+
* Holds if in a call to the function with canonical path `path`, defined in the
10+
* crate `crate`, the value referred to by `output` is a flow source of the given
11+
* `kind`.
12+
*
13+
* `output = "ReturnValue"` simply means the result of the call itself.
14+
*
15+
* The following kinds are supported:
16+
*
17+
* - `remote`: a general remote flow source.
18+
*/
19+
extensible predicate sourceModel(
20+
string crate, string path, string output, string kind, string provenance,
21+
QlBuiltins::ExtensionId madId
22+
);
23+
24+
/**
25+
* Holds if in a call to the function with canonical path `path`, defined in the
26+
* crate `crate`, the value referred to by `input` is a flow sink of the given
27+
* `kind`.
28+
*
29+
* For example, `input = Argument[0]` means the first argument of the call.
30+
*
31+
* The following kinds are supported:
32+
*
33+
* - `sql-injection`: a flow sink for SQL injection.
34+
*/
35+
extensible predicate sinkModel(
36+
string crate, string path, string input, string kind, string provenance,
37+
QlBuiltins::ExtensionId madId
38+
);
39+
40+
/**
41+
* Holds if in a call to the function with canonical path `path`, defined in the
42+
* crate `crate`, the value referred to by `input` can flow to the value referred
43+
* to by `output`.
44+
*
45+
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving
46+
* steps, respectively.
47+
*/
48+
extensible predicate summaryModel(
49+
string crate, string path, string input, string output, string kind, string provenance,
50+
QlBuiltins::ExtensionId madId
51+
);
52+
53+
/**
54+
* Holds if the given extension tuple `madId` should pretty-print as `model`.
55+
*
56+
* This predicate should only be used in tests.
57+
*/
58+
predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
59+
exists(string crate, string path, string output, string kind |
60+
sourceModel(crate, path, kind, output, _, madId) and
61+
model = "Source: " + crate + "; " + path + "; " + output + "; " + kind
62+
)
63+
or
64+
exists(string crate, string path, string input, string kind |
65+
sinkModel(crate, path, kind, input, _, madId) and
66+
model = "Sink: " + crate + "; " + path + "; " + input + "; " + kind
67+
)
68+
or
69+
exists(string type, string path, string input, string output, string kind |
70+
summaryModel(type, path, input, output, kind, _, madId) and
71+
model = "Summary: " + type + "; " + path + "; " + input + "; " + output + "; " + kind
72+
)
73+
}
74+
75+
private class SummarizedCallableFromModel extends SummarizedCallable::Range {
76+
private string crate;
77+
private string path;
78+
79+
SummarizedCallableFromModel() {
80+
summaryModel(crate, path, _, _, _, _, _) and
81+
this = crate + "::_::" + path
82+
}
83+
84+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
85+
exists(string kind | summaryModel(crate, path, input, output, kind, _, _) |
86+
kind = "value" and
87+
preservesValue = true
88+
or
89+
kind = "taint" and
90+
preservesValue = false
91+
)
92+
}
93+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extensions:
2+
# Make sure that the extensible model predicates have at least one definition
3+
# to avoid errors about undefined extensionals.
4+
- addsTo:
5+
pack: codeql/rust-all
6+
extensible: sourceModel
7+
data: []
8+
9+
- addsTo:
10+
pack: codeql/rust-all
11+
extensible: sinkModel
12+
data: []
13+
14+
- addsTo:
15+
pack: codeql/rust-all
16+
extensible: summaryModel
17+
data: []
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/rust-all
4+
extensible: summaryModel
5+
data:
6+
- ["lang:core", "<crate::option::Option>::unwrap", "Argument[self].Variant[crate::option::Option::Some(0)]", "ReturnValue", "value", "manual"]

rust/ql/lib/qlpack.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ dependencies:
1313
codeql/ssa: ${workspace}
1414
codeql/tutorial: ${workspace}
1515
codeql/util: ${workspace}
16+
dataExtensions:
17+
- /**/*.model.yml
1618
warnOnImplicitThis: true
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/rust-all
4+
extensible: summaryModel
5+
data:
6+
- ["repo::test", "crate::identity", "Argument[0]", "ReturnValue", "value", "manual"]
7+
- ["repo::test", "crate::coerce", "Argument[0]", "ReturnValue", "taint", "manual"]
8+
- ["repo::test", "crate::get_var_pos", "Argument[0].Variant[crate::MyPosEnum::A(0)]", "ReturnValue", "value", "manual"]
9+
- ["repo::test", "crate::set_var_pos", "Argument[0]", "ReturnValue.Variant[crate::MyPosEnum::B(0)]", "value", "manual"]
10+
- ["repo::test", "crate::get_var_field", "Argument[0].Variant[crate::MyFieldEnum::C::field_c]", "ReturnValue", "value", "manual"]
11+
- ["repo::test", "crate::set_var_field", "Argument[0]", "ReturnValue.Variant[crate::MyFieldEnum::D::field_d]", "value", "manual"]

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

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -15,66 +15,6 @@ query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c)
1515
Private::External::invalidSpecComponent(s, c)
1616
}
1717

18-
private class SummarizedCallableIdentity extends SummarizedCallable::Range {
19-
SummarizedCallableIdentity() { this = "repo::test::_::crate::identity" }
20-
21-
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
22-
input = "Argument[0]" and
23-
output = "ReturnValue" and
24-
preservesValue = true
25-
}
26-
}
27-
28-
private class SummarizedCallableCoerce extends SummarizedCallable::Range {
29-
SummarizedCallableCoerce() { this = "repo::test::_::crate::coerce" }
30-
31-
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
32-
input = "Argument[0]" and
33-
output = "ReturnValue" and
34-
preservesValue = false
35-
}
36-
}
37-
38-
private class SummarizedCallableGetVarPos extends SummarizedCallable::Range {
39-
SummarizedCallableGetVarPos() { this = "repo::test::_::crate::get_var_pos" }
40-
41-
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
42-
input = "Argument[0].Variant[crate::MyPosEnum::A(0)]" and
43-
output = "ReturnValue" and
44-
preservesValue = true
45-
}
46-
}
47-
48-
private class SummarizedCallableSetVarPos extends SummarizedCallable::Range {
49-
SummarizedCallableSetVarPos() { this = "repo::test::_::crate::set_var_pos" }
50-
51-
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
52-
input = "Argument[0]" and
53-
output = "ReturnValue.Variant[crate::MyPosEnum::B(0)]" and
54-
preservesValue = true
55-
}
56-
}
57-
58-
private class SummarizedCallableGetVarField extends SummarizedCallable::Range {
59-
SummarizedCallableGetVarField() { this = "repo::test::_::crate::get_var_field" }
60-
61-
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
62-
input = "Argument[0].Variant[crate::MyFieldEnum::C::field_c]" and
63-
output = "ReturnValue" and
64-
preservesValue = true
65-
}
66-
}
67-
68-
private class SummarizedCallableSetVarField extends SummarizedCallable::Range {
69-
SummarizedCallableSetVarField() { this = "repo::test::_::crate::set_var_field" }
70-
71-
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
72-
input = "Argument[0]" and
73-
output = "ReturnValue.Variant[crate::MyFieldEnum::D::field_d]" and
74-
preservesValue = true
75-
}
76-
}
77-
7818
module CustomConfig implements DataFlow::ConfigSig {
7919
predicate isSource(DataFlow::Node source) { DefaultFlowConfig::isSource(source) }
8020

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
query: queries/security/CWE-089/SqlInjection.ql
2-
postprocess: utils/InlineExpectationsTestQuery.ql
2+
postprocess:
3+
- utils/PrettyPrintModels.ql
4+
- utils/InlineExpectationsTestQuery.ql

rust/ql/test/utils/InlineFlowTest.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ private import codeql.rust.controlflow.CfgNodes
99
private import codeql.rust.dataflow.DataFlow
1010
private import codeql.rust.dataflow.internal.DataFlowImpl
1111
private import codeql.rust.dataflow.internal.TaintTrackingImpl
12+
private import codeql.rust.dataflow.internal.ModelsAsData as MaD
1213
private import internal.InlineExpectationsTestImpl as InlineExpectationsTestImpl
1314

1415
// Holds if the target expression of `call` is a path and the string representation of the path is `name`.
@@ -38,7 +39,7 @@ private module FlowTestImpl implements InputSig<Location, RustDataFlow> {
3839
exists(sink)
3940
}
4041

41-
predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) { none() }
42+
predicate interpretModelForTest = MaD::interpretModelForTest/2;
4243
}
4344

4445
import InlineFlowTestMake<Location, RustDataFlow, RustTaintTracking, InlineExpectationsTestImpl::Impl, FlowTestImpl>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* @kind test-postprocess
3+
*/
4+
5+
import codeql.rust.dataflow.internal.ModelsAsData
6+
import codeql.dataflow.test.ProvenancePathGraph
7+
import codeql.dataflow.test.ProvenancePathGraph::TestPostProcessing::TranslateProvenanceResults<interpretModelForTest/2>

0 commit comments

Comments
 (0)