Skip to content

Commit faabc99

Browse files
authored
Merge pull request github#18056 from paldepind/rust-df-global
Rust: Extend data flow library instantiation for global data flow
2 parents cdfb085 + e81c348 commit faabc99

File tree

10 files changed

+259
-69
lines changed

10 files changed

+259
-69
lines changed

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

+2
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

+116-46
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 PositionalParameterNode extends ParameterNode, TParameterNode {
157191
override ParamCfgNode n;
158192

159-
ParameterNode() { this = TParameterNode(n) }
193+
PositionalParameterNode() { 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,7 @@ 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() { this.getCfgNode().getASuccessor() instanceof AnnotatedExitCfgNode }
189234

190235
ReturnKind getKind() { any() }
191236
}
@@ -197,10 +242,10 @@ module Node {
197242
}
198243

199244
final private class ExprOutNode extends ExprNode, OutNode {
200-
ExprOutNode() { this.asExpr() instanceof CallExprCfgNode }
245+
ExprOutNode() { this.asExpr() instanceof CallExprBaseCfgNode }
201246

202247
/** Gets the underlying call CFG node that includes this out node. */
203-
override DataFlowCall getCall() { result.asExprCfgNode() = this.getCfgNode() }
248+
override DataFlowCall getCall() { result.asCallBaseExprCfgNode() = this.getCfgNode() }
204249
}
205250

206251
/**
@@ -214,9 +259,19 @@ module Node {
214259
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
215260
* to the value before the update.
216261
*/
217-
final class PostUpdateNode extends Node::NaNode {
262+
final class PostUpdateNode extends Node, TArgumentPostUpdateNode {
263+
private ExprCfgNode n;
264+
265+
PostUpdateNode() { this = TArgumentPostUpdateNode(n) }
266+
218267
/** Gets the node before the state update. */
219-
Node getPreUpdateNode() { none() }
268+
Node getPreUpdateNode() { result = TExprNode(n) }
269+
270+
final override CfgScope getCfgScope() { result = n.getScope() }
271+
272+
final override Location getLocation() { result = n.getLocation() }
273+
274+
final override string toString() { result = n.toString() }
220275
}
221276

222277
final class CastNode = NaNode;
@@ -226,25 +281,27 @@ final class Node = Node::Node;
226281

227282
/** Provides logic related to SSA. */
228283
module SsaFlow {
229-
private module Impl = SsaImpl::DataFlowIntegration;
284+
private module SsaFlow = SsaImpl::DataFlowIntegration;
230285

231-
private Node::ParameterNode toParameterNode(ParamCfgNode p) { result.getParameter() = p }
286+
private Node::ParameterNode toParameterNode(ParamCfgNode p) {
287+
result.(Node::PositionalParameterNode).getParameter() = p
288+
}
232289

233290
/** Converts a control flow node into an SSA control flow node. */
234-
Impl::Node asNode(Node n) {
291+
SsaFlow::Node asNode(Node n) {
235292
n = TSsaNode(result)
236293
or
237-
result.(Impl::ExprNode).getExpr() = n.asExpr()
294+
result.(SsaFlow::ExprNode).getExpr() = n.asExpr()
238295
or
239-
n = toParameterNode(result.(Impl::ParameterNode).getParameter())
296+
n = toParameterNode(result.(SsaFlow::ParameterNode).getParameter())
240297
}
241298

242299
predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep) {
243-
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
300+
SsaFlow::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
244301
}
245302

246303
predicate localMustFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
247-
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
304+
SsaFlow::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
248305
}
249306
}
250307

@@ -276,6 +333,9 @@ module LocalFlow {
276333
nodeFrom.(Node::AstCfgFlowNode).getCfgNode() =
277334
nodeTo.(Node::SsaNode).getDefinitionExt().(Ssa::WriteDefinition).getControlFlowNode()
278335
or
336+
nodeFrom.(Node::PositionalParameterNode).getParameter().getPat() =
337+
nodeTo.(Node::PatNode).getPat()
338+
or
279339
SsaFlow::localFlowStep(_, nodeFrom, nodeTo, _)
280340
or
281341
exists(AssignmentExprCfgNode a |
@@ -291,6 +351,8 @@ private class ReturnKindAlias = ReturnKind;
291351

292352
private class DataFlowCallAlias = DataFlowCall;
293353

354+
private class ParameterPositionAlias = ParameterPosition;
355+
294356
module RustDataFlow implements InputSig<Location> {
295357
/**
296358
* An element, viewed as a node in a data flow graph. Either an expression
@@ -310,9 +372,15 @@ module RustDataFlow implements InputSig<Location> {
310372

311373
final class CastNode = Node::NaNode;
312374

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

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

317385
DataFlowCallable nodeGetEnclosingCallable(Node node) { result = node.getEnclosingCallable() }
318386

@@ -335,10 +403,9 @@ module RustDataFlow implements InputSig<Location> {
335403
DataFlowCallable viableCallable(DataFlowCall c) {
336404
exists(Function f, string name | result.asCfgScope() = f and name = f.getName().toString() |
337405
if f.getParamList().hasSelfParam()
338-
then name = c.asMethodCallExprCfgNode().getMethodCallExpr().getNameRef().getText()
406+
then name = c.asMethodCallExprCfgNode().getNameRef().getText()
339407
else
340-
name =
341-
c.asCallExprCfgNode().getCallExpr().getExpr().(PathExpr).getPath().getPart().toString()
408+
name = c.asCallExprCfgNode().getExpr().getExpr().(PathExpr).getPath().getPart().toString()
342409
)
343410
}
344411

@@ -377,19 +444,15 @@ module RustDataFlow implements InputSig<Location> {
377444

378445
ContentApprox getContentApprox(Content c) { any() }
379446

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

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

388451
/**
389452
* Holds if the parameter position `ppos` matches the argument position
390453
* `apos`.
391454
*/
392-
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { none() }
455+
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
393456

394457
/**
395458
* Holds if there is a simple local flow step from `node1` to `node2`. These
@@ -497,13 +560,13 @@ private module Cached {
497560
newtype TNode =
498561
TExprNode(ExprCfgNode n) or
499562
TParameterNode(ParamCfgNode p) or
563+
TSelfParameterNode(SelfParamCfgNode p) or
500564
TPatNode(PatCfgNode p) or
565+
TArgumentPostUpdateNode(ExprCfgNode e) { isArgumentForCall(e, _, _) } or
501566
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node)
502567

503568
cached
504-
newtype TDataFlowCall =
505-
TNormalCall(CallExprCfgNode c) or
506-
TMethodCall(MethodCallExprCfgNode c)
569+
newtype TDataFlowCall = TCall(CallExprBaseCfgNode c)
507570

508571
cached
509572
newtype TOptionalContentSet =
@@ -521,6 +584,13 @@ private module Cached {
521584
predicate localFlowStepImpl(Node::Node nodeFrom, Node::Node nodeTo) {
522585
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
523586
}
587+
588+
cached
589+
newtype TParameterPosition =
590+
TPositionalParameterPosition(int i) {
591+
i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1]
592+
} or
593+
TSelfParameterPosition()
524594
}
525595

526596
import Cached
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 |
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

+2-2
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)