Skip to content

Commit a5e13ee

Browse files
committed
Rust: Handle async blocks in SSA analysis
1 parent 74a6d98 commit a5e13ee

File tree

9 files changed

+40
-19
lines changed

9 files changed

+40
-19
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class BasicBlock = BasicBlockImpl;
1212
* without branches or joins.
1313
*/
1414
private class BasicBlockImpl extends TBasicBlockStart {
15-
/** Gets the scope of this basic block. */
15+
/** Gets the CFG scope of this basic block. */
1616
CfgScope getScope() { result = this.getAPredecessor().getScope() }
1717

1818
/** Gets an immediate successor of this basic block, if any. */

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ private import BasicBlocks
99

1010
final class CfgScope = Scope::CfgScope;
1111

12+
predicate getEnclosingCfgScope = Scope::getEnclosingCfgScope/1;
13+
1214
final class SuccessorType = SuccessorTypeImpl;
1315

1416
final class NormalSuccessor = NormalSuccessorImpl;
@@ -52,3 +54,6 @@ final class CfgNode extends Node {
5254
/** Gets the basic block that this control flow node belongs to. */
5355
BasicBlock getBasicBlock() { result.getANode() = this }
5456
}
57+
58+
/** Holds if evaluating `e` jumps to the evaluation of a different CFG scope. */
59+
predicate isControlFlowJump(Expr e) { e instanceof CallExprBase or e instanceof AwaitExpr }

rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ private module CfgInput implements InputSig<Location> {
2323
class CfgScope = Scope::CfgScope;
2424

2525
CfgScope getCfgScope(AstNode n) {
26-
result = n.getEnclosingCallable() and
26+
result = Scope::getEnclosingCfgScope(n) and
2727
Stages::CfgStage::ref()
2828
}
2929

rust/ql/lib/codeql/rust/controlflow/internal/Scope.qll

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ abstract private class CfgScopeImpl extends AstNode {
1414

1515
final class CfgScope = CfgScopeImpl;
1616

17-
class AsyncBlockScope extends CfgScopeImpl, BlockExpr {
18-
AsyncBlockScope() { this.isAsync() }
19-
17+
final class AsyncBlockScope extends CfgScopeImpl, AsyncBlockExpr {
2018
override predicate scopeFirst(AstNode first) {
2119
first(this.(ExprTrees::AsyncBlockExprTree).getFirstChildNode(), first)
2220
}
@@ -29,7 +27,7 @@ class AsyncBlockScope extends CfgScopeImpl, BlockExpr {
2927
/**
3028
* A CFG scope for a callable (a function or a closure) with a body.
3129
*/
32-
class CallableScope extends CfgScopeImpl, Callable {
30+
final class CallableScope extends CfgScopeImpl, Callable {
3331
CallableScope() {
3432
// A function without a body corresponds to a trait method signature and
3533
// should not have a CFG scope.
@@ -52,3 +50,13 @@ class CallableScope extends CfgScopeImpl, Callable {
5250
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
5351
override predicate scopeLast(AstNode last, Completion c) { last(this.getBody(), last, c) }
5452
}
53+
54+
/** Gets the CFG scope that encloses `node`, if any. */
55+
CfgScope getEnclosingCfgScope(AstNode node) {
56+
exists(AstNode p | p = node.getParentNode() |
57+
result = p
58+
or
59+
not p instanceof CfgScope and
60+
result = getEnclosingCfgScope(p)
61+
)
62+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ module Node {
134134

135135
ExprNode() { this = TExprNode(n) }
136136

137-
override CfgScope getCfgScope() { result = this.asExpr().getEnclosingCallable() }
137+
override CfgScope getCfgScope() { result = getEnclosingCfgScope(this.asExpr()) }
138138

139139
override Location getLocation() { result = n.getExpr().getLocation() }
140140

@@ -154,7 +154,7 @@ module Node {
154154

155155
ParameterNode() { this = TParameterNode(parameter) }
156156

157-
override CfgScope getCfgScope() { result = parameter.getEnclosingCallable() }
157+
override CfgScope getCfgScope() { result = getEnclosingCfgScope(parameter) }
158158

159159
override Location getLocation() { result = parameter.getLocation() }
160160

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,19 +156,19 @@ private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
156156
*/
157157
pragma[noinline]
158158
private predicate hasCapturedWrite(Variable v, Cfg::CfgScope scope) {
159-
any(VariableWriteAccess write | write.getVariable() = v and scope = write.getEnclosingCallable+())
159+
any(VariableWriteAccess write | write.getVariable() = v and scope = getEnclosingCfgScope+(write))
160160
.isCapture()
161161
}
162162

163163
/**
164164
* Holds if `v` is read inside basic block `bb` at index `i`, which is in the
165-
* immediate outer scope of `scope`.
165+
* immediate outer CFG scope of `scope`.
166166
*/
167167
pragma[noinline]
168168
private predicate variableReadActualInOuterScope(
169169
BasicBlock bb, int i, Variable v, Cfg::CfgScope scope
170170
) {
171-
variableReadActual(bb, i, v) and bb.getScope() = scope.getEnclosingCallable()
171+
variableReadActual(bb, i, v) and bb.getScope() = getEnclosingCfgScope(scope)
172172
}
173173

174174
pragma[noinline]
@@ -263,7 +263,7 @@ private predicate readsCapturedVariable(BasicBlock bb, Variable v) {
263263
*/
264264
pragma[noinline]
265265
private predicate hasCapturedRead(Variable v, Cfg::CfgScope scope) {
266-
any(VariableReadAccess read | read.getVariable() = v and scope = read.getEnclosingCallable+())
266+
any(VariableReadAccess read | read.getVariable() = v and scope = getEnclosingCfgScope+(read))
267267
.isCapture()
268268
}
269269

@@ -273,14 +273,15 @@ private predicate hasCapturedRead(Variable v, Cfg::CfgScope scope) {
273273
*/
274274
pragma[noinline]
275275
private predicate variableWriteInOuterScope(BasicBlock bb, int i, Variable v, Cfg::CfgScope scope) {
276-
SsaInput::variableWrite(bb, i, v, _) and scope.getEnclosingCallable() = bb.getScope()
276+
SsaInput::variableWrite(bb, i, v, _) and getEnclosingCfgScope(scope) = bb.getScope()
277277
}
278278

279279
/**
280280
* Holds if the call `call` at index `i` in basic block `bb` may reach
281281
* a callable that reads captured variable `v`.
282282
*/
283-
private predicate capturedCallRead(CallExprBase call, BasicBlock bb, int i, Variable v) {
283+
private predicate capturedCallRead(Expr call, BasicBlock bb, int i, Variable v) {
284+
Cfg::isControlFlowJump(call) and
284285
exists(Cfg::CfgScope scope |
285286
hasCapturedRead(v, scope) and
286287
(
@@ -295,7 +296,8 @@ private predicate capturedCallRead(CallExprBase call, BasicBlock bb, int i, Vari
295296
* Holds if the call `call` at index `i` in basic block `bb` may reach a callable
296297
* that writes captured variable `v`.
297298
*/
298-
predicate capturedCallWrite(CallExprBase call, BasicBlock bb, int i, Variable v) {
299+
predicate capturedCallWrite(Expr call, BasicBlock bb, int i, Variable v) {
300+
Cfg::isControlFlowJump(call) and
299301
call = bb.getNode(i).getAstNode() and
300302
exists(Cfg::CfgScope scope |
301303
hasVariableReadWithCapturedWrite(bb, any(int j | j > i), v, scope)

rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
private import rust
2+
private import codeql.rust.controlflow.ControlFlowGraph
23
private import codeql.rust.elements.internal.generated.ParentChild
34
private import codeql.rust.elements.internal.PathExprBaseImpl::Impl as PathExprBaseImpl
45
private import codeql.rust.elements.internal.FormatTemplateVariableAccessImpl::Impl as FormatTemplateVariableAccessImpl
@@ -445,7 +446,7 @@ module Impl {
445446
Variable getVariable() { result = v }
446447

447448
/** Holds if this access is a capture. */
448-
predicate isCapture() { this.getEnclosingCallable() != v.getPat().getEnclosingCallable() }
449+
predicate isCapture() { getEnclosingCfgScope(this) != getEnclosingCfgScope(v.getPat()) }
449450

450451
override string toString() { result = name }
451452

rust/ql/test/library-tests/variables/Ssa.expected

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ definition
124124
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 |
125125
| variables.rs:436:9:436:13 | i | variables.rs:436:13:436:13 | i |
126126
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block |
127+
| variables.rs:438:9:438:9 | i | variables.rs:436:13:436:13 | i |
128+
| variables.rs:441:5:441:15 | AwaitExpr | variables.rs:436:13:436:13 | i |
127129
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b |
128130
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x |
129131
| variables.rs:449:5:457:5 | phi | variables.rs:446:13:446:13 | x |
@@ -239,8 +241,8 @@ read
239241
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
240242
| variables.rs:423:5:423:14 | CallExpr | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
241243
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
242-
| variables.rs:436:9:436:13 | i | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
243244
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block | variables.rs:441:5:441:9 | block |
245+
| variables.rs:441:5:441:15 | AwaitExpr | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
244246
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b | variables.rs:449:8:449:8 | b |
245247
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:447:15:447:15 | x |
246248
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:448:15:448:15 | x |
@@ -343,8 +345,8 @@ firstRead
343345
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
344346
| variables.rs:423:5:423:14 | CallExpr | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
345347
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
346-
| variables.rs:436:9:436:13 | i | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
347348
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block | variables.rs:441:5:441:9 | block |
349+
| variables.rs:441:5:441:15 | AwaitExpr | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
348350
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b | variables.rs:449:8:449:8 | b |
349351
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:447:15:447:15 | x |
350352
| variables.rs:449:5:457:5 | phi | variables.rs:446:13:446:13 | x | variables.rs:458:15:458:15 | x |
@@ -443,8 +445,8 @@ lastRead
443445
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
444446
| variables.rs:423:5:423:14 | CallExpr | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
445447
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
446-
| variables.rs:436:9:436:13 | i | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
447448
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block | variables.rs:441:5:441:9 | block |
449+
| variables.rs:441:5:441:15 | AwaitExpr | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
448450
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b | variables.rs:449:8:449:8 | b |
449451
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:448:15:448:15 | x |
450452
| variables.rs:449:5:457:5 | phi | variables.rs:446:13:446:13 | x | variables.rs:458:15:458:15 | x |
@@ -554,5 +556,6 @@ assigns
554556
| variables.rs:23:5:23:6 | x2 | variables.rs:23:10:23:10 | 5 |
555557
| variables.rs:30:5:30:5 | x | variables.rs:30:9:30:9 | 2 |
556558
| variables.rs:421:9:421:9 | y | variables.rs:421:13:421:13 | 3 |
559+
| variables.rs:438:9:438:9 | i | variables.rs:438:13:438:13 | 1 |
557560
| variables.rs:450:9:450:9 | x | variables.rs:450:13:450:13 | 2 |
558561
| variables.rs:454:9:454:9 | x | variables.rs:454:13:454:13 | 3 |

rust/ql/test/library-tests/variables/variables.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,10 @@ capturedVariable
461461
| variables.rs:410:13:410:13 | x |
462462
| variables.rs:418:13:418:13 | y |
463463
| variables.rs:426:13:426:13 | z |
464+
| variables.rs:436:13:436:13 | i |
464465
capturedAccess
465466
| variables.rs:403:19:403:19 | x |
466467
| variables.rs:413:19:413:19 | x |
467468
| variables.rs:421:9:421:9 | y |
468469
| variables.rs:429:9:429:9 | z |
470+
| variables.rs:438:9:438:9 | i |

0 commit comments

Comments
 (0)