Skip to content

Commit 57661df

Browse files
committed
Rust: Fix variable capture inconsistencies
1 parent 632cde6 commit 57661df

File tree

5 files changed

+28
-41
lines changed

5 files changed

+28
-41
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,11 @@ module VariableCapture {
910910
CapturedVariable v;
911911

912912
VariableRead() {
913-
exists(VariableReadAccess read | this.getExpr() = read and v = read.getVariable())
913+
exists(VariableAccess read | this.getExpr() = read and v = read.getVariable() |
914+
read instanceof VariableReadAccess
915+
or
916+
read = any(RefExpr re).getExpr()
917+
)
914918
}
915919

916920
CapturedVariable getVariable() { result = v }

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

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ predicate variableWrite(AstNode write, Variable v) {
3838
)
3939
}
4040

41+
private predicate variableReadCertain(BasicBlock bb, int i, VariableAccess va, Variable v) {
42+
bb.getNode(i).getAstNode() = va and
43+
va = v.getAnAccess() and
44+
(
45+
va instanceof VariableReadAccess
46+
or
47+
// For immutable variables, we model a read when they are borrowed
48+
// (although the actual read happens later, if at all).
49+
va = any(RefExpr re).getExpr()
50+
or
51+
// Although compound assignments, like `x += y`, may in fact not read `x`,
52+
// it makes sense to treat them as such
53+
va = any(CompoundAssignmentExpr cae).getLhs()
54+
)
55+
}
56+
4157
module SsaInput implements SsaImplCommon::InputSig<Location> {
4258
class BasicBlock = BasicBlocks::BasicBlock;
4359

@@ -66,20 +82,7 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
6682
}
6783

6884
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
69-
exists(VariableAccess va |
70-
bb.getNode(i).getAstNode() = va and
71-
va = v.getAnAccess()
72-
|
73-
va instanceof VariableReadAccess
74-
or
75-
// For immutable variables, we model a read when they are borrowed
76-
// (although the actual read happens later, if at all).
77-
va = any(RefExpr re).getExpr()
78-
or
79-
// Although compound assignments, like `x += y`, may in fact not read `x`,
80-
// it makes sense to treat them as such
81-
va = any(CompoundAssignmentExpr cae).getLhs()
82-
) and
85+
variableReadCertain(bb, i, _, v) and
8386
certain = true
8487
or
8588
capturedCallRead(_, bb, i, v) and certain = false
@@ -100,16 +103,6 @@ class PhiDefinition = Impl::PhiNode;
100103

101104
module Consistency = Impl::Consistency;
102105

103-
/** Holds if `v` is read at index `i` in basic block `bb`. */
104-
private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
105-
exists(VariableAccess read |
106-
read instanceof VariableReadAccess or read = any(RefExpr re).getExpr()
107-
|
108-
read.getVariable() = v and
109-
read = bb.getNode(i).getAstNode()
110-
)
111-
}
112-
113106
/**
114107
* Holds if captured variable `v` is written directly inside `scope`,
115108
* or inside a (transitively) nested scope of `scope`.
@@ -125,18 +118,18 @@ private predicate hasCapturedWrite(Variable v, Cfg::CfgScope scope) {
125118
* immediate outer CFG scope of `scope`.
126119
*/
127120
pragma[noinline]
128-
private predicate variableReadActualInOuterScope(
121+
private predicate variableReadCertainInOuterScope(
129122
BasicBlock bb, int i, Variable v, Cfg::CfgScope scope
130123
) {
131-
variableReadActual(bb, i, v) and bb.getScope() = scope.getEnclosingCfgScope()
124+
variableReadCertain(bb, i, _, v) and bb.getScope() = scope.getEnclosingCfgScope()
132125
}
133126

134127
pragma[noinline]
135128
private predicate hasVariableReadWithCapturedWrite(
136129
BasicBlock bb, int i, Variable v, Cfg::CfgScope scope
137130
) {
138131
hasCapturedWrite(v, scope) and
139-
variableReadActualInOuterScope(bb, i, v, scope)
132+
variableReadCertainInOuterScope(bb, i, v, scope)
140133
}
141134

142135
private VariableAccess getACapturedVariableAccess(BasicBlock bb, Variable v) {
@@ -154,7 +147,7 @@ private predicate writesCapturedVariable(BasicBlock bb, Variable v) {
154147
/** Holds if `bb` contains a captured read to variable `v`. */
155148
pragma[nomagic]
156149
private predicate readsCapturedVariable(BasicBlock bb, Variable v) {
157-
getACapturedVariableAccess(bb, v) instanceof VariableReadAccess
150+
variableReadCertain(_, _, getACapturedVariableAccess(bb, v), _)
158151
}
159152

160153
/**
@@ -254,7 +247,7 @@ private module Cached {
254247
CfgNode getARead(Definition def) {
255248
exists(Variable v, BasicBlock bb, int i |
256249
Impl::ssaDefReachesRead(v, def, bb, i) and
257-
variableReadActual(bb, i, v) and
250+
variableReadCertain(bb, i, v.getAnAccess(), v) and
258251
result = bb.getNode(i)
259252
)
260253
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ read
255255
| main.rs:355:14:355:14 | x | main.rs:355:14:355:14 | x | main.rs:356:13:356:13 | x |
256256
| main.rs:362:9:362:9 | v | main.rs:362:9:362:9 | v | main.rs:365:12:365:12 | v |
257257
| main.rs:364:9:364:12 | text | main.rs:364:9:364:12 | text | main.rs:366:19:366:22 | text |
258+
| main.rs:371:13:371:13 | a | main.rs:371:13:371:13 | a | main.rs:372:5:372:5 | a |
258259
| main.rs:372:5:372:5 | a | main.rs:371:13:371:13 | a | main.rs:373:15:373:15 | a |
259260
| main.rs:372:5:372:5 | a | main.rs:371:13:371:13 | a | main.rs:374:11:374:11 | a |
260261
| main.rs:374:6:374:11 | &mut a | main.rs:371:13:371:13 | a | main.rs:375:15:375:15 | a |

rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/SsaConsistency.expected

Lines changed: 0 additions & 6 deletions
This file was deleted.

rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/VariableCaptureConsistency.expected

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)