Skip to content

Commit fffeac6

Browse files
committed
Rust: Extend data flow library instantiation for global data flow
1 parent bb70bfc commit fffeac6

File tree

8 files changed

+219
-55
lines changed

8 files changed

+219
-55
lines changed

rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class AstCfgNode = CfgImpl::AstCfgNode;
1313

1414
class ExitCfgNode = CfgImpl::ExitNode;
1515

16+
class AnnotatedExitCfgNode = CfgImpl::AnnotatedExitNode;
17+
1618
/**
1719
* An assignment expression, for example
1820
*

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

Lines changed: 118 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,59 @@ final class DataFlowCallable extends TDataFlowCallable {
4343
}
4444

4545
final class DataFlowCall extends TDataFlowCall {
46+
private CallExprBaseCfgNode call;
47+
48+
DataFlowCall() { this = TCall(call) }
49+
4650
/** Gets the underlying call in the CFG, if any. */
47-
CallExprCfgNode asCallExprCfgNode() { this = TNormalCall(result) }
51+
CallExprCfgNode asCallExprCfgNode() { result = call }
4852

49-
MethodCallExprCfgNode asMethodCallExprCfgNode() { this = TMethodCall(result) }
53+
MethodCallExprCfgNode asMethodCallExprCfgNode() { result = call }
5054

51-
CallExprBaseCfgNode asExprCfgNode() {
52-
result = this.asCallExprCfgNode() or result = this.asMethodCallExprCfgNode()
53-
}
55+
CallExprBaseCfgNode asCallBaseExprCfgNode() { result = call }
5456

5557
DataFlowCallable getEnclosingCallable() {
56-
result = TCfgScope(this.asExprCfgNode().getExpr().getEnclosingCfgScope())
58+
result = TCfgScope(call.getExpr().getEnclosingCfgScope())
59+
}
60+
61+
string toString() { result = this.asCallBaseExprCfgNode().toString() }
62+
63+
Location getLocation() { result = this.asCallBaseExprCfgNode().getLocation() }
64+
}
65+
66+
/**
67+
* The position of a parameter or an argument in a function or call.
68+
*
69+
* As there is a 1-to-1 correspondence between parameter positions and
70+
* arguments positions in Rust we use the same type for both.
71+
*/
72+
final class ParameterPosition extends TParameterPosition {
73+
/** Gets the underlying integer position, if any. */
74+
int getPosition() { this = TPositionalParameterPosition(result) }
75+
76+
/** Holds if this position represents the `self` position. */
77+
predicate isSelf() { this = TSelfParameterPosition() }
78+
79+
/** Gets a textual representation of this position. */
80+
string toString() {
81+
result = this.getPosition().toString()
82+
or
83+
result = "self" and this.isSelf()
5784
}
5885

59-
string toString() { result = this.asExprCfgNode().toString() }
86+
AstNode getParameterIn(ParamList ps) {
87+
result = ps.getParam(this.getPosition())
88+
or
89+
result = ps.getSelfParam() and this.isSelf()
90+
}
91+
}
6092

61-
Location getLocation() { result = this.asExprCfgNode().getLocation() }
93+
/** Holds if `arg` is an argument of `call` at the position `pos`. */
94+
private predicate isArgumentForCall(ExprCfgNode arg, CallExprBaseCfgNode call, ParameterPosition pos) {
95+
arg = call.getArgument(pos.getPosition())
96+
or
97+
// The self argument in a method call.
98+
arg = call.(MethodCallExprCfgNode).getReceiver() and pos.isSelf()
6299
}
63100

64101
module Node {
@@ -93,11 +130,6 @@ module Node {
93130
* Gets this node's underlying SSA definition, if any.
94131
*/
95132
Ssa::Definition asDefinition() { none() }
96-
97-
/**
98-
* Gets the parameter that corresponds to this node, if any.
99-
*/
100-
Param asParameter() { none() }
101133
}
102134

103135
/** A node type that is not implemented. */
@@ -111,7 +143,7 @@ module Node {
111143
override Location getLocation() { none() }
112144
}
113145

114-
/** A data flow node that corresponds to a CFG node for an AST node. */
146+
/** A data flow node that corresponds directly to a CFG node for an AST node. */
115147
abstract class AstCfgFlowNode extends Node {
116148
AstCfgNode n;
117149

@@ -145,24 +177,37 @@ module Node {
145177

146178
PatNode() { this = TPatNode(n) }
147179

148-
/** Gets the `Pat` in the AST that this node corresponds to. */
149-
Pat getPat() { result = n.getPat() }
180+
/** Gets the `PatCfgNode` in the CFG that this node corresponds to. */
181+
PatCfgNode getPat() { result = n }
150182
}
151183

184+
abstract class ParameterNode extends AstCfgFlowNode { }
185+
152186
/**
153187
* The value of a parameter at function entry, viewed as a node in a data
154188
* flow graph.
155189
*/
156-
final class ParameterNode extends AstCfgFlowNode, TParameterNode {
190+
final class NormalParameterNode extends ParameterNode, TParameterNode {
157191
override ParamCfgNode n;
158192

159-
ParameterNode() { this = TParameterNode(n) }
193+
NormalParameterNode() { this = TParameterNode(n) }
160194

161195
/** Gets the parameter in the CFG that this node corresponds to. */
162196
ParamCfgNode getParameter() { result = n }
163197
}
164198

165-
final class ArgumentNode = NaNode;
199+
final class SelfParameterNode extends ParameterNode, TSelfParameterNode {
200+
override SelfParamCfgNode n;
201+
202+
SelfParameterNode() { this = TSelfParameterNode(n) }
203+
204+
/** Gets the self parameter in the AST that this node corresponds to. */
205+
SelfParamCfgNode getSelfParameter() { result = n }
206+
}
207+
208+
final class ArgumentNode extends ExprNode {
209+
ArgumentNode() { isArgumentForCall(n, _, _) }
210+
}
166211

167212
/** An SSA node. */
168213
class SsaNode extends Node, TSsaNode {
@@ -185,7 +230,10 @@ module Node {
185230

186231
/** A data flow node that represents a value returned by a callable. */
187232
final class ReturnNode extends ExprNode {
188-
ReturnNode() { this.getCfgNode().getASuccessor() instanceof ExitCfgNode }
233+
ReturnNode() {
234+
this.getCfgNode().getASuccessor() instanceof ExitCfgNode or
235+
this.getCfgNode().getASuccessor() instanceof AnnotatedExitCfgNode
236+
}
189237

190238
ReturnKind getKind() { any() }
191239
}
@@ -197,10 +245,10 @@ module Node {
197245
}
198246

199247
final private class ExprOutNode extends ExprNode, OutNode {
200-
ExprOutNode() { this.asExpr() instanceof CallExprCfgNode }
248+
ExprOutNode() { this.asExpr() instanceof CallExprBaseCfgNode }
201249

202250
/** Gets the underlying call CFG node that includes this out node. */
203-
override DataFlowCall getCall() { result.asExprCfgNode() = this.getCfgNode() }
251+
override DataFlowCall getCall() { result.asCallBaseExprCfgNode() = this.getCfgNode() }
204252
}
205253

206254
/**
@@ -214,9 +262,19 @@ module Node {
214262
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
215263
* to the value before the update.
216264
*/
217-
final class PostUpdateNode extends Node::NaNode {
265+
final class PostUpdateNode extends Node, TArgumentPostUpdateNode {
266+
private ExprCfgNode n;
267+
268+
PostUpdateNode() { this = TArgumentPostUpdateNode(n) }
269+
218270
/** Gets the node before the state update. */
219-
Node getPreUpdateNode() { none() }
271+
Node getPreUpdateNode() { result = TExprNode(n) }
272+
273+
final override CfgScope getCfgScope() { result = n.getAstNode().getEnclosingCfgScope() }
274+
275+
final override Location getLocation() { result = n.getAstNode().getLocation() }
276+
277+
final override string toString() { result = n.getAstNode().toString() }
220278
}
221279

222280
final class CastNode = NaNode;
@@ -226,25 +284,27 @@ final class Node = Node::Node;
226284

227285
/** Provides logic related to SSA. */
228286
module SsaFlow {
229-
private module Impl = SsaImpl::DataFlowIntegration;
287+
private module SsaFlow = SsaImpl::DataFlowIntegration;
230288

231-
private Node::ParameterNode toParameterNode(ParamCfgNode p) { result.getParameter() = p }
289+
private Node::ParameterNode toParameterNode(ParamCfgNode p) {
290+
result.(Node::NormalParameterNode).getParameter() = p
291+
}
232292

233293
/** Converts a control flow node into an SSA control flow node. */
234-
Impl::Node asNode(Node n) {
294+
SsaFlow::Node asNode(Node n) {
235295
n = TSsaNode(result)
236296
or
237-
result.(Impl::ExprNode).getExpr() = n.asExpr()
297+
result.(SsaFlow::ExprNode).getExpr() = n.asExpr()
238298
or
239-
n = toParameterNode(result.(Impl::ParameterNode).getParameter())
299+
n = toParameterNode(result.(SsaFlow::ParameterNode).getParameter())
240300
}
241301

242302
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
243-
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
303+
SsaFlow::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
244304
}
245305

246306
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
247-
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
307+
SsaFlow::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
248308
}
249309
}
250310

@@ -276,6 +336,8 @@ module LocalFlow {
276336
nodeFrom.(Node::AstCfgFlowNode).getCfgNode() =
277337
nodeTo.(Node::SsaNode).getDefinitionExt().(Ssa::WriteDefinition).getControlFlowNode()
278338
or
339+
nodeFrom.(Node::NormalParameterNode).getParameter().getPat() = nodeTo.(Node::PatNode).getPat()
340+
or
279341
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
280342
or
281343
exists(AssignmentExprCfgNode a |
@@ -291,6 +353,8 @@ private class ReturnKindAlias = ReturnKind;
291353

292354
private class DataFlowCallAlias = DataFlowCall;
293355

356+
private class ParameterPositionAlias = ParameterPosition;
357+
294358
module RustDataFlow implements InputSig<Location> {
295359
/**
296360
* An element, viewed as a node in a data flow graph. Either an expression
@@ -310,9 +374,15 @@ module RustDataFlow implements InputSig<Location> {
310374

311375
final class CastNode = Node::NaNode;
312376

313-
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { none() }
377+
/** Holds if `p` is a parameter of `c` at the position `pos`. */
378+
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
379+
p.getCfgNode().getAstNode() = pos.getParameterIn(c.asCfgScope().(Function).getParamList())
380+
}
314381

315-
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) { none() }
382+
/** Holds if `n` is an argument of `c` at the position `pos`. */
383+
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
384+
isArgumentForCall(n.getCfgNode(), call.asCallBaseExprCfgNode(), pos)
385+
}
316386

317387
DataFlowCallable nodeGetEnclosingCallable(Node node) { result = node.getEnclosingCallable() }
318388

@@ -335,10 +405,9 @@ module RustDataFlow implements InputSig<Location> {
335405
DataFlowCallable viableCallable(DataFlowCall c) {
336406
exists(Function f, string name | result.asCfgScope() = f and name = f.getName().toString() |
337407
if f.getParamList().hasSelfParam()
338-
then name = c.asMethodCallExprCfgNode().getMethodCallExpr().getNameRef().getText()
408+
then name = c.asMethodCallExprCfgNode().getNameRef().getText()
339409
else
340-
name =
341-
c.asCallExprCfgNode().getCallExpr().getExpr().(PathExpr).getPath().getPart().toString()
410+
name = c.asCallExprCfgNode().getExpr().getExpr().(PathExpr).getPath().getPart().toString()
342411
)
343412
}
344413

@@ -377,19 +446,15 @@ module RustDataFlow implements InputSig<Location> {
377446

378447
ContentApprox getContentApprox(Content c) { any() }
379448

380-
class ParameterPosition extends string {
381-
ParameterPosition() { this = "pos" }
382-
}
449+
class ParameterPosition = ParameterPositionAlias;
383450

384-
class ArgumentPosition extends string {
385-
ArgumentPosition() { this = "pos" }
386-
}
451+
class ArgumentPosition = ParameterPosition;
387452

388453
/**
389454
* Holds if the parameter position `ppos` matches the argument position
390455
* `apos`.
391456
*/
392-
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { none() }
457+
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
393458

394459
/**
395460
* Holds if there is a simple local flow step from `node1` to `node2`. These
@@ -497,13 +562,13 @@ private module Cached {
497562
newtype TNode =
498563
TExprNode(ExprCfgNode n) or
499564
TParameterNode(ParamCfgNode p) or
565+
TSelfParameterNode(SelfParamCfgNode p) or
500566
TPatNode(PatCfgNode p) or
567+
TArgumentPostUpdateNode(ExprCfgNode e) { isArgumentForCall(e, _, _) } or
501568
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node)
502569

503570
cached
504-
newtype TDataFlowCall =
505-
TNormalCall(CallExprCfgNode c) or
506-
TMethodCall(MethodCallExprCfgNode c)
571+
newtype TDataFlowCall = TCall(CallExprBaseCfgNode c)
507572

508573
cached
509574
newtype TOptionalContentSet =
@@ -521,6 +586,13 @@ private module Cached {
521586
predicate localFlowStepImpl(Node::Node nodeFrom, Node::Node nodeTo) {
522587
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
523588
}
589+
590+
cached
591+
newtype TParameterPosition =
592+
TPositionalParameterPosition(int i) {
593+
exists(any(ParamList l).getParam(i)) or exists(any(ArgList l).getArg(i))
594+
} or
595+
TSelfParameterPosition()
524596
}
525597

526598
import Cached
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
uniqueNodeLocation
22
| file://:0:0:0:0 | ... .parent(...) | Node should have one location but has 0. |
3+
| file://:0:0:0:0 | ... .parent(...) | Node should have one location but has 0. |
34
| file://:0:0:0:0 | ... .unwrap(...) | Node should have one location but has 0. |
45
| file://:0:0:0:0 | BlockExpr | Node should have one location but has 0. |
56
| file://:0:0:0:0 | Param | Node should have one location but has 0. |
67
| file://:0:0:0:0 | path | Node should have one location but has 0. |
78
| file://:0:0:0:0 | path | Node should have one location but has 0. |
9+
| file://:0:0:0:0 | path | Node should have one location but has 0. |
810
missingLocation
9-
| Nodes without location: 6 |
11+
| Nodes without location: 8 |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
models
22
edges
3+
| main.rs:9:13:9:19 | Param : unit | main.rs:9:30:14:1 | BlockExpr : unit | provenance | |
34
| main.rs:21:13:21:21 | CallExpr : unit | main.rs:22:10:22:10 | s | provenance | |
5+
| main.rs:26:13:26:21 | CallExpr : unit | main.rs:27:22:27:22 | s : unit | provenance | |
6+
| main.rs:27:13:27:23 | CallExpr : unit | main.rs:28:10:28:10 | s | provenance | |
7+
| main.rs:27:22:27:22 | s : unit | main.rs:9:13:9:19 | Param : unit | provenance | |
8+
| main.rs:27:22:27:22 | s : unit | main.rs:27:13:27:23 | CallExpr : unit | provenance | |
49
| main.rs:32:13:32:21 | CallExpr : unit | main.rs:33:10:33:10 | s | provenance | |
510
nodes
11+
| main.rs:9:13:9:19 | Param : unit | semmle.label | Param : unit |
12+
| main.rs:9:30:14:1 | BlockExpr : unit | semmle.label | BlockExpr : unit |
613
| main.rs:17:10:17:18 | CallExpr | semmle.label | CallExpr |
714
| main.rs:21:13:21:21 | CallExpr : unit | semmle.label | CallExpr : unit |
815
| main.rs:22:10:22:10 | s | semmle.label | s |
16+
| main.rs:26:13:26:21 | CallExpr : unit | semmle.label | CallExpr : unit |
17+
| main.rs:27:13:27:23 | CallExpr : unit | semmle.label | CallExpr : unit |
18+
| main.rs:27:22:27:22 | s : unit | semmle.label | s : unit |
19+
| main.rs:28:10:28:10 | s | semmle.label | s |
920
| main.rs:32:13:32:21 | CallExpr : unit | semmle.label | CallExpr : unit |
1021
| main.rs:33:10:33:10 | s | semmle.label | s |
1122
subpaths
23+
| main.rs:27:22:27:22 | s : unit | main.rs:9:13:9:19 | Param : unit | main.rs:9:30:14:1 | BlockExpr : unit | main.rs:27:13:27:23 | CallExpr : unit |
1224
testFailures
1325
#select
1426
| main.rs:17:10:17:18 | CallExpr | main.rs:17:10:17:18 | CallExpr | main.rs:17:10:17:18 | CallExpr | $@ | main.rs:17:10:17:18 | CallExpr | CallExpr |
1527
| main.rs:22:10:22:10 | s | main.rs:21:13:21:21 | CallExpr : unit | main.rs:22:10:22:10 | s | $@ | main.rs:21:13:21:21 | CallExpr : unit | CallExpr : unit |
28+
| main.rs:28:10:28:10 | s | main.rs:26:13:26:21 | CallExpr : unit | main.rs:28:10:28:10 | s | $@ | main.rs:26:13:26:21 | CallExpr : unit | CallExpr : unit |
1629
| main.rs:33:10:33:10 | s | main.rs:32:13:32:21 | CallExpr : unit | main.rs:33:10:33:10 | s | $@ | main.rs:32:13:32:21 | CallExpr : unit | CallExpr : unit |

rust/ql/test/library-tests/dataflow/barrier/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn sink(s: &str) {
99
fn sanitize(s: &str) -> &str {
1010
match s {
1111
"dangerous" => "",
12-
s => s
12+
s => s,
1313
}
1414
}
1515

@@ -25,7 +25,7 @@ fn through_variable() {
2525
fn with_barrier() {
2626
let s = source(1);
2727
let s = sanitize(s);
28-
sink(s);
28+
sink(s); // $ SPURIOUS: hasValueFlow=1
2929
}
3030

3131
fn main() {

0 commit comments

Comments
 (0)