mirror of
https://github.com/github/codeql.git
synced 2026-02-11 20:51:06 +01:00
278 lines
8.1 KiB
Plaintext
278 lines
8.1 KiB
Plaintext
/**
|
|
* Provides classes and predicates for null guards.
|
|
*/
|
|
overlay[local?]
|
|
module;
|
|
|
|
import java
|
|
import SSA
|
|
private import semmle.code.java.controlflow.Guards
|
|
private import semmle.code.java.frameworks.apache.Collections
|
|
private import IntegerGuards
|
|
|
|
/** Gets an expression that is always `null`. */
|
|
Expr alwaysNullExpr() {
|
|
result instanceof NullLiteral or
|
|
result.(CastingExpr).getExpr() = alwaysNullExpr()
|
|
}
|
|
|
|
/** Gets an equality test between an expression `e` and an enum constant `c`. */
|
|
Expr enumConstEquality(Expr e, boolean polarity, EnumConstant c) {
|
|
exists(EqualityTest eqtest |
|
|
eqtest = result and
|
|
eqtest.hasOperands(e, c.getAnAccess()) and
|
|
polarity = eqtest.polarity()
|
|
)
|
|
}
|
|
|
|
/** Gets an instanceof expression of `v` with type `type` */
|
|
InstanceOfExpr instanceofExpr(SsaDefinition v, RefType type) {
|
|
result.getCheckedType() = type and
|
|
result.getExpr() = v.getARead()
|
|
}
|
|
|
|
/**
|
|
* Gets an expression of the form `v1 == v2` or `v1 != v2`.
|
|
* The predicate is symmetric in `v1` and `v2`.
|
|
*
|
|
* Note this includes Kotlin's `==` and `!=` operators, which are value-equality tests.
|
|
*/
|
|
EqualityTest varEqualityTestExpr(SsaDefinition v1, SsaDefinition v2, boolean isEqualExpr) {
|
|
result.hasOperands(v1.getARead(), v2.getARead()) and
|
|
isEqualExpr = result.polarity()
|
|
}
|
|
|
|
/** Gets an expression that is provably not `null`. */
|
|
Expr baseNotNullExpr() {
|
|
result instanceof ClassInstanceExpr
|
|
or
|
|
result instanceof ArrayCreationExpr
|
|
or
|
|
result instanceof TypeLiteral
|
|
or
|
|
result instanceof ThisAccess
|
|
or
|
|
result instanceof StringLiteral
|
|
or
|
|
result instanceof AddExpr and result.getType() instanceof TypeString
|
|
or
|
|
exists(Field f |
|
|
result = f.getAnAccess() and
|
|
(f.hasName("TRUE") or f.hasName("FALSE")) and
|
|
f.getDeclaringType().hasQualifiedName("java.lang", "Boolean")
|
|
)
|
|
or
|
|
result = any(EnumConstant c).getAnAccess()
|
|
or
|
|
result instanceof ImplicitNotNullExpr
|
|
or
|
|
result instanceof ImplicitCoercionToUnitExpr
|
|
or
|
|
result
|
|
.(MethodCall)
|
|
.getMethod()
|
|
.hasQualifiedName("com.google.common.base", "Strings", "nullToEmpty")
|
|
}
|
|
|
|
/** Gets an expression that is provably not `null`. */
|
|
Expr clearlyNotNullExpr(Expr reason) {
|
|
result = baseNotNullExpr() and reason = result
|
|
or
|
|
result.(CastExpr).getExpr() = clearlyNotNullExpr(reason)
|
|
or
|
|
result.(ImplicitCastExpr).getExpr() = clearlyNotNullExpr(reason)
|
|
or
|
|
result.(AssignExpr).getSource() = clearlyNotNullExpr(reason)
|
|
or
|
|
exists(ConditionalExpr c, Expr r1, Expr r2 |
|
|
c = result and
|
|
c.getThen() = clearlyNotNullExpr(r1) and
|
|
c.getElse() = clearlyNotNullExpr(r2) and
|
|
(reason = r1 or reason = r2)
|
|
)
|
|
or
|
|
exists(SsaDefinition v, boolean branch, VarRead rval, Guard guard |
|
|
guard = directNullGuard(v, branch, false) and
|
|
guard.controls(rval.getBasicBlock(), branch) and
|
|
reason = guard and
|
|
rval = v.getARead() and
|
|
result = rval and
|
|
not result = baseNotNullExpr()
|
|
)
|
|
or
|
|
exists(SsaDefinition v |
|
|
clearlyNotNull(v, reason) and
|
|
result = v.getARead() and
|
|
not result = baseNotNullExpr()
|
|
)
|
|
or
|
|
exists(Field f |
|
|
result = f.getAnAccess() and
|
|
f.isFinal() and
|
|
f.getInitializer() = clearlyNotNullExpr(reason) and
|
|
not result = baseNotNullExpr()
|
|
)
|
|
}
|
|
|
|
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
|
predicate clearlyNotNull(SsaDefinition v, Expr reason) {
|
|
exists(Expr src |
|
|
src = v.(SsaExplicitWrite).getValue() and
|
|
src = clearlyNotNullExpr(reason)
|
|
)
|
|
or
|
|
exists(CatchClause cc, LocalVariableDeclExpr decl |
|
|
decl = cc.getVariable() and
|
|
decl = v.(SsaExplicitWrite).getDefiningExpr() and
|
|
reason = decl
|
|
)
|
|
or
|
|
exists(SsaDefinition captured |
|
|
v.(SsaCapturedDefinition).captures(captured) and
|
|
clearlyNotNull(captured, reason)
|
|
)
|
|
or
|
|
exists(Field f |
|
|
v.getSourceVariable().getVariable() = f and
|
|
f.isFinal() and
|
|
f.getInitializer() = clearlyNotNullExpr(reason)
|
|
)
|
|
}
|
|
|
|
/** Gets an expression that is provably not `null`. */
|
|
Expr clearlyNotNullExpr() { result = clearlyNotNullExpr(_) }
|
|
|
|
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
|
predicate clearlyNotNull(SsaDefinition v) { clearlyNotNull(v, _) }
|
|
|
|
/**
|
|
* Holds if the evaluation of a call to `m` resulting in the value `branch`
|
|
* implies that the argument to the call is guaranteed to be null if `isnull`
|
|
* is true, and non-null if `isnull` is false.
|
|
*/
|
|
predicate nullCheckMethod(Method m, boolean branch, boolean isnull) {
|
|
exists(boolean polarity |
|
|
m.getDeclaringType().hasQualifiedName("java.util", "Objects") and
|
|
(
|
|
m.hasName("isNull") and polarity = true
|
|
or
|
|
m.hasName("nonNull") and polarity = false
|
|
) and
|
|
(
|
|
branch = true and isnull = polarity
|
|
or
|
|
branch = false and isnull = polarity.booleanNot()
|
|
)
|
|
)
|
|
or
|
|
m instanceof EqualsMethod and branch = true and isnull = false
|
|
or
|
|
m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "StringUtils") and
|
|
m.hasName("isBlank") and
|
|
branch = false and
|
|
isnull = false
|
|
or
|
|
m instanceof MethodApacheCollectionsIsEmpty and
|
|
branch = false and
|
|
isnull = false
|
|
or
|
|
m instanceof MethodApacheCollectionsIsNotEmpty and
|
|
branch = true and
|
|
isnull = false
|
|
or
|
|
m.getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and
|
|
m.hasName("isNullOrEmpty") and
|
|
branch = false and
|
|
isnull = false
|
|
}
|
|
|
|
/**
|
|
* Gets an expression that directly tests whether a given expression, `e`, is null or not.
|
|
*
|
|
* If `result` evaluates to `branch`, then `e` is guaranteed to be null if `isnull`
|
|
* is true, and non-null if `isnull` is false.
|
|
*/
|
|
Expr basicNullGuard(Expr e, boolean branch, boolean isnull) {
|
|
Guards_v3::nullGuard(result, any(GuardValue v | v.asBooleanValue() = branch), e, isnull)
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: Use `basicNullGuard` instead.
|
|
*
|
|
* Gets an expression that directly tests whether a given expression, `e`, is null or not.
|
|
*
|
|
* If `result` evaluates to `branch`, then `e` is guaranteed to be null if `isnull`
|
|
* is true, and non-null if `isnull` is false.
|
|
*/
|
|
deprecated Expr basicOrCustomNullGuard(Expr e, boolean branch, boolean isnull) {
|
|
result = basicNullGuard(e, branch, isnull)
|
|
}
|
|
|
|
/**
|
|
* Gets an expression that directly tests whether a given SSA variable is null or not.
|
|
*
|
|
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
|
|
* is true, and non-null if `isnull` is false.
|
|
*/
|
|
Expr directNullGuard(SsaDefinition v, boolean branch, boolean isnull) {
|
|
result = basicNullGuard(sameValue(v, _), branch, isnull)
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: Use `nullGuardControls`/`nullGuardControlsBranchEdge` instead.
|
|
*
|
|
* Gets a `Guard` that tests (possibly indirectly) whether a given SSA variable is null or not.
|
|
*
|
|
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
|
|
* is true, and non-null if `isnull` is false.
|
|
*/
|
|
deprecated Guard nullGuard(SsaDefinition v, boolean branch, boolean isnull) {
|
|
result = directNullGuard(v, branch, isnull)
|
|
}
|
|
|
|
/**
|
|
* Holds if there exists a null check on `v`, such that taking the branch edge
|
|
* from `bb1` to `bb2` implies that `v` is guaranteed to be null if `isnull` is
|
|
* true, and non-null if `isnull` is false.
|
|
*/
|
|
predicate nullGuardControlsBranchEdge(
|
|
SsaDefinition v, boolean isnull, BasicBlock bb1, BasicBlock bb2
|
|
) {
|
|
exists(GuardValue gv |
|
|
Guards_v3::ssaControlsBranchEdge(v, bb1, bb2, gv) and
|
|
gv.isNullness(isnull)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if there exists a null check on `v` that controls `bb`, such that in
|
|
* `bb` `v` is guaranteed to be null if `isnull` is true, and non-null if
|
|
* `isnull` is false.
|
|
*/
|
|
predicate nullGuardControls(SsaDefinition v, boolean isnull, BasicBlock bb) {
|
|
exists(GuardValue gv |
|
|
Guards_v3::ssaControls(v, bb, gv) and
|
|
gv.isNullness(isnull)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `guard` is a guard expression that suggests that `e` might be null.
|
|
*/
|
|
predicate guardSuggestsExprMaybeNull(Expr guard, Expr e) {
|
|
guard.(EqualityTest).hasOperands(e, any(NullLiteral n))
|
|
or
|
|
exists(MethodCall call |
|
|
call = guard and
|
|
call.getAnArgument() = e and
|
|
nullCheckMethod(call.getMethod(), _, true)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `guard` is a guard expression that suggests that `v` might be null.
|
|
*/
|
|
predicate guardSuggestsVarMaybeNull(Expr guard, SsaDefinition v) {
|
|
guardSuggestsExprMaybeNull(guard, sameValue(v, _))
|
|
}
|