Skip to content

Commit ab94524

Browse files
committed
Cfg: Address review comments.
1 parent 94121f1 commit ab94524

File tree

2 files changed

+107
-38
lines changed

2 files changed

+107
-38
lines changed

java/ql/lib/semmle/code/java/ControlFlowGraph.qll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ private import Cfg2
2121
import Public
2222

2323
/** Provides an implementation of the AST signature for Java. */
24-
module Ast implements AstSig<Location> {
24+
private module Ast implements AstSig<Location> {
2525
private import java as J
2626

2727
class AstNode = ExprParent;
@@ -474,7 +474,6 @@ private module Input implements InputSig1, InputSig2 {
474474
catch.getACaughtType() instanceof TypeThrowable
475475
or
476476
exists(TryStmt try, int last |
477-
// not Exceptions::expectUncaught(try) and
478477
catch.getACaughtType() instanceof TypeException and
479478
try.getCatchClause(last) = catch and
480479
not exists(try.getCatchClause(last + 1))

shared/controlflow/codeql/controlflow/ControlFlowGraph.qll

Lines changed: 106 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ signature module AstSig<LocationSig Location> {
7474
Expr getExpr();
7575
}
7676

77-
/** An if statement. */
77+
/** An `if` statement. */
7878
class IfStmt extends Stmt {
79-
/** Gets the condition of this if statement. */
79+
/** Gets the condition of this `if` statement. */
8080
Expr getCondition();
8181

8282
/** Gets the `then` (true) branch of this `if` statement. */
@@ -231,6 +231,8 @@ signature module AstSig<LocationSig Location> {
231231
* This is most often the same as the AST order, but can be different in some
232232
* languages if the language allows a default case to appear before other
233233
* cases.
234+
*
235+
* The values do not need to be contiguous; only the relative ordering matters.
234236
*/
235237
default int getCaseControlFlowOrder(Switch s, Case c) { s.getCase(result) = c }
236238

@@ -308,7 +310,7 @@ signature module AstSig<LocationSig Location> {
308310
* completed by subsequent instatiation of `Make1` and `Make2`.
309311
*
310312
* A complete instantiation can look as follows:
311-
* ```
313+
* ```ql
312314
* private module Input implements InputSig1, InputSig2 { .. }
313315
* private module Cfg0 = Make0<Location, Ast>;
314316
* private module Cfg1 = Make1<Input>;
@@ -347,34 +349,49 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
347349
/**
348350
* Holds if the value of `child` is propagated to `parent`. For example,
349351
* the right-hand side of short-circuiting expressions.
352+
*
353+
* This predicate is only relevant for AST constructs that are not already
354+
* handled by this library.
350355
*/
351356
default predicate propagatesValue(AstNode child, AstNode parent) { none() }
352357

353358
/**
354359
* Holds if `n` is in a conditional context of kind `kind`. For example,
355360
* the left-hand side of a short-circuiting `&&` expression is in a
356361
* boolean conditional context.
362+
*
363+
* This predicate is only relevant for AST constructs that are not already
364+
* handled by this library.
357365
*/
358366
default predicate inConditionalContext(AstNode n, ConditionKind kind) { none() }
359367

360368
/**
361369
* Holds if `e` is executed in pre-order. This is typical for expressions
362370
* that are pure control-flow constructions without calculation or side
363371
* effects, such as `ConditionalExpr` and `Switch` expressions.
372+
*
373+
* This predicate is only relevant for AST constructs that are not already
374+
* handled by this library.
364375
*/
365376
default predicate preOrderExpr(Expr e) { none() }
366377

367378
/**
368379
* Holds if `n` is executed in post-order or in-order. This means that an
369380
* additional node is created to represent `n` in the control flow graph.
370381
* Otherwise, `n` is represented by the "before" node.
382+
*
383+
* This predicate is only relevant for AST constructs that are not already
384+
* handled by this library.
371385
*/
372386
default predicate postOrInOrder(AstNode n) { none() }
373387

374388
/**
375389
* Holds if an additional node tagged with `tag` should be created for
376390
* `n`. Edges targeting such nodes are labeled with `t` and therefore `t`
377391
* should be unique for a given `(n,tag)` pair.
392+
*
393+
* This predicate is only relevant for AST constructs that are not already
394+
* handled by this library.
378395
*/
379396
default predicate additionalNode(AstNode n, string tag, NormalSuccessor t) { none() }
380397

@@ -446,9 +463,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
446463
not postOrInOrder(parent) and
447464
parent.(BinaryExpr).getRightOperand() = child
448465
or
449-
parent.(ConditionalExpr).getThen() = child
450-
or
451-
parent.(ConditionalExpr).getElse() = child
466+
parent = any(ConditionalExpr ce | child = [ce.getThen(), ce.getElse()])
452467
or
453468
parent.(BlockStmt).getLastStmt() = child
454469
or
@@ -581,15 +596,19 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
581596
cached
582597
private newtype TNode =
583598
TBeforeNode(AstNode n) { Input1::cfgCachedStageRef() and exists(getEnclosingCallable(n)) } or
584-
TAstNode(AstNode n) { postOrInOrder(n) } or
585-
TAfterValueNode(AstNode n, ConditionalSuccessor t) { inConditionalContext(n, t.getKind()) } or
599+
TAstNode(AstNode n) { postOrInOrder(n) and exists(getEnclosingCallable(n)) } or
600+
TAfterValueNode(AstNode n, ConditionalSuccessor t) {
601+
inConditionalContext(n, t.getKind()) and exists(getEnclosingCallable(n))
602+
} or
586603
TAfterNode(AstNode n) {
587604
exists(getEnclosingCallable(n)) and
588605
not inConditionalContext(n, _) and
589606
not cannotTerminateNormally(n) and
590607
not simpleLeafNode(n)
591608
} or
592-
TAdditionalNode(AstNode n, string tag) { additionalNode(n, tag, _) } or
609+
TAdditionalNode(AstNode n, string tag) {
610+
additionalNode(n, tag, _) and exists(getEnclosingCallable(n))
611+
} or
593612
TEntryNode(Callable c) { exists(callableGetBody(c)) } or
594613
TAnnotatedExitNode(Callable c, Boolean normal) { exists(callableGetBody(c)) } or
595614
TExitNode(Callable c) { exists(callableGetBody(c)) }
@@ -636,6 +655,15 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
636655
this = TAfterValueNode(n, t)
637656
or
638657
exists(ConditionalSuccessor t0 | this = TAfterValueNode(n, t0) |
658+
// When this predicate is used to identify the end point of a step,
659+
// the kinds of `t` and `t0` may not match. For example, in
660+
// `(x || y) ?? z`, the `||` may short-circuit with a known boolean
661+
// value `t`, but it occurs in a nullness conditional context, which
662+
// means that the `t0` has nullness kind. In these cases we check
663+
// whether there is an implication that allows translatiion from `t`
664+
// to `t0`, and if not `t0` is simply unrestricted. If the kinds did
665+
// match, then no translation is needed and we're covered by the
666+
// `this = TAfterValueNode(n, t)` case above.
639667
Input1::successorValueImplies(t, t0)
640668
or
641669
not Input1::successorValueImplies(t, _) and
@@ -738,7 +766,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
738766
override string getIdTag() { result = "before" }
739767

740768
override string toString() {
741-
if postOrInOrder(n) then result = "Before " + n.toString() else result = n.toString()
769+
if postOrInOrder(n) then result = "Before " + n else result = n.toString()
742770
}
743771
}
744772

@@ -768,7 +796,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
768796
t.getValue() = false and result = "after-false"
769797
}
770798

771-
override string toString() { result = "After " + n.toString() + " [" + t.toString() + "]" }
799+
override string toString() { result = "After " + n + " [" + t + "]" }
772800
}
773801

774802
private class AfterNode extends NodeImpl, TAfterNode {
@@ -780,7 +808,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
780808

781809
override string getIdTag() { result = "after" }
782810

783-
override string toString() { result = "After " + n.toString() }
811+
override string toString() { result = "After " + n }
784812
}
785813

786814
private class AdditionalNode extends NodeImpl, TAdditionalNode {
@@ -795,7 +823,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
795823

796824
override string getIdTag() { result = "add. " + tag }
797825

798-
override string toString() { result = tag + " " + n.toString() }
826+
override string toString() { result = tag + " " + n }
799827
}
800828

801829
final private class EntryNodeImpl extends NodeImpl, TEntryNode {
@@ -877,7 +905,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
877905
or
878906
exists(AbruptSuccessor t, Input1::Label l |
879907
this = TLabeledAbruptCompletion(t, l) and
880-
result = t.toString() + " " + l.toString()
908+
result = t + " " + l
881909
)
882910
}
883911

@@ -913,6 +941,9 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
913941
* Holds if `ast` may result in an abrupt completion `c` originating at
914942
* `n`. The boolean `always` indicates whether the abrupt completion
915943
* always occurs or whether `n` may also terminate normally.
944+
*
945+
* This predicate is only relevant for AST constructs that are not already
946+
* handled by this library.
916947
*/
917948
predicate beginAbruptCompletion(
918949
AstNode ast, PreControlFlowNode n, AbruptCompletion c, boolean always
@@ -921,10 +952,18 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
921952
/**
922953
* Holds if an abrupt completion `c` from within `ast` is caught with
923954
* flow continuing at `n`.
955+
*
956+
* This predicate is only relevant for AST constructs that are not already
957+
* handled by this library.
924958
*/
925959
predicate endAbruptCompletion(AstNode ast, PreControlFlowNode n, AbruptCompletion c);
926960

927-
/** Holds if there is a local non-abrupt step from `n1` to `n2`. */
961+
/**
962+
* Holds if there is a local non-abrupt step from `n1` to `n2`.
963+
*
964+
* This predicate is only relevant for AST constructs that are not already
965+
* handled by this library.
966+
*/
928967
predicate step(PreControlFlowNode n1, PreControlFlowNode n2);
929968
}
930969

@@ -1596,10 +1635,10 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
15961635
/** Gets an immediate successor of a given type, if any. */
15971636
ControlFlowNode getASuccessor(SuccessorType t) { succ(this, result, t) }
15981637

1599-
/** Gets an immediate successor of this node. */
1638+
/** Gets an immediate successor of this node, if this is not an `ExitNode`. */
16001639
ControlFlowNode getASuccessor() { result = this.getASuccessor(_) }
16011640

1602-
/** Gets an immediate predecessor of this node. */
1641+
/** Gets an immediate predecessor of this node, if this is not an `EntryNode`. */
16031642
ControlFlowNode getAPredecessor() { result.getASuccessor() = this }
16041643

16051644
/**
@@ -1656,11 +1695,11 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
16561695
}
16571696
}
16581697

1659-
import CfgAlias
1698+
module Cfg = BB::Make<Location, BbInput>;
16601699

16611700
private module CfgAlias = Cfg;
16621701

1663-
module Cfg = BB::Make<Location, BbInput>;
1702+
import CfgAlias
16641703
}
16651704

16661705
private module Additional {
@@ -1684,32 +1723,32 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
16841723

16851724
import Pp::PrintGraph<Location, PrintGraphInput>
16861725

1687-
/*
1688-
* Consistency checks
1689-
*
1690-
* - there should be no dead ends except at ExitNodes
1691-
* - inConditionalContext(n, kind) kind must be unique for n
1692-
* - flow must preserve getEnclosingCallable
1693-
* - additionalNode(AstNode n, string tag, NormalSuccessor t) should have a unique t for (n, tag)
1694-
* - if "before" is reachable and node is post-or-in-order, then "in" must generally be reachable
1695-
*/
1696-
16971726
/** Provides a set of consistency queries. */
16981727
module Consistency {
1699-
/** Holds if `node` is lacking a successor. */
1728+
/**
1729+
* Holds if `node` is lacking a successor.
1730+
*
1731+
* There should be no dead ends except at `ExitNode`s.
1732+
*/
17001733
query predicate deadEnd(ControlFlowNode node) {
17011734
not node instanceof ControlFlow::ExitNode and
17021735
not exists(node.getASuccessor(_))
17031736
}
17041737

1705-
/** Holds if `n` is in a conditional context with multiple condition kinds. */
1738+
/**
1739+
* Holds if `n` is in a conditional context with multiple condition kinds.
1740+
*
1741+
* For `inConditionalContext(n, kind)`, the `kind` must be unique for a given `n`.
1742+
*/
17061743
query predicate nonUniqueInConditionalContext(AstNode n) {
17071744
1 < strictcount(ConditionKind kind | inConditionalContext(n, kind))
17081745
}
17091746

17101747
/**
17111748
* Holds if `n1` steps to `n2` with successor type `t` but they belong
17121749
* to different callables.
1750+
*
1751+
* Flow must preserve `getEnclosingCallable`.
17131752
*/
17141753
query predicate nonLocalStep(ControlFlowNode n1, SuccessorType t, ControlFlowNode n2) {
17151754
n1.getASuccessor(t) = n2 and
@@ -1719,12 +1758,21 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
17191758
/**
17201759
* Holds if the additional node for a given AST node and tag has
17211760
* multiple successor types.
1761+
*
1762+
* For `additionalNode(AstNode n, string tag, NormalSuccessor t)`,
1763+
* the `t` must be unique for a given `(n, tag)`.
17221764
*/
17231765
query predicate ambiguousAdditionalNode(AstNode n, string tag) {
17241766
1 < strictcount(NormalSuccessor t | additionalNode(n, tag, t))
17251767
}
17261768

1727-
/** Holds if the "in" node is unreachable for a post-or-in-order AST node. */
1769+
/**
1770+
* Holds if the "in" node is unreachable for a post-or-in-order AST node.
1771+
*
1772+
* If the "before" node of a post-or-in-order AST node is reachable,
1773+
* then the "in" node should generally be reachable as well. If not,
1774+
* it may indicate missing control flow edges.
1775+
*/
17281776
query predicate missingInNodeForPostOrInOrder(AstNode ast) {
17291777
postOrInOrder(ast) and
17301778
exists(ControlFlowNode before | before.isBefore(ast)) and
@@ -1748,7 +1796,13 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
17481796
)
17491797
}
17501798

1751-
/** Holds if `node` has multiple successors of the same type `t`. */
1799+
/**
1800+
* Holds if `node` has multiple successors of the same type `t`.
1801+
*
1802+
* In most cases, multiple successors are distinguished by their
1803+
* successor types, for example the true and false successors of a
1804+
* condition.
1805+
*/
17521806
query predicate multipleSuccessors(
17531807
ControlFlowNode node, SuccessorType t, ControlFlowNode successor
17541808
) {
@@ -1774,7 +1828,13 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
17741828
not (t instanceof DirectSuccessor and node instanceof ControlFlow::EntryNode)
17751829
}
17761830

1777-
/** Holds if `node` has conditional successors of different kinds. */
1831+
/**
1832+
* Holds if `node` has conditional successors of different kinds.
1833+
*
1834+
* The kind of a conditional successor is determined by the context
1835+
* of the node. For example, a condition in an `if` statement has
1836+
* boolean successors.
1837+
*/
17781838
query predicate multipleConditionalSuccessorKinds(
17791839
ControlFlowNode node, ConditionalSuccessor t1, ConditionalSuccessor t2,
17801840
ControlFlowNode succ1, ControlFlowNode succ2
@@ -1784,7 +1844,13 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
17841844
succ2 = node.getASuccessor(t2)
17851845
}
17861846

1787-
/** Holds if `node` has both a direct and a conditional successor type. */
1847+
/**
1848+
* Holds if `node` has both a direct and a conditional successor type.
1849+
*
1850+
* If a node is in a conditional context, it should only have
1851+
* conditional successors of the appropriate kind, and not other
1852+
* normal successors.
1853+
*/
17881854
query predicate directAndConditionalSuccessors(
17891855
ControlFlowNode node, ConditionalSuccessor t1, DirectSuccessor t2,
17901856
ControlFlowNode succ1, ControlFlowNode succ2
@@ -1793,7 +1859,11 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
17931859
succ2 = node.getASuccessor(t2)
17941860
}
17951861

1796-
/** Holds if `node` has a self-loop with successor type `t`. */
1862+
/**
1863+
* Holds if `node` has a self-loop with successor type `t`.
1864+
*
1865+
* Self-loops are not expected in control flow graphs.
1866+
*/
17971867
query predicate selfLoop(ControlFlowNode node, SuccessorType t) {
17981868
node.getASuccessor(t) = node
17991869
}

0 commit comments

Comments
 (0)