Skip to content

Commit 8b58c2c

Browse files
committed
Guards: Add support for wrappers that may throw exceptions.
1 parent e7bdfe1 commit 8b58c2c

File tree

4 files changed

+33
-0
lines changed

4 files changed

+33
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ private module GuardsInput implements SharedGuards::InputSig<Location> {
146146

147147
class ControlFlowNode = J::ControlFlowNode;
148148

149+
class NormalExitNode = ControlFlow::NormalExitNode;
150+
149151
class BasicBlock = J::BasicBlock;
150152

151153
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { J::dominatingEdge(bb1, bb2) }

java/ql/test/library-tests/guards/Guards.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,14 @@ void testWrappers(String s, Integer i) {
202202
break;
203203
}
204204
}
205+
206+
static void ensureNotNull(Object o) throws Exception {
207+
if (o == null) throw new Exception();
208+
}
209+
210+
void testExceptionWrapper(String s) throws Exception {
211+
chk(); // nothing guards here
212+
ensureNotNull(s);
213+
chk(); // $ guarded='ensureNotNull(...):no exception' guarded='s:not null'
214+
}
205215
}

java/ql/test/library-tests/guards/GuardsInline.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,5 @@
112112
| Guards.java:201:9:201:13 | chk(...) | 'testEnumWrapper(...):FAILURE' |
113113
| Guards.java:201:9:201:13 | chk(...) | 'testEnumWrapper(...):match FAILURE' |
114114
| Guards.java:201:9:201:13 | chk(...) | g(1):false |
115+
| Guards.java:213:5:213:9 | chk(...) | 'ensureNotNull(...):no exception' |
116+
| Guards.java:213:5:213:9 | chk(...) | 's:not null' |

shared/controlflow/codeql/controlflow/Guards.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ signature module InputSig<LocationSig Location> {
7979
Location getLocation();
8080
}
8181

82+
/** A control flow node indicating normal termination of a callable. */
83+
class NormalExitNode extends ControlFlowNode;
84+
8285
/**
8386
* A basic block, that is, a maximal straight-line sequence of control flow nodes
8487
* without branches or joins.
@@ -520,6 +523,8 @@ module Make<LocationSig Location, InputSig<Location> Input> {
520523
)
521524
}
522525

526+
private predicate normalExitBlock(BasicBlock bb) { bb.getNode(_) instanceof NormalExitNode }
527+
523528
signature module LogicInputSig {
524529
class SsaDefinition {
525530
/** Gets the basic block to which this SSA definition belongs. */
@@ -1047,6 +1052,13 @@ module Make<LocationSig Location, InputSig<Location> Input> {
10471052
)
10481053
}
10491054

1055+
private predicate guardDirectlyControlsExit(Guard guard, GuardValue val) {
1056+
exists(BasicBlock bb |
1057+
guard.directlyValueControls(bb, val) and
1058+
normalExitBlock(bb)
1059+
)
1060+
}
1061+
10501062
/**
10511063
* Gets a non-overridable method that performs a check on the `ppos`th
10521064
* parameter. A return value equal to `retval` allows us to conclude
@@ -1064,6 +1076,13 @@ module Make<LocationSig Location, InputSig<Location> Input> {
10641076
|
10651077
validReturnInCustomGuard(ret, ppos, retval, val)
10661078
)
1079+
or
1080+
exists(SsaDefinition param, Guard g0, GuardValue v0 |
1081+
parameterDefinition(result.getParameter(ppos), param) and
1082+
guardDirectlyControlsExit(g0, v0) and
1083+
retval = TException(false) and
1084+
BranchImplies::ssaControls(param, val, g0, v0)
1085+
)
10671086
}
10681087

10691088
/**

0 commit comments

Comments
 (0)