Skip to content

Commit 3fa52ad

Browse files
authored
Merge pull request #17633 from hvitved/rust/cfg-fixes
Rust: More CFG modelling
2 parents 8b536f5 + 9c7216f commit 3fa52ad

File tree

103 files changed

+649
-579
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+649
-579
lines changed

rust/ql/consistency-queries/CfgConsistency.ql

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import codeql.rust.controlflow.internal.Completion
1111
query predicate nonPostOrderExpr(Expr e, string cls) {
1212
cls = e.getPrimaryQlClasses() and
1313
not e instanceof LetExpr and
14+
not e instanceof ParenExpr and
1415
not e instanceof LogicalAndExpr and // todo
1516
not e instanceof LogicalOrExpr and
1617
exists(AstNode last, Completion c |
@@ -25,3 +26,14 @@ query predicate scopeNoFirst(CfgScope scope) {
2526
not scope = any(Function f | not exists(f.getBody())) and
2627
not scope = any(ClosureExpr c | not exists(c.getBody()))
2728
}
29+
30+
/** Holds if `be` is the `else` branch of a `let` statement that results in a panic. */
31+
private predicate letElsePanic(BlockExpr be) {
32+
be = any(LetStmt let).getLetElse().getBlockExpr() and
33+
exists(Completion c | CfgImpl::last(be, _, c) | completionIsNormal(c))
34+
}
35+
36+
query predicate deadEnd(CfgImpl::Node node) {
37+
Consistency::deadEnd(node) and
38+
not letElsePanic(node.getAstNode())
39+
}

rust/ql/lib/codeql/rust/controlflow/internal/Completion.qll

+5-1
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,18 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
8484
private predicate isValidForSpecific0(AstNode e) {
8585
e = any(IfExpr c).getCondition()
8686
or
87-
any(MatchArm arm).getGuard() = e
87+
e = any(WhileExpr c).getCondition()
88+
or
89+
any(MatchGuard guard).getCondition() = e
8890
or
8991
exists(BinaryExpr expr |
9092
expr.getOperatorName() = ["&&", "||"] and
9193
e = expr.getLhs()
9294
)
9395
or
9496
exists(Expr parent | this.isValidForSpecific0(parent) |
97+
e = parent.(ParenExpr).getExpr()
98+
or
9599
parent =
96100
any(PrefixExpr expr |
97101
expr.getOperatorName() = "!" and

rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll

+142-17
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ import CfgImpl
6161
/** Holds if `p` is a trivial pattern that is always guaranteed to match. */
6262
predicate trivialPat(Pat p) { p instanceof WildcardPat or p instanceof IdentPat }
6363

64+
class ArrayExprTree extends StandardPostOrderTree, ArrayExpr {
65+
override AstNode getChildNode(int i) { result = this.getExpr(i) }
66+
}
67+
6468
class AsmExprTree extends LeafTree instanceof AsmExpr { }
6569

6670
class AwaitExprTree extends StandardPostOrderTree instanceof AwaitExpr {
@@ -132,14 +136,40 @@ class LogicalAndBinaryOpExprTree extends PreOrderTree, LogicalAndExpr {
132136
}
133137
}
134138

135-
class BlockExprBaseTree extends StandardPostOrderTree instanceof BlockExpr {
139+
class BlockExprTree extends StandardPostOrderTree, BlockExpr {
136140
override AstNode getChildNode(int i) {
137141
result = super.getStmtList().getStatement(i)
138142
or
139143
not exists(super.getStmtList().getStatement(i)) and
140144
(exists(super.getStmtList().getStatement(i - 1)) or i = 0) and
141145
result = super.getStmtList().getTailExpr()
142146
}
147+
148+
override predicate propagatesAbnormal(AstNode child) { none() }
149+
150+
/** Holds if this block captures the break completion `c`. */
151+
private predicate capturesBreakCompletion(LoopJumpCompletion c) {
152+
c.isBreak() and
153+
c.getLabelName() = this.getLabel().getLifetime().getText()
154+
}
155+
156+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
157+
super.succ(pred, succ, c)
158+
or
159+
// Edge for exiting the block with a break expressions
160+
last(this.getChildNode(_), pred, c) and
161+
this.capturesBreakCompletion(c) and
162+
succ = this
163+
}
164+
165+
override predicate last(AstNode last, Completion c) {
166+
super.last(last, c)
167+
or
168+
// Any abnormal completions that this block does not capture should propagate
169+
last(this.getChildNode(_), last, c) and
170+
not completionIsNormal(c) and
171+
not this.capturesBreakCompletion(c)
172+
}
143173
}
144174

145175
class BreakExprTree extends PostOrderTree instanceof BreakExpr {
@@ -189,7 +219,7 @@ class IfExprTree extends PostOrderTree instanceof IfExpr {
189219
child = [super.getCondition(), super.getThen(), super.getElse()]
190220
}
191221

192-
ConditionalCompletion conditionCompletion(Completion c) {
222+
private ConditionalCompletion conditionCompletion(Completion c) {
193223
if super.getCondition() instanceof LetExpr
194224
then result = c.(MatchCompletion)
195225
else result = c.(BooleanCompletion)
@@ -226,6 +256,8 @@ class IndexExprTree extends StandardPostOrderTree instanceof IndexExpr {
226256
}
227257
}
228258

259+
class ItemTree extends LeafTree, Item { }
260+
229261
// `LetExpr` is a pre-order tree such that the pattern itself ends up
230262
// dominating successors in the graph in the same way that patterns do in
231263
// `match` expressions.
@@ -277,45 +309,124 @@ class LetStmtTree extends PreOrderTree instanceof LetStmt {
277309

278310
class LiteralExprTree extends LeafTree instanceof LiteralExpr { }
279311

280-
class LoopExprTree extends PostOrderTree instanceof LoopExpr {
312+
abstract class LoopingExprTree extends PostOrderTree {
281313
override predicate propagatesAbnormal(AstNode child) { none() }
282314

283-
override predicate first(AstNode node) { first(super.getLoopBody(), node) }
315+
abstract BlockExpr getLoopBody();
316+
317+
abstract Label getLabel();
318+
319+
/**
320+
* Gets the node to execute when continuing the loop; either after
321+
* executing the last node in the body or after an explicit `continue`.
322+
*/
323+
abstract AstNode getLoopContinue();
284324

285-
/** Whether this `LoopExpr` captures the `c` completion. */
325+
/** Holds if this loop captures the `c` completion. */
286326
private predicate capturesLoopJumpCompletion(LoopJumpCompletion c) {
287327
not c.hasLabel()
288328
or
289-
c.getLabelName() = super.getLabel().getLifetime().getText()
329+
c.getLabelName() = this.getLabel().getLifetime().getText()
290330
}
291331

292332
override predicate succ(AstNode pred, AstNode succ, Completion c) {
333+
// Edge for exiting the loop with a break expressions
334+
last(this.getLoopBody(), pred, c) and
335+
c.(LoopJumpCompletion).isBreak() and
336+
this.capturesLoopJumpCompletion(c) and
337+
succ = this
338+
or
293339
// Edge back to the start for final expression and continue expressions
294-
last(super.getLoopBody(), pred, c) and
340+
last(this.getLoopBody(), pred, c) and
295341
(
296342
completionIsNormal(c)
297343
or
298344
c.(LoopJumpCompletion).isContinue() and this.capturesLoopJumpCompletion(c)
299345
) and
300-
this.first(succ)
301-
or
302-
// Edge for exiting the loop with a break expressions
303-
last(super.getLoopBody(), pred, c) and
304-
c.(LoopJumpCompletion).isBreak() and
305-
this.capturesLoopJumpCompletion(c) and
306-
succ = this
346+
first(this.getLoopContinue(), succ)
307347
}
308348

309349
override predicate last(AstNode last, Completion c) {
310350
super.last(last, c)
311351
or
312352
// Any abnormal completions that this loop does not capture should propagate
313-
last(super.getLoopBody(), last, c) and
353+
last(this.getLoopBody(), last, c) and
314354
not completionIsNormal(c) and
315355
not this.capturesLoopJumpCompletion(c)
316356
}
317357
}
318358

359+
class LoopExprTree extends LoopingExprTree instanceof LoopExpr {
360+
override BlockExpr getLoopBody() { result = LoopExpr.super.getLoopBody() }
361+
362+
override Label getLabel() { result = LoopExpr.super.getLabel() }
363+
364+
override AstNode getLoopContinue() { result = this.getLoopBody() }
365+
366+
override predicate first(AstNode node) { first(this.getLoopBody(), node) }
367+
}
368+
369+
class WhileExprTree extends LoopingExprTree instanceof WhileExpr {
370+
override BlockExpr getLoopBody() { result = WhileExpr.super.getLoopBody() }
371+
372+
override Label getLabel() { result = WhileExpr.super.getLabel() }
373+
374+
override AstNode getLoopContinue() { result = super.getCondition() }
375+
376+
override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() }
377+
378+
override predicate first(AstNode node) { first(super.getCondition(), node) }
379+
380+
private ConditionalCompletion conditionCompletion(Completion c) {
381+
if super.getCondition() instanceof LetExpr
382+
then result = c.(MatchCompletion)
383+
else result = c.(BooleanCompletion)
384+
}
385+
386+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
387+
super.succ(pred, succ, c)
388+
or
389+
last(super.getCondition(), pred, c) and
390+
this.conditionCompletion(c).succeeded() and
391+
first(this.getLoopBody(), succ)
392+
or
393+
last(super.getCondition(), pred, c) and
394+
this.conditionCompletion(c).failed() and
395+
succ = this
396+
}
397+
}
398+
399+
class ForExprTree extends LoopingExprTree instanceof ForExpr {
400+
override BlockExpr getLoopBody() { result = ForExpr.super.getLoopBody() }
401+
402+
override Label getLabel() { result = ForExpr.super.getLabel() }
403+
404+
override AstNode getLoopContinue() { result = super.getPat() }
405+
406+
override predicate propagatesAbnormal(AstNode child) { child = super.getIterable() }
407+
408+
override predicate first(AstNode node) { first(super.getIterable(), node) }
409+
410+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
411+
super.succ(pred, succ, c)
412+
or
413+
last(super.getIterable(), pred, c) and
414+
first(super.getPat(), succ) and
415+
completionIsNormal(c)
416+
or
417+
last(super.getPat(), pred, c) and
418+
c.(MatchCompletion).succeeded() and
419+
first(this.getLoopBody(), succ)
420+
or
421+
last(super.getPat(), pred, c) and
422+
c.(MatchCompletion).failed() and
423+
succ = this
424+
}
425+
}
426+
427+
// TODO: replace with expanded macro once the extractor supports it
428+
class MacroExprTree extends LeafTree, MacroExpr { }
429+
319430
class MatchArmTree extends ControlFlowTree instanceof MatchArm {
320431
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
321432

@@ -376,10 +487,24 @@ class MethodCallExprTree extends StandardPostOrderTree instanceof MethodCallExpr
376487
}
377488
}
378489

490+
class NameTree extends LeafTree, Name { }
491+
492+
class NameRefTree extends LeafTree, NameRef { }
493+
379494
class OffsetOfExprTree extends LeafTree instanceof OffsetOfExpr { }
380495

381-
class ParenExprTree extends StandardPostOrderTree, ParenExpr {
382-
override AstNode getChildNode(int i) { i = 0 and result = super.getExpr() }
496+
class ParenExprTree extends ControlFlowTree, ParenExpr {
497+
private ControlFlowTree expr;
498+
499+
ParenExprTree() { expr = super.getExpr() }
500+
501+
override predicate propagatesAbnormal(AstNode child) { expr.propagatesAbnormal(child) }
502+
503+
override predicate first(AstNode first) { expr.first(first) }
504+
505+
override predicate last(AstNode last, Completion c) { expr.last(last, c) }
506+
507+
override predicate succ(AstNode pred, AstNode succ, Completion c) { none() }
383508
}
384509

385510
// This covers all patterns as they all extend `Pat`

rust/ql/test/extractor-tests/generated/Abi/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ArgList/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ArrayExpr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ArrayType/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/AssocItemList/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/AssocTypeArg/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/Attr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ClosureBinder/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/Const/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ConstArg/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ConstParam/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/DynTraitType/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/Enum/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ExprStmt/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ExternBlock/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ExternCrate/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ExternItemList/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/FnPtrType/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ForExpr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ForType/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/FormatArgsArg/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/FormatArgsExpr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/GenericParamList/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/IfExpr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/Impl/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ImplTraitType/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/InferType/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/ItemList/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/Label/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/LetElse/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/LetExpr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/Lifetime/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/LifetimeArg/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/LifetimeParam/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/LoopExpr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/MacroCall/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/MacroDef/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/MacroExpr/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/MacroPat/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

rust/ql/test/extractor-tests/generated/MacroRules/CONSISTENCY/CfgConsistency.expected

-2
This file was deleted.

0 commit comments

Comments
 (0)