Skip to content

Commit 3313af5

Browse files
authored
Merge pull request github#2036 from aschackmull/java/eq-ssa-guard
Java: Improve guards for equal ssa variables.
2 parents e5380aa + 0154e31 commit 3313af5

File tree

5 files changed

+58
-29
lines changed

5 files changed

+58
-29
lines changed

change-notes/1.23/analysis-java.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The following changes in version 1.23 affect Java analysis in all applications.
66

77
| **Query** | **Expected impact** | **Change** |
88
|------------------------------|------------------------|-----------------------------------|
9+
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Certain indirect null guards involving two auxiliary variables known to be equal can now be detected. |
910
| Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
1011
| Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
1112
| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |

java/ql/src/semmle/code/java/controlflow/Guards.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ class Guard extends ExprParent {
9797
result = this.(SwitchCase).getSwitchExpr().getEnclosingStmt()
9898
}
9999

100+
/**
101+
* Gets the basic block containing this guard or the basic block containing
102+
* the switch expression if the guard is a switch case.
103+
*/
104+
BasicBlock getBasicBlock() {
105+
result = this.(Expr).getBasicBlock() or
106+
result = this.(SwitchCase).getSwitch().getExpr().getProperExpr().getBasicBlock() or
107+
result = this.(SwitchCase).getSwitchExpr().getExpr().getProperExpr().getBasicBlock()
108+
}
109+
100110
/**
101111
* Holds if this guard is an equality test between `e1` and `e2`. The test
102112
* can be either `==`, `!=`, `.equals`, or a switch case. If the test is

java/ql/src/semmle/code/java/controlflow/internal/GuardsLogic.qll

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ predicate implies_v2(Guard g1, boolean b1, Guard g2, boolean b2) {
107107
uniqueValue(v, e, k) and
108108
guardImpliesEqual(g1, b1, v, k) and
109109
g2.directlyControls(e.getBasicBlock(), b2) and
110-
not g2.directlyControls(getBasicBlockOfGuard(g1), b2)
110+
not g2.directlyControls(g1.getBasicBlock(), b2)
111111
)
112112
}
113113

@@ -272,15 +272,23 @@ private predicate uniqueValue(SsaVariable v, Expr e, AbstractValue k) {
272272
}
273273

274274
/**
275-
* Holds if `guard` evaluating to `branch` implies that `v` equals `k`.
275+
* Holds if `v1` and `v2` have the same value in `bb`.
276276
*/
277-
private predicate guardImpliesEqual(Guard guard, boolean branch, SsaVariable v, AbstractValue k) {
278-
guard.isEquality(v.getAUse(), k.getExpr(), branch)
277+
private predicate equalVarsInBlock(BasicBlock bb, SsaVariable v1, SsaVariable v2) {
278+
exists(Guard guard, boolean branch |
279+
guard.isEquality(v1.getAUse(), v2.getAUse(), branch) and
280+
guardControls_v1(guard, bb, branch)
281+
)
279282
}
280283

281-
private BasicBlock getBasicBlockOfGuard(Guard g) {
282-
result = g.(Expr).getControlFlowNode().getBasicBlock() or
283-
result = g.(SwitchCase).getSwitch().getExpr().getProperExpr().getControlFlowNode().getBasicBlock()
284+
/**
285+
* Holds if `guard` evaluating to `branch` implies that `v` equals `k`.
286+
*/
287+
private predicate guardImpliesEqual(Guard guard, boolean branch, SsaVariable v, AbstractValue k) {
288+
exists(SsaVariable v0 |
289+
guard.isEquality(v0.getAUse(), k.getExpr(), branch) and
290+
(v = v0 or equalVarsInBlock(guard.getBasicBlock(), v0, v))
291+
)
284292
}
285293

286294
private ControlFlowNode getAGuardBranchSuccessor(Guard g, boolean branch) {
@@ -299,7 +307,7 @@ private predicate guardControlsPhiBranch(
299307
guard.directlyControls(upd.getBasicBlock(), branch) and
300308
upd.getDefiningExpr().(VariableAssign).getSource().getProperExpr() = e and
301309
upd = phi.getAPhiInput() and
302-
getBasicBlockOfGuard(guard).bbStrictlyDominates(phi.getBasicBlock())
310+
guard.getBasicBlock().bbStrictlyDominates(phi.getBasicBlock())
303311
}
304312

305313
/**
@@ -326,7 +334,7 @@ private predicate conditionalAssign(SsaVariable v, Guard guard, boolean branch,
326334
forall(SsaVariable other | other != upd and other = phi.getAPhiInput() |
327335
guard.directlyControls(other.getBasicBlock(), branch.booleanNot())
328336
or
329-
other.getBasicBlock().bbDominates(getBasicBlockOfGuard(guard)) and
337+
other.getBasicBlock().bbDominates(guard.getBasicBlock()) and
330338
not other.isLiveAtEndOfBlock(getAGuardBranchSuccessor(guard, branch))
331339
)
332340
)
@@ -341,6 +349,11 @@ private predicate conditionalAssignVal(SsaVariable v, Guard guard, boolean branc
341349

342350
private predicate relevantEq(SsaVariable v, AbstractValue val) {
343351
conditionalAssignVal(v, _, _, val)
352+
or
353+
exists(SsaVariable v0 |
354+
conditionalAssignVal(v0, _, _, val) and
355+
equalVarsInBlock(_, v0, v)
356+
)
344357
}
345358

346359
/**
@@ -349,16 +362,19 @@ private predicate relevantEq(SsaVariable v, AbstractValue val) {
349362
private predicate guardImpliesNotEqual1(
350363
Guard guard, boolean branch, SsaVariable v, AbstractValue val
351364
) {
352-
relevantEq(v, val) and
353-
(
354-
guard.isEquality(v.getAUse(), val.getExpr(), branch.booleanNot())
355-
or
356-
exists(AbstractValue val2 |
357-
guard.isEquality(v.getAUse(), val2.getExpr(), branch) and
358-
val != val2
359-
)
360-
or
361-
guard.(InstanceOfExpr).getExpr() = sameValue(v, _) and branch = true and val = TAbsValNull()
365+
exists(SsaVariable v0 |
366+
relevantEq(v0, val) and
367+
(
368+
guard.isEquality(v0.getAUse(), val.getExpr(), branch.booleanNot())
369+
or
370+
exists(AbstractValue val2 |
371+
guard.isEquality(v0.getAUse(), val2.getExpr(), branch) and
372+
val != val2
373+
)
374+
or
375+
guard.(InstanceOfExpr).getExpr() = sameValue(v0, _) and branch = true and val = TAbsValNull()
376+
) and
377+
(v = v0 or equalVarsInBlock(guard.getBasicBlock(), v0, v))
362378
)
363379
}
364380

@@ -368,13 +384,16 @@ private predicate guardImpliesNotEqual1(
368384
private predicate guardImpliesNotEqual2(
369385
Guard guard, boolean branch, SsaVariable v, AbstractValue val
370386
) {
371-
relevantEq(v, val) and
372-
(
373-
guard = directNullGuard(v, branch, false) and val = TAbsValNull()
374-
or
375-
exists(int k |
376-
guard = integerGuard(v.getAUse(), branch, k, false) and
377-
val = TAbsValInt(k)
378-
)
387+
exists(SsaVariable v0 |
388+
relevantEq(v0, val) and
389+
(
390+
guard = directNullGuard(v0, branch, false) and val = TAbsValNull()
391+
or
392+
exists(int k |
393+
guard = integerGuard(v0.getAUse(), branch, k, false) and
394+
val = TAbsValInt(k)
395+
)
396+
) and
397+
(v = v0 or equalVarsInBlock(guard.getBasicBlock(), v0, v))
379398
)
380399
}

java/ql/test/query-tests/Nullness/B.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public void lengthGuard(int[] a, int[] b) {
101101
if (alen == blen) {
102102
for(int i = 0; i < alen; i++) {
103103
sum += a[i]; // OK
104-
sum += b[i]; // NPE - false positive
104+
sum += b[i]; // OK
105105
}
106106
}
107107
int alen2;

java/ql/test/query-tests/Nullness/NullMaybe.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
| B.java:72:15:72:16 | xs | Variable $@ may be null here because of $@ assignment. | B.java:68:5:68:41 | int[] xs | xs | B.java:68:11:68:40 | xs | this |
1313
| B.java:75:20:75:21 | xs | Variable $@ may be null here because of $@ assignment. | B.java:68:5:68:41 | int[] xs | xs | B.java:68:11:68:40 | xs | this |
1414
| B.java:78:20:78:21 | xs | Variable $@ may be null here because of $@ assignment. | B.java:68:5:68:41 | int[] xs | xs | B.java:68:11:68:40 | xs | this |
15-
| B.java:104:16:104:16 | b | Variable $@ may be null here as suggested by $@ null guard. | B.java:97:36:97:42 | b | b | B.java:99:16:99:24 | ... == ... | this |
1615
| B.java:118:5:118:7 | obj | Variable $@ may be null here as suggested by $@ null guard. | B.java:117:27:117:36 | obj | obj | B.java:119:13:119:23 | ... != ... | this |
1716
| B.java:133:5:133:7 | obj | Variable $@ may be null here because of $@ assignment. | B.java:128:5:128:22 | Object obj | obj | B.java:128:12:128:21 | obj | this |
1817
| B.java:190:7:190:7 | o | Variable $@ may be null here because of $@ assignment. | B.java:178:5:178:20 | Object o | o | B.java:186:5:186:12 | ...=... | this |

0 commit comments

Comments
 (0)