Skip to content

Commit b96698a

Browse files
authored
Merge pull request github#17871 from paldepind/rust-data-flow-skeleton
Rust: Add basic skeleton setup for data flow
2 parents c936468 + efa59fd commit b96698a

File tree

10 files changed

+441
-0
lines changed

10 files changed

+441
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Provides a module for performing local (intra-procedural) and global
3+
* (inter-procedural) data flow analyses.
4+
*/
5+
6+
private import rust
7+
private import codeql.dataflow.DataFlow
8+
private import internal.DataFlowImpl as DataFlowImpl
9+
private import DataFlowImpl::Node as Node
10+
11+
/**
12+
* Provides classes for performing local (intra-procedural) and global
13+
* (inter-procedural) data flow analyses.
14+
*/
15+
module DataFlow {
16+
final class Node = Node::Node;
17+
18+
final class ParameterNode = Node::ParameterNode;
19+
20+
final class PostUpdateNode = Node::PostUpdateNode;
21+
22+
/**
23+
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
24+
* (intra-procedural) step.
25+
*/
26+
predicate localFlowStep = DataFlowImpl::localFlowStepImpl/2;
27+
28+
/**
29+
* Holds if data flows from `source` to `sink` in zero or more local
30+
* (intra-procedural) steps.
31+
*/
32+
pragma[inline]
33+
predicate localFlow(Node::Node source, Node::Node sink) { localFlowStep*(source, sink) }
34+
35+
import DataFlowMake<Location, DataFlowImpl::RustDataFlow>
36+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Provides the module `TaintTracking` for performing local (intra-procedural)
3+
* and global (inter-procedural) taint-tracking analyses.
4+
*/
5+
6+
private import rust
7+
8+
/**
9+
* Provides a library for performing local (intra-procedural) and global
10+
* (inter-procedural) taint-tracking analyses.
11+
*/
12+
module TaintTracking {
13+
private import codeql.dataflow.TaintTracking
14+
private import internal.DataFlowImpl
15+
private import internal.TaintTrackingImpl
16+
import TaintFlowMake<Location, RustDataFlow, RustTaintTracking>
17+
}
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/**
2+
* Provides Rust-specific definitions for use in the data flow library.
3+
*/
4+
5+
private import codeql.util.Void
6+
private import codeql.util.Unit
7+
private import codeql.dataflow.DataFlow
8+
private import codeql.dataflow.internal.DataFlowImpl
9+
private import rust
10+
private import codeql.rust.controlflow.ControlFlowGraph
11+
private import codeql.rust.dataflow.Ssa
12+
13+
module Node {
14+
/**
15+
* An element, viewed as a node in a data flow graph. Either an expression
16+
* (`ExprNode`) or a parameter (`ParameterNode`).
17+
*/
18+
abstract class Node extends TNode {
19+
/** Gets the location of this node. */
20+
abstract Location getLocation();
21+
22+
/** Gets a textual representation of this node. */
23+
abstract string toString();
24+
25+
/**
26+
* Gets the expression that corresponds to this node, if any.
27+
*/
28+
Expr asExpr() { none() }
29+
30+
/**
31+
* Gets the control flow node that corresponds to this data flow node.
32+
*/
33+
CfgNode getCfgNode() { none() }
34+
35+
/**
36+
* Gets this node's underlying SSA definition, if any.
37+
*/
38+
Ssa::Definition asDefinition() { none() }
39+
40+
/**
41+
* Gets the parameter that corresponds to this node, if any.
42+
*/
43+
Param asParameter() { none() }
44+
}
45+
46+
/** A node type that is not implemented. */
47+
final class NaNode extends Node {
48+
NaNode() { none() }
49+
50+
override string toString() { result = "N/A" }
51+
52+
override Location getLocation() { none() }
53+
}
54+
55+
/**
56+
* The value of a parameter at function entry, viewed as a node in a data
57+
* flow graph.
58+
*/
59+
final class ParameterNode extends Node {
60+
Param param;
61+
62+
ParameterNode() { this = TSourceParameterNode(param) }
63+
64+
override Location getLocation() { result = param.getLocation() }
65+
66+
override string toString() { result = param.toString() }
67+
}
68+
69+
final class ArgumentNode = NaNode;
70+
71+
final class ReturnNode extends NaNode {
72+
RustDataFlow::ReturnKind getKind() { none() }
73+
}
74+
75+
final class OutNode = NaNode;
76+
77+
/**
78+
* A node associated with an object after an operation that might have
79+
* changed its state.
80+
*
81+
* This can be either the argument to a callable after the callable returns
82+
* (which might have mutated the argument), or the qualifier of a field after
83+
* an update to the field.
84+
*
85+
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
86+
* to the value before the update.
87+
*/
88+
final class PostUpdateNode extends Node::NaNode {
89+
/** Gets the node before the state update. */
90+
Node getPreUpdateNode() { none() }
91+
}
92+
93+
final class CastNode = NaNode;
94+
}
95+
96+
module RustDataFlow implements InputSig<Location> {
97+
/**
98+
* An element, viewed as a node in a data flow graph. Either an expression
99+
* (`ExprNode`) or a parameter (`ParameterNode`).
100+
*/
101+
final class Node = Node::Node;
102+
103+
final class ParameterNode = Node::ParameterNode;
104+
105+
final class ArgumentNode = Node::ArgumentNode;
106+
107+
final class ReturnNode = Node::ReturnNode;
108+
109+
final class OutNode = Node::OutNode;
110+
111+
final class PostUpdateNode = Node::PostUpdateNode;
112+
113+
final class CastNode = Node::NaNode;
114+
115+
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { none() }
116+
117+
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) { none() }
118+
119+
DataFlowCallable nodeGetEnclosingCallable(Node node) { none() }
120+
121+
DataFlowType getNodeType(Node node) { none() }
122+
123+
predicate nodeIsHidden(Node node) { none() }
124+
125+
class DataFlowExpr = Void;
126+
127+
/** Gets the node corresponding to `e`. */
128+
Node exprNode(DataFlowExpr e) { none() }
129+
130+
final class DataFlowCall extends TNormalCall {
131+
private CallExpr c;
132+
133+
DataFlowCall() { this = TNormalCall(c) }
134+
135+
DataFlowCallable getEnclosingCallable() { none() }
136+
137+
string toString() { result = c.toString() }
138+
139+
Location getLocation() { result = c.getLocation() }
140+
}
141+
142+
final class DataFlowCallable = CfgScope;
143+
144+
final class ReturnKind = Void;
145+
146+
/** Gets a viable implementation of the target of the given `Call`. */
147+
DataFlowCallable viableCallable(DataFlowCall c) { none() }
148+
149+
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { none() }
150+
151+
final class DataFlowType = Unit;
152+
153+
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() }
154+
155+
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
156+
157+
final class Content = Void;
158+
159+
predicate forceHighPrecision(Content c) { none() }
160+
161+
class ContentSet extends TContentSet {
162+
/** Gets a textual representation of this element. */
163+
string toString() { result = "ContentSet" }
164+
165+
/** Gets a content that may be stored into when storing into this set. */
166+
Content getAStoreContent() { none() }
167+
168+
/** Gets a content that may be read from when reading from this set. */
169+
Content getAReadContent() { none() }
170+
}
171+
172+
final class ContentApprox = Void;
173+
174+
ContentApprox getContentApprox(Content c) { any() }
175+
176+
class ParameterPosition extends string {
177+
ParameterPosition() { this = "pos" }
178+
}
179+
180+
class ArgumentPosition extends string {
181+
ArgumentPosition() { this = "pos" }
182+
}
183+
184+
/**
185+
* Holds if the parameter position `ppos` matches the argument position
186+
* `apos`.
187+
*/
188+
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { none() }
189+
190+
/**
191+
* Holds if there is a simple local flow step from `node1` to `node2`. These
192+
* are the value-preserving intra-callable flow steps.
193+
*/
194+
predicate simpleLocalFlowStep(Node node1, Node node2, string model) { none() }
195+
196+
/**
197+
* Holds if data can flow from `node1` to `node2` through a non-local step
198+
* that does not follow a call edge. For example, a step through a global
199+
* variable.
200+
*/
201+
predicate jumpStep(Node node1, Node node2) { none() }
202+
203+
/**
204+
* Holds if data can flow from `node1` to `node2` via a read of `c`. Thus,
205+
* `node1` references an object with a content `c.getAReadContent()` whose
206+
* value ends up in `node2`.
207+
*/
208+
predicate readStep(Node node1, ContentSet c, Node node2) { none() }
209+
210+
/**
211+
* Holds if data can flow from `node1` to `node2` via a store into `c`. Thus,
212+
* `node2` references an object with a content `c.getAStoreContent()` that
213+
* contains the value of `node1`.
214+
*/
215+
predicate storeStep(Node node1, ContentSet c, Node node2) { none() }
216+
217+
/**
218+
* Holds if values stored inside content `c` are cleared at node `n`. For example,
219+
* any value stored inside `f` is cleared at the pre-update node associated with `x`
220+
* in `x.f = newValue`.
221+
*/
222+
predicate clearsContent(Node n, ContentSet c) { none() }
223+
224+
/**
225+
* Holds if the value that is being tracked is expected to be stored inside content `c`
226+
* at node `n`.
227+
*/
228+
predicate expectsContent(Node n, ContentSet c) { none() }
229+
230+
class NodeRegion instanceof Void {
231+
string toString() { result = "NodeRegion" }
232+
233+
predicate contains(Node n) { none() }
234+
}
235+
236+
/**
237+
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
238+
*/
239+
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() }
240+
241+
/**
242+
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
243+
* side-effect, resulting in a summary from `p` to itself.
244+
*
245+
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
246+
* by default as a heuristic.
247+
*/
248+
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
249+
250+
/**
251+
* Holds if the value of `node2` is given by `node1`.
252+
*
253+
* This predicate is combined with type information in the following way: If
254+
* the data flow library is able to compute an improved type for `node1` then
255+
* it will also conclude that this type applies to `node2`. Vice versa, if
256+
* `node2` must be visited along a flow path, then any type known for `node2`
257+
* must also apply to `node1`.
258+
*/
259+
predicate localMustFlowStep(Node node1, Node node2) { none() }
260+
261+
class LambdaCallKind = Void;
262+
263+
// class LambdaCallKind;
264+
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
265+
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
266+
267+
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
268+
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
269+
270+
/** Extra data-flow steps needed for lambda flow analysis. */
271+
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
272+
273+
predicate knownSourceModel(Node source, string model) { none() }
274+
275+
predicate knownSinkModel(Node sink, string model) { none() }
276+
277+
class DataFlowSecondLevelScope = Void;
278+
}
279+
280+
final class ContentSet = RustDataFlow::ContentSet;
281+
282+
import MakeImpl<Location, RustDataFlow>
283+
284+
/** A collection of cached types and predicates to be evaluated in the same stage. */
285+
cached
286+
private module Cached {
287+
cached
288+
newtype TNode =
289+
TExprNode(CfgNode n, Expr e) { n.getAstNode() = e } or
290+
TSourceParameterNode(Param param)
291+
292+
cached
293+
newtype TDataFlowCall = TNormalCall(CallExpr c)
294+
295+
cached
296+
newtype TOptionalContentSet =
297+
TAnyElementContent() or
298+
TAnyContent()
299+
300+
cached
301+
class TContentSet = TAnyElementContent or TAnyContent;
302+
303+
/** This is the local flow predicate that is exposed. */
304+
cached
305+
predicate localFlowStepImpl(Node::Node nodeFrom, Node::Node nodeTo) { none() }
306+
}
307+
308+
import Cached
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
private import rust
2+
private import codeql.dataflow.TaintTracking
3+
private import DataFlowImpl
4+
5+
module RustTaintTracking implements InputSig<Location, RustDataFlow> {
6+
predicate defaultTaintSanitizer(Node::Node node) { none() }
7+
8+
/**
9+
* Holds if the additional step from `src` to `sink` should be included in all
10+
* global taint flow configurations.
11+
*/
12+
predicate defaultAdditionalTaintStep(Node::Node src, Node::Node sink, string model) { none() }
13+
14+
/**
15+
* Holds if taint flow configurations should allow implicit reads of `c` at sinks
16+
* and inputs to additional taint steps.
17+
*/
18+
bindingset[node]
19+
predicate defaultImplicitTaintRead(Node::Node node, ContentSet c) { none() }
20+
}

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

Whitespace-only changes.

0 commit comments

Comments
 (0)