Skip to content

Commit d52a2d6

Browse files
committed
Rust: Create CFG scope for async blocks
1 parent e05b126 commit d52a2d6

File tree

6 files changed

+79
-28
lines changed

6 files changed

+79
-28
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ query predicate nonPostOrderExpr(Expr e, string cls) {
3232
*/
3333
query predicate scopeNoFirst(CfgScope scope) {
3434
Consistency::scopeNoFirst(scope) and
35-
not scope = any(Function f | not exists(f.getBody())) and
36-
not scope = any(ClosureExpr c | not exists(c.getBody()))
35+
not scope =
36+
[
37+
any(AstNode f | not f.(Function).hasBody()),
38+
any(ClosureExpr c | not c.hasBody()),
39+
any(AsyncBlockExpr b | not b.hasStmtList())
40+
]
3741
}
3842

3943
/** Holds if `be` is the `else` branch of a `let` statement that results in a panic. */

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

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,10 @@ private module CfgInput implements InputSig<Location> {
4444
predicate successorTypeIsCondition(SuccessorType t) { t instanceof Cfg::BooleanSuccessor }
4545

4646
/** Holds if `first` is first executed when entering `scope`. */
47-
predicate scopeFirst(CfgScope scope, AstNode first) {
48-
first(scope.(CfgScopeTree).getFirstChildNode(), first)
49-
}
47+
predicate scopeFirst(CfgScope scope, AstNode first) { scope.scopeFirst(first) }
5048

5149
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
52-
predicate scopeLast(CfgScope scope, AstNode last, Completion c) { last(scope.getBody(), last, c) }
50+
predicate scopeLast(CfgScope scope, AstNode last, Completion c) { scope.scopeLast(last, c) }
5351
}
5452

5553
private module CfgSplittingInput implements SplittingInputSig<Location, CfgInput> {
@@ -71,14 +69,7 @@ private module CfgImpl =
7169

7270
import CfgImpl
7371

74-
class CfgScopeTree extends StandardTree, Scope::CfgScope {
75-
override predicate first(AstNode first) { first = this }
76-
77-
override predicate last(AstNode last, Completion c) {
78-
last = this and
79-
completionIsValidFor(c, this)
80-
}
81-
72+
class CallableScopeTree extends StandardTree, PreOrderTree, PostOrderTree, Scope::CallableScope {
8273
override predicate propagatesAbnormal(AstNode child) { none() }
8374

8475
override AstNode getChildNode(int i) {
@@ -280,13 +271,23 @@ module ExprTrees {
280271
}
281272
}
282273

274+
private AstNode getBlockChildNode(BlockExpr b, int i) {
275+
result = b.getStmtList().getStatement(i)
276+
or
277+
i = b.getStmtList().getNumberOfStatements() and
278+
result = b.getStmtList().getTailExpr()
279+
}
280+
281+
class AsyncBlockExprTree extends StandardTree, PreOrderTree, PostOrderTree, AsyncBlockExpr {
282+
override AstNode getChildNode(int i) { result = getBlockChildNode(this, i) }
283+
284+
override predicate propagatesAbnormal(AstNode child) { none() }
285+
}
286+
283287
class BlockExprTree extends StandardPostOrderTree, BlockExpr {
284-
override AstNode getChildNode(int i) {
285-
result = this.getStmtList().getStatement(i)
286-
or
287-
i = this.getStmtList().getNumberOfStatements() and
288-
result = this.getStmtList().getTailExpr()
289-
}
288+
BlockExprTree() { not this.isAsync() }
289+
290+
override AstNode getChildNode(int i) { result = getBlockChildNode(this, i) }
290291

291292
override predicate propagatesAbnormal(AstNode child) { child = this.getChildNode(_) }
292293
}

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

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,32 @@ private import codeql.rust.elements.internal.generated.ParentChild
55

66
/**
77
* A control-flow graph (CFG) scope.
8-
*
9-
* A CFG scope is a callable with a body.
108
*/
11-
class CfgScope extends Callable {
12-
CfgScope() {
9+
abstract private class CfgScopeImpl extends AstNode {
10+
abstract predicate scopeFirst(AstNode first);
11+
12+
abstract predicate scopeLast(AstNode last, Completion c);
13+
}
14+
15+
final class CfgScope = CfgScopeImpl;
16+
17+
class AsyncBlockScope extends CfgScopeImpl, BlockExpr {
18+
AsyncBlockScope() { this.isAsync() }
19+
20+
override predicate scopeFirst(AstNode first) {
21+
first(this.(ExprTrees::AsyncBlockExprTree).getFirstChildNode(), first)
22+
}
23+
24+
override predicate scopeLast(AstNode last, Completion c) {
25+
last(this.(ExprTrees::AsyncBlockExprTree).getLastChildElement(), last, c)
26+
}
27+
}
28+
29+
/**
30+
* A CFG scope for a callable (a function or a closure) with a body.
31+
*/
32+
class CallableScope extends CfgScopeImpl, Callable {
33+
CallableScope() {
1334
// A function without a body corresponds to a trait method signature and
1435
// should not have a CFG scope.
1536
this.(Function).hasBody()
@@ -23,4 +44,11 @@ class CfgScope extends Callable {
2344
or
2445
result = this.(ClosureExpr).getBody()
2546
}
47+
48+
override predicate scopeFirst(AstNode first) {
49+
first(this.(CallableScopeTree).getFirstChildNode(), first)
50+
}
51+
52+
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
53+
override predicate scopeLast(AstNode last, Completion c) { last(this.getBody(), last, c) }
2654
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
private import codeql.rust.elements.BlockExpr
2+
3+
/**
4+
* A async block expression. For example:
5+
* ```rust
6+
* async {
7+
* let x = 42;
8+
* }
9+
* ```
10+
*/
11+
final class AsyncBlockExpr extends BlockExpr {
12+
AsyncBlockExpr() { this.isAsync() }
13+
}

rust/ql/lib/rust.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import codeql.Locations
55
import codeql.files.FileSystem
66
import codeql.rust.elements.AssignmentOperation
77
import codeql.rust.elements.LogicalOperation
8+
import codeql.rust.elements.AsyncBlockExpr
89
import codeql.rust.elements.Variable
910
import codeql.rust.elements.NamedFormatArgument
1011
import codeql.rust.elements.PositionalFormatArgument

rust/ql/test/library-tests/controlflow/Cfg.expected

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -781,10 +781,12 @@ edges
781781
| test.rs:372:5:384:5 | enter async_block | test.rs:373:9:375:10 | LetStmt | |
782782
| test.rs:372:5:384:5 | exit async_block (normal) | test.rs:372:5:384:5 | exit async_block | |
783783
| test.rs:372:28:384:5 | BlockExpr | test.rs:372:5:384:5 | exit async_block (normal) | |
784-
| test.rs:373:9:375:10 | LetStmt | test.rs:374:13:374:42 | ExprStmt | |
784+
| test.rs:373:9:375:10 | LetStmt | test.rs:373:26:375:9 | BlockExpr | |
785785
| test.rs:373:13:373:22 | say_godbye | test.rs:376:9:378:10 | LetStmt | match |
786786
| test.rs:373:26:375:9 | BlockExpr | test.rs:373:13:373:22 | say_godbye | |
787-
| test.rs:374:13:374:41 | MacroExpr | test.rs:373:26:375:9 | BlockExpr | |
787+
| test.rs:373:26:375:9 | enter BlockExpr | test.rs:374:13:374:42 | ExprStmt | |
788+
| test.rs:373:26:375:9 | exit BlockExpr (normal) | test.rs:373:26:375:9 | exit BlockExpr | |
789+
| test.rs:374:13:374:41 | MacroExpr | test.rs:373:26:375:9 | exit BlockExpr (normal) | |
788790
| test.rs:374:13:374:41 | PathExpr | test.rs:374:22:374:40 | "godbye, everyone!\\n" | |
789791
| test.rs:374:13:374:42 | ExprStmt | test.rs:374:22:374:40 | MacroStmts | |
790792
| test.rs:374:22:374:40 | "godbye, everyone!\\n" | test.rs:374:22:374:40 | FormatArgsExpr | |
@@ -794,10 +796,12 @@ edges
794796
| test.rs:374:22:374:40 | FormatArgsExpr | test.rs:374:22:374:40 | MacroExpr | |
795797
| test.rs:374:22:374:40 | MacroExpr | test.rs:374:22:374:40 | CallExpr | |
796798
| test.rs:374:22:374:40 | MacroStmts | test.rs:374:22:374:40 | ExprStmt | |
797-
| test.rs:376:9:378:10 | LetStmt | test.rs:377:13:377:37 | ExprStmt | |
799+
| test.rs:376:9:378:10 | LetStmt | test.rs:376:31:378:9 | BlockExpr | |
798800
| test.rs:376:13:376:27 | say_how_are_you | test.rs:379:9:379:28 | LetStmt | match |
799801
| test.rs:376:31:378:9 | BlockExpr | test.rs:376:13:376:27 | say_how_are_you | |
800-
| test.rs:377:13:377:36 | MacroExpr | test.rs:376:31:378:9 | BlockExpr | |
802+
| test.rs:376:31:378:9 | enter BlockExpr | test.rs:377:13:377:37 | ExprStmt | |
803+
| test.rs:376:31:378:9 | exit BlockExpr (normal) | test.rs:376:31:378:9 | exit BlockExpr | |
804+
| test.rs:377:13:377:36 | MacroExpr | test.rs:376:31:378:9 | exit BlockExpr (normal) | |
801805
| test.rs:377:13:377:36 | PathExpr | test.rs:377:22:377:35 | "how are you?\\n" | |
802806
| test.rs:377:13:377:37 | ExprStmt | test.rs:377:22:377:35 | MacroStmts | |
803807
| test.rs:377:22:377:35 | "how are you?\\n" | test.rs:377:22:377:35 | FormatArgsExpr | |

0 commit comments

Comments
 (0)