Skip to content

Commit b67394a

Browse files
authored
Merge pull request #20183 from aschackmull/java/barrierguard-wrappers
Java: Enable BarrierGuard wrappers
2 parents 5c0300c + 492a5ca commit b67394a

File tree

7 files changed

+77
-127
lines changed

7 files changed

+77
-127
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,12 +347,28 @@ private module ControlFlowGraphImpl {
347347
)
348348
}
349349

350+
private predicate methodMayThrow(Method m, ThrowableType t) {
351+
exists(AstNode n |
352+
t = n.(ThrowStmt).getThrownExceptionType() and
353+
not n.(ThrowStmt).getParent() = any(Method m0).getBody()
354+
or
355+
uncheckedExceptionFromMethod(n, t)
356+
|
357+
n.getEnclosingStmt().getEnclosingCallable() = m and
358+
not exists(TryStmt try |
359+
exists(try.getACatchClause()) and try.getBlock() = n.getEnclosingStmt().getEnclosingStmt*()
360+
)
361+
)
362+
}
363+
350364
/**
351-
* Bind `t` to an unchecked exception that may occur in a precondition check.
365+
* Bind `t` to an unchecked exception that may occur in a precondition check or guard wrapper.
352366
*/
353367
private predicate uncheckedExceptionFromMethod(MethodCall ma, ThrowableType t) {
354368
conditionCheckArgument(ma, _, _) and
355369
(t instanceof TypeError or t instanceof TypeRuntimeException)
370+
or
371+
methodMayThrow(ma.getMethod(), t)
356372
}
357373

358374
/**

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,15 @@ class Callable extends StmtParent, Member, @callable {
256256
Exception getAnException() { exceptions(result, _, this) }
257257

258258
/** Gets an exception type that occurs in the `throws` clause of this callable. */
259-
RefType getAThrownExceptionType() { result = this.getAnException().getType() }
259+
RefType getAThrownExceptionType() {
260+
result = this.getAnException().getType()
261+
or
262+
exists(Annotation a |
263+
this.getAnAnnotation() = a and
264+
a.getType().hasQualifiedName("kotlin.jvm", "Throws") and
265+
a.getATypeArrayValue(_) = result
266+
)
267+
}
260268

261269
/** Gets a call site that references this callable. */
262270
Call getAReference() { result.getCallee() = this }

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -490,12 +490,6 @@ module Guards_v2 = GuardsImpl::Logic<LogicInput_v2>;
490490
/** INTERNAL: Don't use. */
491491
module Guards_v3 = GuardsImpl::Logic<LogicInput_v3>;
492492

493-
/** INTERNAL: Don't use. */
494-
predicate implies_v3(Guard g1, boolean b1, Guard g2, boolean b2) {
495-
Guards_v3::boolImplies(g1, any(GuardValue v | v.asBooleanValue() = b1), g2,
496-
any(GuardValue v | v.asBooleanValue() = b2))
497-
}
498-
499493
/**
500494
* A guard. This may be any expression whose value determines subsequent
501495
* control flow. It may also be a switch case, which as a guard is considered

java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -562,14 +562,20 @@ private module Cached {
562562

563563
cached // nothing is actually cached
564564
module BarrierGuard<guardChecksSig/3 guardChecks> {
565-
private predicate guardChecksAdjTypes(
566-
DataFlowIntegrationInput::Guard g, DataFlowIntegrationInput::Expr e, Guards::GuardValue val
565+
private predicate guardChecksAdjTypes(Guards::Guards_v3::Guard g, Expr e, boolean branch) {
566+
guardChecks(g, e, branch)
567+
}
568+
569+
private predicate guardChecksWithWrappers(
570+
DataFlowIntegrationInput::Guard g, Definition def, Guards::GuardValue val, Unit state
567571
) {
568-
guardChecks(g, e, val.asBooleanValue())
572+
Guards::Guards_v3::ValidationWrapper<guardChecksAdjTypes/3>::guardChecksDef(g, def, val) and
573+
exists(state)
569574
}
570575

571576
private Node getABarrierNodeImpl() {
572-
result = DataFlowIntegrationImpl::BarrierGuard<guardChecksAdjTypes/3>::getABarrierNode()
577+
result =
578+
DataFlowIntegrationImpl::BarrierGuardDefWithState<Unit, guardChecksWithWrappers/4>::getABarrierNode(_)
573579
}
574580

575581
predicate getABarrierNode = getABarrierNodeImpl/0;

java/ql/lib/semmle/code/java/security/ArithmeticCommon.qll

Lines changed: 21 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ predicate narrowerThanOrEqualTo(ArithExpr exp, NumType numType) {
2020
exists(CastingExpr cast | cast.getAChildExpr() = exp | numType.widerThanOrEqualTo(cast.getType()))
2121
}
2222

23-
private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
23+
private Guard sizeGuard(Expr e, boolean branch, boolean upper) {
2424
exists(ComparisonExpr comp | comp = result |
25-
comp.getLesserOperand() = ssaRead(v, 0) and
25+
comp.getLesserOperand() = e and
2626
(
2727
branch = true and upper = true
2828
or
2929
branch = false and upper = false
3030
)
3131
or
32-
comp.getGreaterOperand() = ssaRead(v, 0) and
32+
comp.getGreaterOperand() = e and
3333
(
3434
branch = true and upper = false
3535
or
@@ -38,7 +38,7 @@ private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
3838
or
3939
exists(MethodCall ma |
4040
ma.getMethod() instanceof MethodAbs and
41-
ma.getArgument(0) = ssaRead(v, 0) and
41+
ma.getArgument(0) = e and
4242
(
4343
comp.getLesserOperand() = ma and branch = true
4444
or
@@ -49,7 +49,7 @@ private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
4949
or
5050
// overflow test
5151
exists(AddExpr add, VarRead use, Expr pos |
52-
use = ssaRead(v, 0) and
52+
use = e and
5353
add.hasOperands(use, pos) and
5454
positive(use) and
5555
positive(pos) and
@@ -65,70 +65,38 @@ private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
6565
)
6666
)
6767
or
68-
result.isEquality(ssaRead(v, 0), _, branch) and
68+
result.isEquality(e, _, branch) and
6969
(upper = true or upper = false)
70-
or
71-
exists(MethodCall call, Method m, int ix |
72-
call = result and
73-
call.getArgument(ix) = ssaRead(v, 0) and
74-
call.getMethod().getSourceDeclaration() = m and
75-
m = customSizeGuard(ix, branch, upper)
76-
)
7770
}
7871

79-
private Guard derivedSizeGuard(SsaVariable v, boolean branch, boolean upper) {
80-
result = sizeGuard(v, branch, upper) or
81-
exists(boolean branch0 | implies_v3(result, branch, derivedSizeGuard(v, branch0, upper), branch0))
72+
private predicate sizeGuardLessThan(Guard g, Expr e, boolean branch) {
73+
g = sizeGuard(e, branch, true)
8274
}
8375

84-
private Method customSizeGuard(int index, boolean retval, boolean upper) {
85-
exists(Parameter p, SsaImplicitInit v |
86-
result.getReturnType().(PrimitiveType).hasName("boolean") and
87-
not result.isOverridable() and
88-
p.getCallable() = result and
89-
not p.isVarargs() and
90-
p.getType() instanceof NumericOrCharType and
91-
p.getPosition() = index and
92-
v.isParameterDefinition(p) and
93-
forex(ReturnStmt ret |
94-
ret.getEnclosingCallable() = result and
95-
exists(Expr res | res = ret.getResult() |
96-
not res.(BooleanLiteral).getBooleanValue() = retval.booleanNot()
97-
)
98-
|
99-
ret.getResult() = derivedSizeGuard(v, retval, upper)
100-
)
101-
)
76+
private predicate sizeGuardGreaterThan(Guard g, Expr e, boolean branch) {
77+
g = sizeGuard(e, branch, false)
10278
}
10379

10480
/**
105-
* Holds if `e` is bounded in a way that is likely to prevent overflow.
81+
* Holds if `n` is bounded in a way that is likely to prevent overflow.
10682
*/
107-
predicate guardedLessThanSomething(Expr e) {
108-
exists(SsaVariable v, Guard guard, boolean branch |
109-
e = v.getAUse() and
110-
guard = sizeGuard(v.getAPhiInputOrPriorDef*(), branch, true) and
111-
guard.controls(e.getBasicBlock(), branch)
112-
)
83+
predicate guardedLessThanSomething(DataFlow::Node n) {
84+
DataFlow::BarrierGuard<sizeGuardLessThan/3>::getABarrierNode() = n
11385
or
114-
negative(e)
86+
negative(n.asExpr())
11587
or
116-
e.(MethodCall).getMethod() instanceof MethodMathMin
88+
n.asExpr().(MethodCall).getMethod() instanceof MethodMathMin
11789
}
11890

11991
/**
12092
* Holds if `e` is bounded in a way that is likely to prevent underflow.
12193
*/
122-
predicate guardedGreaterThanSomething(Expr e) {
123-
exists(SsaVariable v, Guard guard, boolean branch |
124-
e = v.getAUse() and
125-
guard = sizeGuard(v.getAPhiInputOrPriorDef*(), branch, false) and
126-
guard.controls(e.getBasicBlock(), branch)
127-
)
94+
predicate guardedGreaterThanSomething(DataFlow::Node n) {
95+
DataFlow::BarrierGuard<sizeGuardGreaterThan/3>::getABarrierNode() = n
12896
or
129-
positive(e)
97+
positive(n.asExpr())
13098
or
131-
e.(MethodCall).getMethod() instanceof MethodMathMax
99+
n.asExpr().(MethodCall).getMethod() instanceof MethodMathMax
132100
}
133101

134102
/** Holds if `e` occurs in a context where it will be upcast to a wider type. */
@@ -182,7 +150,7 @@ private predicate unlikelyNode(DataFlow::Node n) {
182150
/** Holds if `n` is likely guarded against overflow. */
183151
predicate overflowBarrier(DataFlow::Node n) {
184152
n.getType() instanceof BooleanType or
185-
guardedLessThanSomething(n.asExpr()) or
153+
guardedLessThanSomething(n) or
186154
unlikelyNode(n) or
187155
upcastToWiderType(n.asExpr()) or
188156
overflowIrrelevant(n.asExpr())
@@ -191,7 +159,7 @@ predicate overflowBarrier(DataFlow::Node n) {
191159
/** Holds if `n` is likely guarded against underflow. */
192160
predicate underflowBarrier(DataFlow::Node n) {
193161
n.getType() instanceof BooleanType or
194-
guardedGreaterThanSomething(n.asExpr()) or
162+
guardedGreaterThanSomething(n) or
195163
unlikelyNode(n) or
196164
upcastToWiderType(n.asExpr()) or
197165
overflowIrrelevant(n.asExpr())
@@ -210,7 +178,6 @@ predicate overflowSink(ArithExpr exp, VarAccess use) {
210178
exp instanceof PostIncExpr or
211179
exp instanceof MulExpr
212180
) and
213-
not guardedLessThanSomething(use) and
214181
// Exclude widening conversions of tainted values due to binary numeric promotion (JLS 5.6.2)
215182
// unless there is an enclosing cast down to a narrower type.
216183
narrowerThanOrEqualTo(exp, use.getType()) and
@@ -230,7 +197,6 @@ predicate underflowSink(ArithExpr exp, VarAccess use) {
230197
exp instanceof PostDecExpr or
231198
exp instanceof MulExpr
232199
) and
233-
not guardedGreaterThanSomething(use) and
234200
// Exclude widening conversions of tainted values due to binary numeric promotion (JLS 5.6.2)
235201
// unless there is an enclosing cast down to a narrower type.
236202
narrowerThanOrEqualTo(exp, use.getType()) and

java/ql/lib/semmle/code/java/security/PathSanitizer.qll

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,6 @@ private import semmle.code.java.dataflow.Nullness
1313
/** A sanitizer that protects against path injection vulnerabilities. */
1414
abstract class PathInjectionSanitizer extends DataFlow::Node { }
1515

16-
/**
17-
* Provides a set of nodes validated by a method that uses a validation guard.
18-
*/
19-
private module ValidationMethod<DataFlow::guardChecksSig/3 validationGuard> {
20-
/** Gets a node that is safely guarded by a method that uses the given guard check. */
21-
DataFlow::Node getAValidatedNode() {
22-
exists(MethodCall ma, int pos, VarRead rv |
23-
validationMethod(ma.getMethod(), pos) and
24-
ma.getArgument(pos) = rv and
25-
adjacentUseUseSameVar(rv, result.asExpr()) and
26-
ma.getBasicBlock().dominates(result.asExpr().getBasicBlock())
27-
)
28-
}
29-
30-
/**
31-
* Holds if `m` validates its `arg`th parameter by using `validationGuard`.
32-
*/
33-
private predicate validationMethod(Method m, int arg) {
34-
exists(Guard g, SsaImplicitInit var, ControlFlow::NormalExitNode normexit, boolean branch |
35-
validationGuard(g, var.getAUse(), branch) and
36-
var.isParameterDefinition(m.getParameter(arg)) and
37-
normexit.getEnclosingCallable() = m and
38-
g.controls(normexit.getBasicBlock(), branch)
39-
)
40-
}
41-
}
42-
4316
/**
4417
* Holds if `g` is guard that compares a path to a trusted value.
4518
*/
@@ -68,8 +41,6 @@ private predicate exactPathMatchGuard(Guard g, Expr e, boolean branch) {
6841
class ExactPathMatchSanitizer extends PathInjectionSanitizer {
6942
ExactPathMatchSanitizer() {
7043
this = DataFlow::BarrierGuard<exactPathMatchGuard/3>::getABarrierNode()
71-
or
72-
this = ValidationMethod<exactPathMatchGuard/3>::getAValidatedNode()
7344
}
7445
}
7546

@@ -120,8 +91,7 @@ private predicate allowedPrefixGuard(Guard g, Expr e, boolean branch) {
12091

12192
private class AllowedPrefixSanitizer extends PathInjectionSanitizer {
12293
AllowedPrefixSanitizer() {
123-
this = DataFlow::BarrierGuard<allowedPrefixGuard/3>::getABarrierNode() or
124-
this = ValidationMethod<allowedPrefixGuard/3>::getAValidatedNode()
94+
this = DataFlow::BarrierGuard<allowedPrefixGuard/3>::getABarrierNode()
12595
}
12696
}
12797

@@ -139,10 +109,7 @@ private predicate dotDotCheckGuard(Guard g, Expr e, boolean branch) {
139109
}
140110

141111
private class DotDotCheckSanitizer extends PathInjectionSanitizer {
142-
DotDotCheckSanitizer() {
143-
this = DataFlow::BarrierGuard<dotDotCheckGuard/3>::getABarrierNode() or
144-
this = ValidationMethod<dotDotCheckGuard/3>::getAValidatedNode()
145-
}
112+
DotDotCheckSanitizer() { this = DataFlow::BarrierGuard<dotDotCheckGuard/3>::getABarrierNode() }
146113
}
147114

148115
private class BlockListGuard extends PathGuard instanceof MethodCall {
@@ -179,10 +146,7 @@ private predicate blockListGuard(Guard g, Expr e, boolean branch) {
179146
}
180147

181148
private class BlockListSanitizer extends PathInjectionSanitizer {
182-
BlockListSanitizer() {
183-
this = DataFlow::BarrierGuard<blockListGuard/3>::getABarrierNode() or
184-
this = ValidationMethod<blockListGuard/3>::getAValidatedNode()
185-
}
149+
BlockListSanitizer() { this = DataFlow::BarrierGuard<blockListGuard/3>::getABarrierNode() }
186150
}
187151

188152
private class ConstantOrRegex extends Expr {
@@ -368,7 +332,6 @@ private class FileConstructorChildArgumentStep extends AdditionalTaintStep {
368332
n2.asExpr() = constrCall
369333
|
370334
not n1 = DataFlow::BarrierGuard<pathTraversalGuard/3>::getABarrierNode() and
371-
not n1 = ValidationMethod<pathTraversalGuard/3>::getAValidatedNode() and
372335
not TaintTracking::localExprTaint(any(PathNormalizeSanitizer p), n1.asExpr())
373336
or
374337
DataFlow::localExprFlow(nullExpr(), constrCall.getArgument(0))
@@ -546,7 +509,6 @@ private predicate directoryCharactersGuard(Guard g, Expr e, boolean branch) {
546509
private class DirectoryCharactersSanitizer extends PathInjectionSanitizer {
547510
DirectoryCharactersSanitizer() {
548511
this.asExpr() instanceof ReplaceDirectoryCharactersSanitizer or
549-
this = DataFlow::BarrierGuard<directoryCharactersGuard/3>::getABarrierNode() or
550-
this = ValidationMethod<directoryCharactersGuard/3>::getAValidatedNode()
512+
this = DataFlow::BarrierGuard<directoryCharactersGuard/3>::getABarrierNode()
551513
}
552514
}

0 commit comments

Comments
 (0)