Skip to content

Commit 57e15b9

Browse files
authored
Merge pull request #20367 from aschackmull/shared/controlflow
Shared/Java: Introduce a shared control flow reachability library and replace the Java Nullness implementation.
2 parents 6264f46 + acb4d9f commit 57e15b9

File tree

14 files changed

+1230
-651
lines changed

14 files changed

+1230
-651
lines changed

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ module;
8282
*/
8383

8484
import java
85+
private import codeql.controlflow.SuccessorType
8586
private import codeql.util.Boolean
8687
private import Completion
8788
private import controlflow.internal.Preconditions
@@ -124,6 +125,28 @@ module ControlFlow {
124125
result = succ(this, NormalCompletion())
125126
}
126127

128+
/** Gets an immediate successor of this node of a given type, if any. */
129+
Node getASuccessor(SuccessorType t) {
130+
result = branchSuccessor(this, t.(BooleanSuccessor).getValue())
131+
or
132+
exists(Completion completion |
133+
result = succ(this, completion) and
134+
not result = branchSuccessor(this, _)
135+
|
136+
completion = NormalCompletion() and t instanceof DirectSuccessor
137+
or
138+
completion = ReturnCompletion() and t instanceof ReturnSuccessor
139+
or
140+
completion = BreakCompletion(_) and t instanceof BreakSuccessor
141+
or
142+
completion = YieldCompletion(_) and t instanceof BreakSuccessor
143+
or
144+
completion = ContinueCompletion(_) and t instanceof ContinueSuccessor
145+
or
146+
completion = ThrowCompletion(_) and t instanceof ExceptionSuccessor
147+
)
148+
}
149+
127150
/** Gets the basic block that contains this node. */
128151
BasicBlock getBasicBlock() { result.getANode() = this }
129152

java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,8 @@ private module Input implements BB::InputSig<Location> {
2222
/** Gets the CFG scope in which this node occurs. */
2323
CfgScope nodeGetCfgScope(Node node) { node.getEnclosingCallable() = result }
2424

25-
private Node getASpecificSuccessor(Node node, SuccessorType t) {
26-
node.(ConditionNode).getABranchSuccessor(t.(BooleanSuccessor).getValue()) = result
27-
or
28-
node.getAnExceptionSuccessor() = result and t instanceof ExceptionSuccessor
29-
}
30-
3125
/** Gets an immediate successor of this node. */
32-
Node nodeGetASuccessor(Node node, SuccessorType t) {
33-
result = getASpecificSuccessor(node, t)
34-
or
35-
node.getASuccessor() = result and
36-
t instanceof DirectSuccessor and
37-
not result = getASpecificSuccessor(node, _)
38-
}
26+
Node nodeGetASuccessor(Node node, SuccessorType t) { result = node.getASuccessor(t) }
3927

4028
/**
4129
* Holds if `node` represents an entry node to be used when calculating
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Provides an implementation of local (intraprocedural) control flow reachability.
3+
*/
4+
overlay[local?]
5+
module;
6+
7+
import java
8+
private import codeql.controlflow.ControlFlow
9+
private import semmle.code.java.dataflow.SSA as SSA
10+
private import semmle.code.java.controlflow.Guards as Guards
11+
12+
private module ControlFlowInput implements InputSig<Location, ControlFlowNode, BasicBlock> {
13+
private import java as J
14+
15+
AstNode getEnclosingAstNode(ControlFlowNode node) { node.getAstNode() = result }
16+
17+
class AstNode = ExprParent;
18+
19+
AstNode getParent(AstNode node) {
20+
result = node.(Expr).getParent() or
21+
result = node.(Stmt).getParent()
22+
}
23+
24+
class FinallyBlock extends AstNode {
25+
FinallyBlock() { any(TryStmt try).getFinally() = this }
26+
}
27+
28+
class Expr = J::Expr;
29+
30+
class SourceVariable = SSA::SsaSourceVariable;
31+
32+
class SsaDefinition = SSA::SsaVariable;
33+
34+
class SsaWriteDefinition extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
35+
Expr getDefinition() {
36+
super.getDefiningExpr().(VariableAssign).getSource() = result or
37+
super.getDefiningExpr().(AssignOp) = result
38+
}
39+
}
40+
41+
class SsaPhiNode = SSA::SsaPhiNode;
42+
43+
class SsaUncertainDefinition extends SsaDefinition instanceof SSA::SsaUncertainImplicitUpdate {
44+
SsaDefinition getPriorDefinition() { result = super.getPriorDef() }
45+
}
46+
47+
class GuardValue = Guards::GuardValue;
48+
49+
predicate ssaControlsBranchEdge(SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v) {
50+
Guards::Guards_v3::ssaControlsBranchEdge(def, bb1, bb2, v)
51+
}
52+
53+
predicate ssaControls(SsaDefinition def, BasicBlock bb, GuardValue v) {
54+
Guards::Guards_v3::ssaControls(def, bb, v)
55+
}
56+
57+
import Guards::Guards_v3::InternalUtil
58+
}
59+
60+
module ControlFlow = Make<Location, Cfg, ControlFlowInput>;

java/ql/lib/semmle/code/java/dataflow/IntegerGuards.qll

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private import RangeUtils
1010
private import RangeAnalysis
1111

1212
/** Gets an expression that might have the value `i`. */
13-
private Expr exprWithIntValue(int i) {
13+
deprecated private Expr exprWithIntValue(int i) {
1414
result.(ConstantIntegerExpr).getIntValue() = i or
1515
result.(ChooseExpr).getAResultExpr() = exprWithIntValue(i)
1616
}
@@ -19,11 +19,11 @@ private Expr exprWithIntValue(int i) {
1919
* An expression for which the predicate `integerGuard` is relevant.
2020
* This includes `VarRead` and `MethodCall`.
2121
*/
22-
class IntComparableExpr extends Expr {
22+
deprecated class IntComparableExpr extends Expr {
2323
IntComparableExpr() { this instanceof VarRead or this instanceof MethodCall }
2424

2525
/** Gets an integer that is directly assigned to the expression in case of a variable; or zero. */
26-
int relevantInt() {
26+
deprecated int relevantInt() {
2727
exists(SsaExplicitUpdate ssa, SsaSourceVariable v |
2828
this = v.getAnAccess() and
2929
ssa.getSourceVariable() = v and
@@ -55,14 +55,18 @@ private predicate comparison(ComparisonExpr comp, boolean branch, Expr e1, Expr
5555
* Holds if `guard` evaluating to `branch` ensures that:
5656
* `e <= k` when `upper = true`
5757
* `e >= k` when `upper = false`
58+
*
59+
* Does _not_ include the constant comparison case where the guard directly
60+
* ensures `e == k`.
5861
*/
5962
pragma[nomagic]
6063
predicate rangeGuard(Expr guard, boolean branch, Expr e, int k, boolean upper) {
6164
exists(EqualityTest eqtest, Expr c |
6265
eqtest = guard and
6366
eqtest.hasOperands(e, c) and
6467
bounded(c, any(ZeroBound zb), k, upper, _) and
65-
branch = eqtest.polarity()
68+
branch = eqtest.polarity() and
69+
not c instanceof ConstantIntegerExpr
6670
)
6771
or
6872
exists(Expr c, int val, boolean strict, int d |
@@ -87,6 +91,30 @@ predicate rangeGuard(Expr guard, boolean branch, Expr e, int k, boolean upper) {
8791
}
8892

8993
/**
94+
* Gets an expression that directly tests whether a given expression, `e`, is
95+
* non-zero.
96+
*/
97+
Expr nonZeroGuard(Expr e, boolean branch) {
98+
exists(EqualityTest eqtest, boolean polarity, int k |
99+
eqtest = result and
100+
eqtest.hasOperands(e, any(ConstantIntegerExpr c | c.getIntValue() = k)) and
101+
polarity = eqtest.polarity()
102+
|
103+
k = 0 and branch = polarity.booleanNot()
104+
or
105+
k != 0 and branch = polarity
106+
)
107+
or
108+
exists(int val, boolean upper | rangeGuard(result, branch, e, val, upper) |
109+
upper = true and val < 0 // e <= val < 0 ==> e != 0
110+
or
111+
upper = false and val > 0 // e >= val > 0 ==> e != 0
112+
)
113+
}
114+
115+
/**
116+
* DEPRECATED.
117+
*
90118
* An expression that directly tests whether a given expression is equal to `k` or not.
91119
* The set of `k`s is restricted to those that are relevant for the expression or
92120
* have a direct comparison with the expression.
@@ -95,7 +123,7 @@ predicate rangeGuard(Expr guard, boolean branch, Expr e, int k, boolean upper) {
95123
* is true, and different from `k` if `is_k` is false.
96124
*/
97125
pragma[nomagic]
98-
Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
126+
deprecated Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
99127
exists(EqualityTest eqtest, boolean polarity |
100128
eqtest = result and
101129
eqtest.hasOperands(e, any(ConstantIntegerExpr c | c.getIntValue() = k)) and
@@ -119,13 +147,15 @@ Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
119147
}
120148

121149
/**
150+
* DEPRECATED: Use `rangeGuard` instead.
151+
*
122152
* A guard that splits the values of a variable into one range with an upper bound of `k-1`
123153
* and one with a lower bound of `k`.
124154
*
125155
* If `branch_with_lower_bound_k` is true then `result` is equivalent to `k <= x`
126156
* and if it is false then `result` is equivalent to `k > x`.
127157
*/
128-
Expr intBoundGuard(VarRead x, boolean branch_with_lower_bound_k, int k) {
158+
deprecated Expr intBoundGuard(VarRead x, boolean branch_with_lower_bound_k, int k) {
129159
exists(ComparisonExpr comp, ConstantIntegerExpr c, int val |
130160
comp = result and
131161
comp.hasOperands(x, c) and

0 commit comments

Comments
 (0)