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: Flow through captured variables #18270

Merged
merged 9 commits into from
Dec 16, 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
8 changes: 8 additions & 0 deletions rust/ql/consistency-queries/VariableCaptureConsistency.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @name Variable capture data flow inconsistencies
* @description Lists the variable capture data flow inconsistencies in the database. This query is intended for internal use.
* @kind table
* @id rust/diagnostics/variable-capture-data-flow-consistency
*/

import codeql.rust.dataflow.internal.DataFlowImpl::VariableCapture::Flow::ConsistencyChecks
60 changes: 46 additions & 14 deletions rust/ql/lib/codeql/rust/dataflow/Ssa.qll
Original file line number Diff line number Diff line change
Expand Up @@ -210,23 +210,22 @@ module Ssa {
final CfgNode getWriteAccess() { result = write }

/**
* Holds if this SSA definition assigns `value` to the underlying variable.
* Holds if this SSA definition assigns `value` to the underlying
* variable.
*
* This is either a direct assignment, `x = value`, or an assignment via
* simple pattern matching
*
* ```rb
* case value
* in Foo => x then ...
* in y => then ...
* end
* ```
* This is either the value in a direct assignment, `x = value`, or in a
* `let` statement, `let x = value`. Note that patterns on the lhs. are
* currently not supported.
*/
predicate assigns(ExprCfgNode value) {
exists(AssignmentExprCfgNode ae, BasicBlock bb, int i |
this.definesAt(_, bb, i) and
ae.getLhs() = bb.getNode(i) and
value = ae.getRhs()
exists(AssignmentExprCfgNode ae |
ae.getLhs() = write and
ae.getRhs() = value
)
or
exists(LetStmtCfgNode ls |
ls.getPat() = write and
ls.getInitializer() = value
)
}

Expand Down Expand Up @@ -338,4 +337,37 @@ module Ssa {

override Location getLocation() { result = this.getBasicBlock().getLocation() }
}

/**
* An SSA definition inserted at a call that may update the value of a captured
* variable. For example, in
*
* ```rust
* fn capture_mut() {
* let mut y = 0;
* (0..5).for_each(|x| {
* y += x
* });
* y
* }
* ```
*
* a definition for `y` is inserted at the call to `for_each`.
*/
private class CapturedCallDefinition extends Definition, SsaImpl::UncertainWriteDefinition {
CapturedCallDefinition() {
exists(Variable v, BasicBlock bb, int i |
this.definesAt(v, bb, i) and
SsaImpl::capturedCallWrite(_, bb, i, v)
)
}

/**
* Gets the immediately preceding definition. Since this update is uncertain,
* the value from the preceding definition might still be valid.
*/
final Definition getPriorDefinition() { result = SsaImpl::uncertainWriteDefinitionInput(this) }

override string toString() { result = "<captured exit> " + this.getSourceVariable() }
}
}
Loading
Loading