Skip to content

Commit 44bebed

Browse files
committed
Rust: Add basic skeleton setup for data flow
1 parent 4c73c62 commit 44bebed

File tree

10 files changed

+446
-0
lines changed

10 files changed

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

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

Whitespace-only changes.

0 commit comments

Comments
 (0)