Skip to content

Commit 7ddc8f0

Browse files
authored
Merge pull request #17786 from paldepind/rust-saa-additions
Rust: SSA additions
2 parents 813ccb9 + 8f8564d commit 7ddc8f0

File tree

7 files changed

+2323
-2090
lines changed

7 files changed

+2323
-2090
lines changed

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

Lines changed: 116 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,20 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
4949
/**
5050
* A variable amenable to SSA construction.
5151
*
52-
* All immutable variables are amenable. Mutable variables are restricted
53-
* to those that are not captured by closures, and are not borrowed
54-
* (either explicitly using `& mut`, or (potentially) implicit as borrowed
55-
* receivers in a method call).
52+
* All immutable variables are amenable. Mutable variables are restricted to
53+
* those that are not borrowed (either explicitly using `& mut`, or
54+
* (potentially) implicit as borrowed receivers in a method call).
5655
*/
5756
class SourceVariable extends Variable {
5857
SourceVariable() {
59-
this.isImmutable()
60-
or
61-
this.isMutable() and
62-
not this.isCaptured() and
63-
forall(VariableAccess va | va = this.getAnAccess() |
64-
va instanceof VariableReadAccess and
58+
this.isMutable()
59+
implies
60+
not exists(VariableAccess va | va = this.getAnAccess() |
61+
va = any(RefExpr re | re.isMut()).getExpr()
62+
or
6563
// receivers can be borrowed implicitly, cf.
6664
// https://doc.rust-lang.org/reference/expressions/method-call-expr.html
67-
not va = any(MethodCallExpr mce).getReceiver()
68-
or
69-
variableWrite(va, this)
65+
va = any(MethodCallExpr mce).getReceiver()
7066
)
7167
}
7268
}
@@ -78,6 +74,8 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
7874
capturedEntryWrite(bb, i, v)
7975
) and
8076
certain = true
77+
or
78+
capturedCallWrite(_, bb, i, v) and certain = false
8179
}
8280

8381
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
@@ -102,6 +100,10 @@ module SsaInput implements SsaImplCommon::InputSig<Location> {
102100
v = va.getVariable() and
103101
certain = false
104102
)
103+
or
104+
capturedCallRead(_, bb, i, v) and certain = false
105+
or
106+
capturedExitRead(bb, i, v) and certain = false
105107
}
106108
}
107109

@@ -147,6 +149,35 @@ private predicate variableReadActual(BasicBlock bb, int i, Variable v) {
147149
)
148150
}
149151

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+
150181
private predicate adjacentDefReachesRead(
151182
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
152183
) {
@@ -207,16 +238,81 @@ private predicate lastRefSkipUncertainReadsExt(DefinitionExt def, BasicBlock bb,
207238
)
208239
}
209240

210-
/** Holds if `bb` contains a captured access to variable `v`. */
241+
private VariableAccess getACapturedVariableAccess(BasicBlock bb, Variable v) {
242+
result = bb.getANode().getAstNode() and
243+
result.isCapture() and
244+
result.getVariable() = v
245+
}
246+
247+
/** Holds if `bb` contains a captured write to variable `v`. */
248+
pragma[noinline]
249+
private predicate writesCapturedVariable(BasicBlock bb, Variable v) {
250+
getACapturedVariableAccess(bb, v) instanceof VariableWriteAccess
251+
}
252+
253+
/** Holds if `bb` contains a captured read to variable `v`. */
211254
pragma[nomagic]
212-
private predicate hasCapturedVariableAccess(BasicBlock bb, Variable v) {
213-
exists(VariableAccess read |
214-
read = bb.getANode().getAstNode() and
215-
read.isCapture() and
216-
read.getVariable() = v
255+
private predicate readsCapturedVariable(BasicBlock bb, Variable v) {
256+
getACapturedVariableAccess(bb, v) instanceof VariableReadAccess
257+
}
258+
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()
217290
)
218291
}
219292

293+
/**
294+
* Holds if the call `call` at index `i` in basic block `bb` may reach a callable
295+
* that writes captured variable `v`.
296+
*/
297+
predicate capturedCallWrite(CallExprBase call, BasicBlock bb, int i, Variable v) {
298+
call = bb.getNode(i).getAstNode() and
299+
exists(Cfg::CfgScope scope |
300+
hasVariableReadWithCapturedWrite(bb, any(int j | j > i), v, scope)
301+
or
302+
hasVariableReadWithCapturedWrite(bb.getASuccessor+(), _, v, scope)
303+
)
304+
}
305+
306+
/**
307+
* Holds if a pseudo read of captured variable `v` should be inserted
308+
* at index `i` in exit block `bb`.
309+
*/
310+
private predicate capturedExitRead(AnnotatedExitBasicBlock bb, int i, Variable v) {
311+
bb.isNormal() and
312+
writesCapturedVariable(bb.getAPredecessor*(), v) and
313+
i = bb.length()
314+
}
315+
220316
cached
221317
private module Cached {
222318
/**
@@ -225,7 +321,7 @@ private module Cached {
225321
*/
226322
cached
227323
predicate capturedEntryWrite(EntryBasicBlock bb, int i, Variable v) {
228-
hasCapturedVariableAccess(bb.getASuccessor*(), v) and
324+
readsCapturedVariable(bb.getASuccessor*(), v) and
229325
i = -1
230326
}
231327

0 commit comments

Comments
 (0)