mirror of
https://github.com/github/codeql.git
synced 2026-02-23 18:33:42 +01:00
307 lines
9.5 KiB
Plaintext
307 lines
9.5 KiB
Plaintext
/**
|
|
* Provides classes and predicates for null guards.
|
|
*/
|
|
|
|
import java
|
|
import SSA
|
|
private import semmle.code.java.controlflow.internal.GuardsLogic
|
|
private import semmle.code.java.frameworks.apache.Collections
|
|
private import RangeUtils
|
|
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(SsaVariable v, RefType type) {
|
|
result.getCheckedType() = type and
|
|
result.getExpr() = v.getAUse()
|
|
}
|
|
|
|
/**
|
|
* 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(SsaVariable v1, SsaVariable v2, boolean isEqualExpr) {
|
|
result.hasOperands(v1.getAUse(), v2.getAUse()) and
|
|
isEqualExpr = result.polarity()
|
|
}
|
|
|
|
/** Gets an expression that is provably not `null`. */
|
|
Expr clearlyNotNullExpr(Expr reason) {
|
|
result instanceof ClassInstanceExpr and reason = result
|
|
or
|
|
result instanceof ArrayCreationExpr and reason = result
|
|
or
|
|
result instanceof TypeLiteral and reason = result
|
|
or
|
|
result instanceof ThisAccess and reason = result
|
|
or
|
|
result instanceof StringLiteral and reason = result
|
|
or
|
|
result instanceof AddExpr and result.getType() instanceof TypeString and reason = result
|
|
or
|
|
exists(Field f |
|
|
result = f.getAnAccess() and
|
|
(f.hasName("TRUE") or f.hasName("FALSE")) and
|
|
f.getDeclaringType().hasQualifiedName("java.lang", "Boolean") and
|
|
reason = result
|
|
)
|
|
or
|
|
result.(CastExpr).getExpr() = clearlyNotNullExpr(reason)
|
|
or
|
|
result.(ImplicitCastExpr).getExpr() = clearlyNotNullExpr(reason)
|
|
or
|
|
result instanceof ImplicitNotNullExpr and reason = result
|
|
or
|
|
result instanceof ImplicitCoercionToUnitExpr and reason = result
|
|
or
|
|
result.(AssignExpr).getSource() = clearlyNotNullExpr(reason)
|
|
or
|
|
exists(ConditionalExpr c, Expr r1, Expr r2 |
|
|
c = result and
|
|
c.getTrueExpr() = clearlyNotNullExpr(r1) and
|
|
c.getFalseExpr() = clearlyNotNullExpr(r2) and
|
|
(reason = r1 or reason = r2)
|
|
)
|
|
or
|
|
exists(SsaVariable v, boolean branch, RValue rval, Guard guard |
|
|
guard = directNullGuard(v, branch, false) and
|
|
guard.controls(rval.getBasicBlock(), branch) and
|
|
reason = guard and
|
|
rval = v.getAUse() and
|
|
result = rval
|
|
)
|
|
or
|
|
exists(SsaVariable v | clearlyNotNull(v, reason) and result = v.getAUse())
|
|
or
|
|
exists(Method m | m = result.(MethodAccess).getMethod() and reason = result |
|
|
m.getDeclaringType().hasQualifiedName("com.google.common.base", "Strings") and
|
|
m.hasName("nullToEmpty")
|
|
)
|
|
}
|
|
|
|
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
|
predicate clearlyNotNull(SsaVariable v, Expr reason) {
|
|
exists(Expr src |
|
|
src = v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() and
|
|
src = clearlyNotNullExpr(reason)
|
|
)
|
|
or
|
|
exists(CatchClause cc, LocalVariableDeclExpr decl |
|
|
decl = cc.getVariable() and
|
|
decl = v.(SsaExplicitUpdate).getDefiningExpr() and
|
|
reason = decl
|
|
)
|
|
or
|
|
exists(SsaVariable captured |
|
|
v.(SsaImplicitInit).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(SsaVariable 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) {
|
|
exists(EqualityTest eqtest, boolean polarity |
|
|
eqtest = result and
|
|
eqtest.hasOperands(e, any(NullLiteral n)) and
|
|
polarity = eqtest.polarity() and
|
|
(
|
|
branch = true and isnull = polarity
|
|
or
|
|
branch = false and isnull = polarity.booleanNot()
|
|
)
|
|
)
|
|
or
|
|
result.(InstanceOfExpr).getExpr() = e and branch = true and isnull = false
|
|
or
|
|
exists(MethodAccess call |
|
|
call = result and
|
|
call.getAnArgument() = e and
|
|
nullCheckMethod(call.getMethod(), branch, isnull)
|
|
)
|
|
or
|
|
exists(EqualityTest eqtest |
|
|
eqtest = result and
|
|
eqtest.hasOperands(e, clearlyNotNullExpr()) and
|
|
isnull = false and
|
|
branch = eqtest.polarity()
|
|
)
|
|
or
|
|
result = enumConstEquality(e, branch, _) 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 basicOrCustomNullGuard(Expr e, boolean branch, boolean isnull) {
|
|
result = basicNullGuard(e, branch, isnull)
|
|
or
|
|
exists(MethodAccess call, Method m, int ix |
|
|
call = result and
|
|
call.getArgument(ix) = e and
|
|
call.getMethod().getSourceDeclaration() = m and
|
|
m = customNullGuard(ix, 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(SsaVariable v, boolean branch, boolean isnull) {
|
|
result = basicOrCustomNullGuard(sameValue(v, _), branch, isnull)
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
|
result = directNullGuard(v, branch, isnull) or
|
|
exists(boolean branch0 | implies_v3(result, branch, nullGuard(v, branch0, isnull), branch0))
|
|
}
|
|
|
|
/**
|
|
* A return statement in a non-overridable method that on a return value of
|
|
* `retval` allows the conclusion that the parameter `p` either is null or
|
|
* non-null as specified by `isnull`.
|
|
*/
|
|
private predicate validReturnInCustomNullGuard(
|
|
ReturnStmt ret, Parameter p, boolean retval, boolean isnull
|
|
) {
|
|
exists(Method m |
|
|
ret.getEnclosingCallable() = m and
|
|
p.getCallable() = m and
|
|
m.getReturnType().(PrimitiveType).hasName("boolean") and
|
|
not p.isVarargs() and
|
|
p.getType() instanceof RefType and
|
|
not m.isOverridable()
|
|
) and
|
|
exists(SsaImplicitInit ssa | ssa.isParameterDefinition(p) |
|
|
nullGuardedReturn(ret, ssa, isnull) and
|
|
(retval = true or retval = false)
|
|
or
|
|
exists(Expr res | res = ret.getResult() | res = nullGuard(ssa, retval, isnull))
|
|
)
|
|
}
|
|
|
|
private predicate nullGuardedReturn(ReturnStmt ret, SsaImplicitInit ssa, boolean isnull) {
|
|
exists(boolean branch |
|
|
nullGuard(ssa, branch, isnull).directlyControls(ret.getBasicBlock(), branch)
|
|
)
|
|
}
|
|
|
|
pragma[nomagic]
|
|
private Method returnStmtGetEnclosingCallable(ReturnStmt ret) {
|
|
ret.getEnclosingCallable() = result
|
|
}
|
|
|
|
/**
|
|
* Gets a non-overridable method with a boolean return value that performs a null-check
|
|
* on the `index`th parameter. A return value equal to `retval` allows us to conclude
|
|
* that the argument either is null or non-null as specified by `isnull`.
|
|
*/
|
|
private Method customNullGuard(int index, boolean retval, boolean isnull) {
|
|
exists(Parameter p |
|
|
p.getCallable() = result and
|
|
p.getPosition() = index and
|
|
forex(ReturnStmt ret |
|
|
returnStmtGetEnclosingCallable(ret) = result and
|
|
exists(Expr res | res = ret.getResult() |
|
|
not res.(BooleanLiteral).getBooleanValue() = retval.booleanNot()
|
|
)
|
|
|
|
|
validReturnInCustomNullGuard(ret, p, retval, isnull)
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* `guard` is a guard expression that suggests that `v` might be null.
|
|
*
|
|
* This is equivalent to `guard = basicNullGuard(sameValue(v, _), _, true)`.
|
|
*/
|
|
predicate guardSuggestsVarMaybeNull(Expr guard, SsaVariable v) {
|
|
guard = basicNullGuard(sameValue(v, _), _, true)
|
|
}
|