Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust: Account for captured variables #17696

Merged
merged 4 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 32 additions & 11 deletions rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
private import rust
private import codeql.rust.elements.internal.generated.ParentChild
private import codeql.rust.elements.internal.PathExprImpl::Impl as PathExprImpl
private import codeql.util.DenseRank

module Impl {
/**
Expand Down Expand Up @@ -119,6 +120,9 @@ module Impl {
result = let.getInitializer()
)
}

/** Holds if this variable is captured. */
predicate isCaptured() { this.getAnAccess().isCapture() }
}

/** A path expression that may access a local variable. */
Expand Down Expand Up @@ -251,7 +255,7 @@ module Impl {
// Use the location of the inner scope as the location of the access, instead of the
// actual access location. This allows us to collapse multiple accesses in inner
// scopes to a single entity
scope.getLocation().hasLocationInfo(_, startline, startcolumn, endline, endcolumn)
inner.getLocation().hasLocationInfo(_, startline, startcolumn, endline, endcolumn)
)
}

Expand Down Expand Up @@ -334,18 +338,30 @@ module Impl {
}
}

private module DenseRankInput implements DenseRankInputSig3 {
class C1 = VariableScope;

class C2 = string;

class Ranked = VariableOrAccessCand;

int getRank(VariableScope scope, string name, VariableOrAccessCand v) {
v =
rank[result](VariableOrAccessCand v0, int startline, int startcolumn, int endline,
int endcolumn |
v0.rankBy(name, scope, startline, startcolumn, endline, endcolumn)
|
v0 order by startline, startcolumn, endline, endcolumn
)
}
}

/**
* Gets the rank of `v` amongst all other declarations or access candidates
* to a variable named `name` in the variable scope `scope`.
*/
private int rankVariableOrAccess(VariableScope scope, string name, VariableOrAccessCand v) {
v =
rank[result + 1](VariableOrAccessCand v0, int startline, int startcolumn, int endline,
int endcolumn |
v0.rankBy(name, scope, startline, startcolumn, endline, endcolumn)
|
v0 order by startline, startcolumn, endline, endcolumn
)
result = DenseRank3<DenseRankInput>::denseRank(scope, name, v) - 1
}

/**
Expand Down Expand Up @@ -379,16 +395,21 @@ module Impl {
)
}

private import codeql.rust.controlflow.internal.Scope

/** A variable access. */
class VariableAccess extends PathExprImpl::PathExpr instanceof VariableAccessCand {
private string name;
private Variable v;

VariableAccess() { variableAccess(_, name, v, this) }
VariableAccess() { variableAccess(name, v, this) }

/** Gets the variable being accessed. */
Variable getVariable() { result = v }

/** Holds if this access is a capture. */
predicate isCapture() { scopeOfAst(this) != scopeOfAst(v.getPat()) }

override string toString() { result = name }

override string getAPrimaryQlClass() { result = "VariableAccess" }
Expand Down Expand Up @@ -426,10 +447,10 @@ module Impl {
MkVariable(AstNode definingNode, string name) { variableDecl(definingNode, _, name) }

cached
predicate variableAccess(VariableScope scope, string name, Variable v, VariableAccessCand cand) {
predicate variableAccess(string name, Variable v, VariableAccessCand cand) {
v =
min(Variable v0, int nestLevel |
variableReachesCand(scope, name, v0, cand, nestLevel)
variableReachesCand(_, name, v0, cand, nestLevel)
|
v0 order by nestLevel
)
Expand Down
34 changes: 17 additions & 17 deletions rust/ql/test/library-tests/controlflow/Cfg.expected
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,42 @@ edges
| test.rs:10:9:22:9 | ExprStmt | test.rs:11:13:11:24 | ExprStmt | |
| test.rs:10:9:22:9 | LoopExpr | test.rs:23:9:23:20 | ExprStmt | |
| test.rs:10:14:22:9 | BlockExpr | test.rs:11:13:11:24 | ExprStmt | |
| test.rs:11:13:11:13 | PathExpr | test.rs:11:17:11:20 | PathExpr | |
| test.rs:11:13:11:13 | i | test.rs:11:17:11:20 | PathExpr | |
| test.rs:11:13:11:23 | ... = ... | test.rs:12:13:14:13 | ExprStmt | |
| test.rs:11:13:11:24 | ExprStmt | test.rs:11:13:11:13 | PathExpr | |
| test.rs:11:17:11:20 | PathExpr | test.rs:11:22:11:22 | PathExpr | |
| test.rs:11:13:11:24 | ExprStmt | test.rs:11:13:11:13 | i | |
| test.rs:11:17:11:20 | PathExpr | test.rs:11:22:11:22 | i | |
| test.rs:11:17:11:23 | CallExpr | test.rs:11:13:11:23 | ... = ... | |
| test.rs:11:22:11:22 | PathExpr | test.rs:11:17:11:23 | CallExpr | |
| test.rs:12:13:14:13 | ExprStmt | test.rs:12:16:12:16 | PathExpr | |
| test.rs:11:22:11:22 | i | test.rs:11:17:11:23 | CallExpr | |
| test.rs:12:13:14:13 | ExprStmt | test.rs:12:16:12:16 | i | |
| test.rs:12:13:14:13 | IfExpr | test.rs:15:13:17:13 | ExprStmt | |
| test.rs:12:16:12:16 | PathExpr | test.rs:12:20:12:24 | 10000 | |
| test.rs:12:16:12:16 | i | test.rs:12:20:12:24 | 10000 | |
| test.rs:12:16:12:24 | ... > ... | test.rs:12:13:14:13 | IfExpr | false |
| test.rs:12:16:12:24 | ... > ... | test.rs:13:17:13:29 | ExprStmt | true |
| test.rs:12:20:12:24 | 10000 | test.rs:12:16:12:24 | ... > ... | |
| test.rs:13:17:13:28 | ReturnExpr | test.rs:8:5:24:5 | exit test_break_and_continue (normal) | return |
| test.rs:13:17:13:29 | ExprStmt | test.rs:13:24:13:28 | false | |
| test.rs:13:24:13:28 | false | test.rs:13:17:13:28 | ReturnExpr | |
| test.rs:15:13:17:13 | ExprStmt | test.rs:15:16:15:16 | PathExpr | |
| test.rs:15:13:17:13 | ExprStmt | test.rs:15:16:15:16 | i | |
| test.rs:15:13:17:13 | IfExpr | test.rs:18:13:20:13 | ExprStmt | |
| test.rs:15:16:15:16 | PathExpr | test.rs:15:21:15:21 | 1 | |
| test.rs:15:16:15:16 | i | test.rs:15:21:15:21 | 1 | |
| test.rs:15:16:15:21 | ... == ... | test.rs:15:13:17:13 | IfExpr | false |
| test.rs:15:16:15:21 | ... == ... | test.rs:16:17:16:22 | ExprStmt | true |
| test.rs:15:21:15:21 | 1 | test.rs:15:16:15:21 | ... == ... | |
| test.rs:16:17:16:21 | BreakExpr | test.rs:10:9:22:9 | LoopExpr | break |
| test.rs:16:17:16:22 | ExprStmt | test.rs:16:17:16:21 | BreakExpr | |
| test.rs:18:13:20:13 | ExprStmt | test.rs:18:16:18:16 | PathExpr | |
| test.rs:18:13:20:13 | IfExpr | test.rs:21:13:21:13 | PathExpr | |
| test.rs:18:16:18:16 | PathExpr | test.rs:18:20:18:20 | 2 | |
| test.rs:18:13:20:13 | ExprStmt | test.rs:18:16:18:16 | i | |
| test.rs:18:13:20:13 | IfExpr | test.rs:21:13:21:13 | i | |
| test.rs:18:16:18:16 | i | test.rs:18:20:18:20 | 2 | |
| test.rs:18:16:18:20 | ... % ... | test.rs:18:25:18:25 | 0 | |
| test.rs:18:16:18:25 | ... != ... | test.rs:18:13:20:13 | IfExpr | false |
| test.rs:18:16:18:25 | ... != ... | test.rs:19:17:19:25 | ExprStmt | true |
| test.rs:18:20:18:20 | 2 | test.rs:18:16:18:20 | ... % ... | |
| test.rs:18:25:18:25 | 0 | test.rs:18:16:18:25 | ... != ... | |
| test.rs:19:17:19:24 | ContinueExpr | test.rs:11:13:11:24 | ExprStmt | continue |
| test.rs:19:17:19:25 | ExprStmt | test.rs:19:17:19:24 | ContinueExpr | |
| test.rs:21:13:21:13 | PathExpr | test.rs:21:17:21:17 | PathExpr | |
| test.rs:21:13:21:13 | i | test.rs:21:17:21:17 | i | |
| test.rs:21:13:21:21 | ... = ... | test.rs:10:14:22:9 | BlockExpr | |
| test.rs:21:17:21:17 | PathExpr | test.rs:21:21:21:21 | 2 | |
| test.rs:21:17:21:17 | i | test.rs:21:21:21:21 | 2 | |
| test.rs:21:17:21:21 | ... / ... | test.rs:21:13:21:21 | ... = ... | |
| test.rs:21:21:21:21 | 2 | test.rs:21:17:21:21 | ... / ... | |
| test.rs:23:9:23:19 | ReturnExpr | test.rs:8:5:24:5 | exit test_break_and_continue (normal) | return |
Expand Down Expand Up @@ -134,9 +134,9 @@ edges
| test.rs:72:21:72:21 | 0 | test.rs:72:17:72:21 | ... > ... | |
| test.rs:73:17:73:21 | BreakExpr | test.rs:70:9:76:9 | WhileExpr | break |
| test.rs:73:17:73:22 | ExprStmt | test.rs:73:17:73:21 | BreakExpr | |
| test.rs:75:13:75:13 | PathExpr | test.rs:75:17:75:21 | false | |
| test.rs:75:13:75:13 | b | test.rs:75:17:75:21 | false | |
| test.rs:75:13:75:21 | ... = ... | test.rs:70:17:76:9 | BlockExpr | |
| test.rs:75:13:75:22 | ExprStmt | test.rs:75:13:75:13 | PathExpr | |
| test.rs:75:13:75:22 | ExprStmt | test.rs:75:13:75:13 | b | |
| test.rs:75:17:75:21 | false | test.rs:75:13:75:21 | ... = ... | |
| test.rs:79:5:86:5 | enter test_while_let | test.rs:80:9:80:29 | LetStmt | |
| test.rs:79:5:86:5 | exit test_while_let (normal) | test.rs:79:5:86:5 | exit test_while_let | |
Expand Down Expand Up @@ -230,13 +230,13 @@ edges
| test.rs:122:28:124:9 | BlockExpr | test.rs:122:9:124:9 | IfExpr | |
| test.rs:123:13:123:13 | n | test.rs:122:28:124:9 | BlockExpr | |
| test.rs:125:9:125:9 | 0 | test.rs:121:43:126:5 | BlockExpr | |
| test.rs:128:5:134:5 | enter test_nested_if | test.rs:129:16:129:16 | PathExpr | |
| test.rs:128:5:134:5 | enter test_nested_if | test.rs:129:16:129:16 | a | |
| test.rs:128:5:134:5 | exit test_nested_if (normal) | test.rs:128:5:134:5 | exit test_nested_if | |
| test.rs:128:38:134:5 | BlockExpr | test.rs:128:5:134:5 | exit test_nested_if (normal) | |
| test.rs:129:9:133:9 | IfExpr | test.rs:128:38:134:5 | BlockExpr | |
| test.rs:129:13:129:48 | [boolean(false)] IfExpr | test.rs:132:13:132:13 | 0 | false |
| test.rs:129:13:129:48 | [boolean(true)] IfExpr | test.rs:130:13:130:13 | 1 | true |
| test.rs:129:16:129:16 | PathExpr | test.rs:129:20:129:20 | 0 | |
| test.rs:129:16:129:16 | a | test.rs:129:20:129:20 | 0 | |
| test.rs:129:16:129:20 | ... < ... | test.rs:129:24:129:24 | a | true |
| test.rs:129:16:129:20 | ... < ... | test.rs:129:41:129:41 | a | false |
| test.rs:129:20:129:20 | 0 | test.rs:129:16:129:20 | ... < ... | |
Expand Down
Loading
Loading