Skip to content

Commit 334602a

Browse files
committed
Rust: Handle calls that might read/write variables through closures
This implementation is copied and adapted from the Ruby SSA implementation.
1 parent 75103f4 commit 334602a

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

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

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
7474
capturedEntryWrite(bb, i, v)
7575
) and
7676
certain = true
77+
or
78+
capturedCallWrite(_, bb, i, v) and certain = false
7779
}
7880

7981
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
@@ -99,6 +101,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
99101
certain = false
100102
)
101103
or
104+
capturedCallRead(_, bb, i, v) and certain = false
105+
or
102106
capturedExitRead(bb, i, v) and certain = false
103107
}
104108
}
@@ -145,6 +149,35 @@ private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
145149
)
146150
}
147151

152+
/**
153+
* Holds if captured variable `v` is written directly inside `scope`,
154+
* or inside a (transitively) nested scope of `scope`.
155+
*/
156+
pragma[noinline]
157+
private predicate hasCapturedWrite(Variable v, Cfg::CfgScope scope) {
158+
any(VariableWriteAccess write | write.getVariable() = v and scope = write.getEnclosingCallable*())
159+
.isCapture()
160+
}
161+
162+
/**
163+
* Holds if `v` is read inside basic block `bb` at index `i`, which is in the
164+
* immediate outer scope of `scope`.
165+
*/
166+
pragma[noinline]
167+
private predicate variableReadActualInOuterScope(
168+
BasicBlock bb, int i, Variable v, Cfg::CfgScope scope
169+
) {
170+
variableReadActual(bb, i, v) and bb.getScope() = scope.getEnclosingCallable()
171+
}
172+
173+
pragma[noinline]
174+
private predicate hasVariableReadWithCapturedWrite(
175+
BasicBlock bb, int i, Variable v, Cfg::CfgScope scope
176+
) {
177+
hasCapturedWrite(v, scope) and
178+
variableReadActualInOuterScope(bb, i, v, scope)
179+
}
180+
148181
private predicate adjacentDefReachesRead(
149182
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
150183
) {
@@ -223,6 +256,40 @@ private predicate readsCapturedVariable(BasicBlock bb, Variable v) {
223256
getCapturedVariableAccess(bb, v) instanceof VariableReadAccess
224257
}
225258

259+
/**
260+
* Holds if captured variable `v` is read directly inside `scope`,
261+
* or inside a (transitively) nested scope of `scope`.
262+
*/
263+
pragma[noinline]
264+
private predicate hasCapturedRead(Variable v, Cfg::CfgScope scope) {
265+
any(VariableReadAccess read | read.getVariable() = v and scope = read.getEnclosingCallable*())
266+
.isCapture()
267+
}
268+
269+
/**
270+
* Holds if `v` is written inside basic block `bb` at index `i`, which is in
271+
* the immediate outer scope of `scope`.
272+
*/
273+
pragma[noinline]
274+
private predicate variableWriteInOuterScope(BasicBlock bb, int i, Variable v, Cfg::CfgScope scope) {
275+
SsaInput::variableWrite(bb, i, v, _) and scope.getEnclosingCallable() = bb.getScope()
276+
}
277+
278+
/**
279+
* Holds if the call `call` at index `i` in basic block `bb` may reach
280+
* a callable that reads captured variable `v`.
281+
*/
282+
private predicate capturedCallRead(CallExprBase call, BasicBlock bb, int i, Variable v) {
283+
exists(Cfg::CfgScope scope |
284+
hasCapturedRead(v, scope) and
285+
(
286+
variableWriteInOuterScope(bb, any(int j | j < i), v, scope) or
287+
variableWriteInOuterScope(bb.getAPredecessor+(), _, v, scope)
288+
) and
289+
call = bb.getNode(i).getAstNode()
290+
)
291+
}
292+
226293
/**
227294
* Holds if a pseudo read of captured variable `v` should be inserted
228295
* at index `i` in exit block `bb`.
@@ -245,6 +312,20 @@ private module Cached {
245312
i = -1
246313
}
247314

315+
/**
316+
* Holds if the call `call` at index `i` in basic block `bb` may reach a callable
317+
* that writes captured variable `v`.
318+
*/
319+
cached
320+
predicate capturedCallWrite(CallExprBase call, BasicBlock bb, int i, Variable v) {
321+
call = bb.getNode(i).getAstNode() and
322+
exists(Cfg::CfgScope scope |
323+
hasVariableReadWithCapturedWrite(bb, any(int j | j > i), v, scope)
324+
or
325+
hasVariableReadWithCapturedWrite(bb.getASuccessor+(), _, v, scope)
326+
)
327+
}
328+
248329
/**
249330
* Holds if `v` is written at index `i` in basic block `bb`, and the corresponding
250331
* AST write access is `write`.

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ definition
120120
| variables.rs:418:9:418:13 | y | variables.rs:418:13:418:13 | y |
121121
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 |
122122
| variables.rs:421:9:421:9 | y | variables.rs:418:13:418:13 | y |
123+
| variables.rs:423:5:423:14 | CallExpr | variables.rs:418:13:418:13 | y |
123124
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 |
124125
| variables.rs:435:8:435:8 | b | variables.rs:435:8:435:8 | b |
125126
| variables.rs:436:9:436:13 | x | variables.rs:436:13:436:13 | x |
@@ -233,8 +234,8 @@ read
233234
| variables.rs:410:9:410:13 | x | variables.rs:410:13:410:13 | x | variables.rs:416:15:416:15 | x |
234235
| variables.rs:412:9:412:16 | closure1 | variables.rs:412:9:412:16 | closure1 | variables.rs:415:5:415:12 | closure1 |
235236
| variables.rs:412:20:414:5 | <captured entry> x | variables.rs:410:13:410:13 | x | variables.rs:413:19:413:19 | x |
236-
| variables.rs:418:9:418:13 | y | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
237237
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
238+
| variables.rs:423:5:423:14 | CallExpr | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
238239
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
239240
| variables.rs:435:8:435:8 | b | variables.rs:435:8:435:8 | b | variables.rs:439:8:439:8 | b |
240241
| variables.rs:436:9:436:13 | x | variables.rs:436:13:436:13 | x | variables.rs:437:15:437:15 | x |
@@ -335,8 +336,8 @@ firstRead
335336
| variables.rs:410:9:410:13 | x | variables.rs:410:13:410:13 | x | variables.rs:416:15:416:15 | x |
336337
| variables.rs:412:9:412:16 | closure1 | variables.rs:412:9:412:16 | closure1 | variables.rs:415:5:415:12 | closure1 |
337338
| variables.rs:412:20:414:5 | <captured entry> x | variables.rs:410:13:410:13 | x | variables.rs:413:19:413:19 | x |
338-
| variables.rs:418:9:418:13 | y | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
339339
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
340+
| variables.rs:423:5:423:14 | CallExpr | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
340341
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
341342
| variables.rs:435:8:435:8 | b | variables.rs:435:8:435:8 | b | variables.rs:439:8:439:8 | b |
342343
| variables.rs:436:9:436:13 | x | variables.rs:436:13:436:13 | x | variables.rs:437:15:437:15 | x |
@@ -433,8 +434,8 @@ lastRead
433434
| variables.rs:410:9:410:13 | x | variables.rs:410:13:410:13 | x | variables.rs:416:15:416:15 | x |
434435
| variables.rs:412:9:412:16 | closure1 | variables.rs:412:9:412:16 | closure1 | variables.rs:415:5:415:12 | closure1 |
435436
| variables.rs:412:20:414:5 | <captured entry> x | variables.rs:410:13:410:13 | x | variables.rs:413:19:413:19 | x |
436-
| variables.rs:418:9:418:13 | y | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
437437
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
438+
| variables.rs:423:5:423:14 | CallExpr | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
438439
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
439440
| variables.rs:435:8:435:8 | b | variables.rs:435:8:435:8 | b | variables.rs:439:8:439:8 | b |
440441
| variables.rs:436:9:436:13 | x | variables.rs:436:13:436:13 | x | variables.rs:438:15:438:15 | x |

0 commit comments

Comments
 (0)