Skip to content

Commit 8d01bbc

Browse files
authored
Merge pull request #18628 from paldepind/rust-flow-summary-generation
Rust: Initial model generation setup
2 parents 294fd0a + cf4f657 commit 8d01bbc

19 files changed

+445
-5
lines changed

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ module DataFlow {
1919
* The value of a parameter at function entry, viewed as a node in a data
2020
* flow graph.
2121
*/
22-
final class ParameterNode extends Node instanceof Node::SourceParameterNode {
23-
/** Gets the parameter that this node corresponds to. */
24-
ParamBase getParameter() { result = super.getParameter().getParamBase() }
25-
}
22+
final class ParameterNode extends Node instanceof Node::SourceParameterNode { }
2623

2724
final class PostUpdateNode = Node::PostUpdateNodePublic;
2825

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ module Node {
149149
*/
150150
ExprCfgNode asExpr() { none() }
151151

152+
/**
153+
* Gets the parameter that corresponds to this node, if any.
154+
*/
155+
ParamBase asParameter() { result = this.(SourceParameterNode).getParameter().getParamBase() }
156+
152157
/**
153158
* Gets the pattern that corresponds to this node, if any.
154159
*/
@@ -274,6 +279,7 @@ module Node {
274279
* flow graph.
275280
*/
276281
abstract class ParameterNode extends Node {
282+
/** Holds if this node is a parameter of `c` at position `pos`. */
277283
abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos);
278284
}
279285

@@ -794,7 +800,7 @@ private class VariantRecordFieldContent extends VariantContent, TVariantRecordFi
794800
}
795801

796802
/** Content stored in a field on a struct. */
797-
private class StructFieldContent extends Content, TStructFieldContent {
803+
class StructFieldContent extends Content, TStructFieldContent {
798804
private Struct s;
799805
private string field_;
800806

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
private import rust as R
2+
private import codeql.mad.test.InlineMadTest
3+
4+
private module InlineMadTestLang implements InlineMadTestLangSig {
5+
class Callable = R::Function;
6+
7+
string getComment(R::Function callable) {
8+
exists(R::Comment comment |
9+
result = comment.getCommentText() and
10+
comment.getLocation().getFile() = callable.getLocation().getFile() and
11+
// When a function is preceded by comments its start line is the line of
12+
// the first comment. Hence all relevant comments are found by including
13+
// comments from the start line and up to the line with the function
14+
// name.
15+
callable.getLocation().getStartLine() <= comment.getLocation().getStartLine() and
16+
comment.getLocation().getStartLine() <= callable.getName().getLocation().getStartLine()
17+
)
18+
}
19+
}
20+
21+
import InlineMadTestImpl<InlineMadTestLang>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Capture content based summary models.
3+
* @description Finds applicable content based summary models to be used by other queries.
4+
* @kind diagnostic
5+
* @id rust/utils/modelgenerator/contentbased-summary-models
6+
* @tags modelgenerator
7+
*/
8+
9+
import internal.CaptureModels
10+
11+
from DataFlowSummaryTargetApi api, string flow
12+
where flow = ContentSensitive::captureFlow(api, _)
13+
select flow order by flow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Capture mixed neutral models.
3+
* @description Finds neutral models to be used by other queries.
4+
* @kind diagnostic
5+
* @id rust/utils/modelgenerator/mixed-neutral-models
6+
* @tags modelgenerator
7+
*/
8+
9+
import internal.CaptureModels
10+
11+
from DataFlowSummaryTargetApi api, string noflow
12+
where noflow = captureMixedNeutral(api)
13+
select noflow order by noflow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Capture mixed summary models.
3+
* @description Finds applicable summary models to be used by other queries.
4+
* @kind diagnostic
5+
* @id rust/utils/modelgenerator/mixed-summary-models
6+
* @tags modelgenerator
7+
*/
8+
9+
import internal.CaptureModels
10+
11+
from DataFlowSummaryTargetApi api, string flow
12+
where flow = captureMixedFlow(api, _)
13+
select flow order by flow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Capture neutral models.
3+
* @description Finds neutral models to be used by other queries.
4+
* @kind diagnostic
5+
* @id rust/utils/modelgenerator/neutral-models
6+
* @tags modelgenerator
7+
*/
8+
9+
import internal.CaptureModels
10+
11+
from DataFlowSummaryTargetApi api, string noflow
12+
where noflow = captureNoFlow(api)
13+
select noflow order by noflow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Capture sink models.
3+
* @description Finds public methods that act as sinks as they flow into a known sink.
4+
* @kind diagnostic
5+
* @id rust/utils/modelgenerator/sink-models
6+
* @tags modelgenerator
7+
*/
8+
9+
import internal.CaptureModels
10+
11+
from DataFlowSinkTargetApi api, string sink
12+
where sink = captureSink(api)
13+
select sink order by sink
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Capture source models.
3+
* @description Finds APIs that act as sources as they expose already known sources.
4+
* @kind diagnostic
5+
* @id rust/utils/modelgenerator/source-models
6+
* @tags modelgenerator
7+
*/
8+
9+
import internal.CaptureModels
10+
11+
from DataFlowSourceTargetApi api, string source
12+
where source = captureSource(api)
13+
select source order by source
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Capture summary models.
3+
* @description Finds applicable summary models to be used by other queries.
4+
* @kind diagnostic
5+
* @id rust/utils/modelgenerator/summary-models
6+
* @tags modelgenerator
7+
*/
8+
9+
import internal.CaptureModels
10+
11+
from DataFlowSummaryTargetApi api, string flow
12+
where flow = captureFlow(api)
13+
select flow order by flow
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/python3
2+
3+
import sys
4+
import os.path
5+
import subprocess
6+
7+
# Add Model as Data script directory to sys.path.
8+
gitroot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
9+
madpath = os.path.join(gitroot, "misc/scripts/models-as-data/")
10+
sys.path.append(madpath)
11+
12+
import generate_flow_model as model
13+
14+
language = "rust"
15+
model.Generator.make(language).run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @name Capture Summary Models Partial Path
3+
* @description Capture Summary Models Partial Path
4+
* @kind path-problem
5+
* @precision low
6+
* @id rust/utils/modelgenerator/summary-models-partial-path
7+
* @severity info
8+
* @tags modelgenerator
9+
*/
10+
11+
private import codeql.rust.dataflow.DataFlow
12+
import utils.modelgenerator.internal.CaptureModels
13+
import PartialFlow::PartialPathGraph
14+
15+
int explorationLimit() { result = 3 }
16+
17+
module PartialFlow = PropagateFlow::FlowExplorationFwd<explorationLimit/0>;
18+
19+
from
20+
PartialFlow::PartialPathNode source, PartialFlow::PartialPathNode sink,
21+
DataFlowSummaryTargetApi api, DataFlow::ParameterNode p
22+
where
23+
PartialFlow::partialFlow(source, sink, _) and
24+
p = source.getNode() and
25+
p.asParameter() = api.getParamList().getAParamBase()
26+
select sink.getNode(), source, sink, "There is flow from a $@ to $@.", source.getNode(),
27+
"parameter", sink.getNode(), "intermediate value"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @name Capture Summary Models Path
3+
* @description Capture Summary Models Path
4+
* @kind path-problem
5+
* @precision low
6+
* @id rust/utils/modelgenerator/summary-models-path
7+
* @severity warning
8+
* @tags modelgenerator
9+
*/
10+
11+
private import codeql.rust.dataflow.DataFlow
12+
import utils.modelgenerator.internal.CaptureModels
13+
import PropagateFlow::PathGraph
14+
15+
from
16+
PropagateFlow::PathNode source, PropagateFlow::PathNode sink, DataFlowSummaryTargetApi api,
17+
DataFlow::Node p, DataFlow::Node returnNodeExt
18+
where
19+
PropagateFlow::flowPath(source, sink) and
20+
p = source.getNode() and
21+
returnNodeExt = sink.getNode() and
22+
exists(captureThroughFlow0(api, p, returnNodeExt))
23+
select sink.getNode(), source, sink, "There is flow from $@ to the $@.", source.getNode(),
24+
"parameter", sink.getNode(), "return value"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The queries in this directory are purely used for model generator debugging purposes in VS Code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
private import codeql.util.Unit
2+
private import rust
3+
private import rust as R
4+
private import codeql.rust.dataflow.DataFlow
5+
private import codeql.rust.dataflow.internal.DataFlowImpl
6+
private import codeql.rust.dataflow.internal.TaintTrackingImpl
7+
private import codeql.mad.modelgenerator.internal.ModelGeneratorImpl
8+
private import codeql.rust.dataflow.internal.FlowSummaryImpl as FlowSummary
9+
10+
module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataFlow> {
11+
// NOTE: We are not using type information for now.
12+
class Type = Unit;
13+
14+
class Parameter = R::ParamBase;
15+
16+
class Callable = R::Callable;
17+
18+
class NodeExtended extends DataFlow::Node {
19+
Callable getAsExprEnclosingCallable() { result = this.asExpr().getScope() }
20+
21+
Type getType() { any() }
22+
23+
Callable getEnclosingCallable() {
24+
result = this.(Node::Node).getEnclosingCallable().asCfgScope()
25+
}
26+
}
27+
28+
private predicate relevant(Function api) {
29+
// This excludes closures (these are not exported API endpoints) and
30+
// functions without a `pub` visiblity. A function can be `pub` without
31+
// ultimately being exported by a crate, so this is an overapproximation.
32+
api.hasVisibility()
33+
or
34+
// If a method implements a public trait it is exposed through the trait.
35+
// We overapproximate this by including all trait method implementations.
36+
exists(Impl impl | impl.hasTrait() and impl.getAssocItemList().getAssocItem(_) = api)
37+
}
38+
39+
predicate isUninterestingForDataFlowModels(Callable api) { none() }
40+
41+
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }
42+
43+
class SourceOrSinkTargetApi extends Callable {
44+
SourceOrSinkTargetApi() { relevant(this) }
45+
}
46+
47+
class SinkTargetApi extends SourceOrSinkTargetApi { }
48+
49+
class SourceTargetApi extends SourceOrSinkTargetApi { }
50+
51+
class SummaryTargetApi extends Callable {
52+
private Callable lift;
53+
54+
SummaryTargetApi() {
55+
lift = this and
56+
relevant(this)
57+
}
58+
59+
Callable lift() { result = lift }
60+
61+
predicate isRelevant() { relevant(this) }
62+
}
63+
64+
predicate isRelevantType(Type t) { any() }
65+
66+
/**
67+
* Gets the underlying type of the content `c`.
68+
*/
69+
Type getUnderlyingContentType(DataFlow::ContentSet c) { result = any(Type t) and exists(c) }
70+
71+
string qualifierString() { result = "Argument[self]" }
72+
73+
string parameterAccess(R::ParamBase p) {
74+
result = "Argument[" + any(ParameterPosition pos | p = pos.getParameterIn(_)).toString() + "]"
75+
}
76+
77+
string parameterContentAccess(R::ParamBase p) { result = parameterAccess(p) }
78+
79+
class InstanceParameterNode extends DataFlow::ParameterNode {
80+
InstanceParameterNode() { this.asParameter() instanceof SelfParam }
81+
}
82+
83+
bindingset[c]
84+
string paramReturnNodeAsOutput(R::Callable c, ParameterPosition pos) {
85+
// TODO: Implement this to support returning through parameters.
86+
result = "paramReturnNodeAsOutput(" + c + ", " + pos + ")"
87+
}
88+
89+
bindingset[c]
90+
string paramReturnNodeAsContentOutput(Callable c, ParameterPosition pos) {
91+
// TODO: Implement this to support returning through parameters.
92+
result = "paramReturnNodeAsContentOutput(" + c + ", " + pos + ")"
93+
}
94+
95+
Callable returnNodeEnclosingCallable(DataFlow::Node ret) {
96+
result = ret.(Node::Node).getEnclosingCallable().asCfgScope()
97+
}
98+
99+
predicate isOwnInstanceAccessNode(RustDataFlow::ReturnNode node) {
100+
// This is probably not relevant to implement for Rust, as we only use
101+
// `captureMixedFlow` which doesn't explicitly distinguish between
102+
// functions that return `self` and those that don't.
103+
none()
104+
}
105+
106+
predicate sinkModelSanitizer(DataFlow::Node node) { none() }
107+
108+
predicate apiSource(DataFlow::Node source) { none() }
109+
110+
bindingset[sourceEnclosing, api]
111+
predicate irrelevantSourceSinkApi(Callable sourceEnclosing, SourceTargetApi api) { none() }
112+
113+
string getInputArgument(DataFlow::Node source) {
114+
// TODO: Implement when we want to generate sources and sinks
115+
result = "getInputArgument(" + source + ")"
116+
}
117+
118+
bindingset[kind]
119+
predicate isRelevantSinkKind(string kind) { any() }
120+
121+
bindingset[kind]
122+
predicate isRelevantSourceKind(string kind) { any() }
123+
124+
predicate containerContent(DataFlow::ContentSet c) {
125+
c.(SingletonContentSet).getContent() instanceof ElementContent
126+
}
127+
128+
predicate isAdditionalContentFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { none() }
129+
130+
predicate isField(DataFlow::ContentSet c) {
131+
c.(SingletonContentSet).getContent() instanceof StructFieldContent
132+
}
133+
134+
predicate isCallback(DataFlow::ContentSet c) { none() }
135+
136+
string getSyntheticName(DataFlow::ContentSet c) { none() }
137+
138+
string printContent(DataFlow::ContentSet cs) {
139+
exists(string arg | result = FlowSummary::Input::encodeContent(cs, arg) + "[" + arg + "]")
140+
}
141+
142+
string partialModelRow(Callable api, int i) {
143+
i = 0 and
144+
(
145+
result = api.(Function).getCrateOrigin()
146+
or
147+
not api.(Function).hasCrateOrigin() and result = ""
148+
) // crate
149+
or
150+
i = 1 and result = api.(Function).getExtendedCanonicalPath() // name
151+
}
152+
153+
string partialNeutralModelRow(Callable api, int i) { result = partialModelRow(api, i) }
154+
155+
// TODO: Implement this when we want to generate sources.
156+
predicate sourceNode(DataFlow::Node node, string kind) { none() }
157+
158+
// TODO: Implement this when we want to generate sinks.
159+
predicate sinkNode(DataFlow::Node node, string kind) { none() }
160+
}
161+
162+
import MakeModelGenerator<Location, RustDataFlow, RustTaintTracking, ModelGeneratorInput>

0 commit comments

Comments
 (0)