mirror of
https://github.com/github/codeql.git
synced 2026-04-25 00:35:20 +02:00
Java: Autoformat most of semmle.code.java.dataflow.
This commit is contained in:
@@ -15,18 +15,14 @@ module DataFlow {
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,14 @@ module DataFlow2 {
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,14 @@ module DataFlow3 {
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,14 @@ module DataFlow4 {
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,18 +15,14 @@ module DataFlow5 {
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
* Four copies are available: `DataFlow` through `DataFlow4`.
|
||||
*/
|
||||
private abstract
|
||||
class ConfigurationRecursionPrevention extends Configuration {
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n)) < 0 or
|
||||
strictcount(Node n | this.isSink(n)) < 0 or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ private import SSA
|
||||
*
|
||||
* This is the transitive closure of `adjacentUseUseSameVar`.
|
||||
*/
|
||||
predicate useUsePairSameVar(RValue use1, RValue use2) {
|
||||
adjacentUseUseSameVar+(use1, use2)
|
||||
}
|
||||
predicate useUsePairSameVar(RValue use1, RValue use2) { adjacentUseUseSameVar+(use1, use2) }
|
||||
|
||||
/**
|
||||
* Holds if `use1` and `use2` form a use-use-pair of the same
|
||||
@@ -25,9 +23,7 @@ predicate useUsePairSameVar(RValue use1, RValue use2) {
|
||||
*
|
||||
* This is the transitive closure of `adjacentUseUse`.
|
||||
*/
|
||||
predicate useUsePair(RValue use1, RValue use2) {
|
||||
adjacentUseUse+(use1, use2)
|
||||
}
|
||||
predicate useUsePair(RValue use1, RValue use2) { adjacentUseUse+(use1, use2) }
|
||||
|
||||
/**
|
||||
* Holds if there exists a path from `def` to `use` without passing through another
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Provides classes and predicates for reasoning about explicit and implicit
|
||||
* instance accesses.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
@@ -35,10 +36,9 @@ private predicate implicitSetEnclosingInstanceToThis(ConstructorCall cc) {
|
||||
*/
|
||||
private RefType getEnclosing(InnerClass ic, RefType t) {
|
||||
exists(RefType enclosing | enclosing = ic.getEnclosingType() |
|
||||
if enclosing.getASourceSupertype*() = t then
|
||||
result = enclosing
|
||||
else
|
||||
result = getEnclosing(enclosing, t)
|
||||
if enclosing.getASourceSupertype*() = t
|
||||
then result = enclosing
|
||||
else result = getEnclosing(enclosing, t)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,9 +56,14 @@ private predicate implicitEnclosingThisCopy(ConstructorCall cc, RefType t1, RefT
|
||||
* Holds if an enclosing instance of the form `t.this` is accessed by `e`.
|
||||
*/
|
||||
private predicate enclosingInstanceAccess(ExprParent e, RefType t) {
|
||||
e.(InstanceAccess).isEnclosingInstanceAccess(t) or
|
||||
exists(MethodAccess ma | ma.isEnclosingMethodAccess(t) and ma = e and not exists(ma.getQualifier())) or
|
||||
exists(FieldAccess fa | fa.isEnclosingFieldAccess(t) and fa = e and not exists(fa.getQualifier())) or
|
||||
e.(InstanceAccess).isEnclosingInstanceAccess(t)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.isEnclosingMethodAccess(t) and ma = e and not exists(ma.getQualifier())
|
||||
)
|
||||
or
|
||||
exists(FieldAccess fa | fa.isEnclosingFieldAccess(t) and fa = e and not exists(fa.getQualifier()))
|
||||
or
|
||||
implicitEnclosingThisCopy(e, t, _)
|
||||
}
|
||||
|
||||
@@ -70,7 +75,9 @@ private predicate enclosingInstanceAccess(ExprParent e, RefType t) {
|
||||
private predicate derivedInstanceAccess(ExprParent e, int i, RefType t1, RefType t2) {
|
||||
enclosingInstanceAccess(e, t2) and
|
||||
i = 0 and
|
||||
exists(Callable c | c = e.(Expr).getEnclosingCallable() or c = e.(Stmt).getEnclosingCallable() | t1 = c.getDeclaringType())
|
||||
exists(Callable c | c = e.(Expr).getEnclosingCallable() or c = e.(Stmt).getEnclosingCallable() |
|
||||
t1 = c.getDeclaringType()
|
||||
)
|
||||
or
|
||||
exists(InnerClass ic |
|
||||
derivedInstanceAccess(e, i - 1, ic, t2) and
|
||||
@@ -82,17 +89,15 @@ private predicate derivedInstanceAccess(ExprParent e, int i, RefType t1, RefType
|
||||
cached
|
||||
private newtype TInstanceAccessExt =
|
||||
TExplicitInstanceAccess(InstanceAccess ia) or
|
||||
TThisQualifier(FieldAccess fa) {
|
||||
fa.isOwnFieldAccess() and not exists(fa.getQualifier())
|
||||
} or
|
||||
TThisQualifier(FieldAccess fa) { fa.isOwnFieldAccess() and not exists(fa.getQualifier()) } or
|
||||
TThisArgument(Call c) {
|
||||
c instanceof ThisConstructorInvocationStmt or
|
||||
c instanceof SuperConstructorInvocationStmt or
|
||||
c instanceof ThisConstructorInvocationStmt
|
||||
or
|
||||
c instanceof SuperConstructorInvocationStmt
|
||||
or
|
||||
c.(MethodAccess).isOwnMethodAccess() and not exists(c.getQualifier())
|
||||
} or
|
||||
TThisEnclosingInstanceCapture(ConstructorCall cc) {
|
||||
implicitSetEnclosingInstanceToThis(cc)
|
||||
} or
|
||||
TThisEnclosingInstanceCapture(ConstructorCall cc) { implicitSetEnclosingInstanceToThis(cc) } or
|
||||
TEnclosingInstanceAccess(ExprParent e, RefType t) {
|
||||
enclosingInstanceAccess(e, t) and not e instanceof InstanceAccess
|
||||
} or
|
||||
@@ -125,23 +130,27 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
private string ppBase() {
|
||||
exists(EnclosingInstanceAccess enc | enc = this |
|
||||
result = enc.getQualifier().toString() + "(" + enc.getType() + ")enclosing"
|
||||
) or
|
||||
)
|
||||
or
|
||||
isOwnInstanceAccess() and result = "this"
|
||||
}
|
||||
|
||||
private string ppKind() {
|
||||
isExplicit(_) and result = " <" + getAssociatedExprOrStmt().toString() + ">" or
|
||||
isImplicitFieldQualifier(_) and result = " <.field>" or
|
||||
isImplicitMethodQualifier(_) and result = " <.method>" or
|
||||
isImplicitThisConstructorArgument(_) and result = " <constr(this)>" or
|
||||
isImplicitEnclosingInstanceCapture(_) and result = " <.new>" or
|
||||
isExplicit(_) and result = " <" + getAssociatedExprOrStmt().toString() + ">"
|
||||
or
|
||||
isImplicitFieldQualifier(_) and result = " <.field>"
|
||||
or
|
||||
isImplicitMethodQualifier(_) and result = " <.method>"
|
||||
or
|
||||
isImplicitThisConstructorArgument(_) and result = " <constr(this)>"
|
||||
or
|
||||
isImplicitEnclosingInstanceCapture(_) and result = " <.new>"
|
||||
or
|
||||
isImplicitEnclosingInstanceQualifier(_) and result = "."
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = ppBase() + ppKind()
|
||||
}
|
||||
string toString() { result = ppBase() + ppKind() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { result = getAssociatedExprOrStmt().getLocation() }
|
||||
@@ -180,11 +189,9 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
* Holds if this is the implicit `this` argument of `cc`, which is either a
|
||||
* `ThisConstructorInvocationStmt` or a `SuperConstructorInvocationStmt`.
|
||||
*/
|
||||
predicate isImplicitThisConstructorArgument(ConstructorCall cc) {
|
||||
this = TThisArgument(cc)
|
||||
}
|
||||
predicate isImplicitThisConstructorArgument(ConstructorCall cc) { this = TThisArgument(cc) }
|
||||
|
||||
/** Holds if this is the implicit qualifier of `cc`.*/
|
||||
/** Holds if this is the implicit qualifier of `cc`. */
|
||||
predicate isImplicitEnclosingInstanceCapture(ConstructorCall cc) {
|
||||
this = TThisEnclosingInstanceCapture(cc) or
|
||||
this = TEnclosingInstanceAccess(cc, _)
|
||||
@@ -199,31 +206,35 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
}
|
||||
|
||||
/** Holds if this is an access to an object's own instance. */
|
||||
predicate isOwnInstanceAccess() {
|
||||
not isEnclosingInstanceAccess(_)
|
||||
}
|
||||
predicate isOwnInstanceAccess() { not isEnclosingInstanceAccess(_) }
|
||||
|
||||
/** Holds if this is an access to an enclosing instance. */
|
||||
predicate isEnclosingInstanceAccess(RefType t) {
|
||||
exists(InstanceAccess ia | this = TExplicitInstanceAccess(ia) and ia.isEnclosingInstanceAccess(t)) or
|
||||
this = TEnclosingInstanceAccess(_, t) or
|
||||
exists(InstanceAccess ia |
|
||||
this = TExplicitInstanceAccess(ia) and ia.isEnclosingInstanceAccess(t)
|
||||
)
|
||||
or
|
||||
this = TEnclosingInstanceAccess(_, t)
|
||||
or
|
||||
exists(int i | this = TInstanceAccessQualifier(_, i, t, _) and i > 0)
|
||||
}
|
||||
|
||||
/** Gets the type of this instance access. */
|
||||
RefType getType() {
|
||||
isEnclosingInstanceAccess(result) or
|
||||
isEnclosingInstanceAccess(result)
|
||||
or
|
||||
isOwnInstanceAccess() and result = getEnclosingCallable().getDeclaringType()
|
||||
}
|
||||
|
||||
/** Gets the control flow node associated with this instance access. */
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(ExprParent e | e = getAssociatedExprOrStmt() |
|
||||
e instanceof Call and result = e or
|
||||
e instanceof InstanceAccess and result = e or
|
||||
e instanceof Call and result = e
|
||||
or
|
||||
e instanceof InstanceAccess and result = e
|
||||
or
|
||||
exists(FieldAccess fa | fa = e |
|
||||
if fa instanceof RValue then fa = result
|
||||
else result.(AssignExpr).getDest() = fa
|
||||
if fa instanceof RValue then fa = result else result.(AssignExpr).getDest() = fa
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -232,9 +243,7 @@ class InstanceAccessExt extends TInstanceAccessExt {
|
||||
/**
|
||||
* An access to an object's own instance.
|
||||
*/
|
||||
class OwnInstanceAccess extends InstanceAccessExt {
|
||||
OwnInstanceAccess() { isOwnInstanceAccess() }
|
||||
}
|
||||
class OwnInstanceAccess extends InstanceAccessExt { OwnInstanceAccess() { isOwnInstanceAccess() } }
|
||||
|
||||
/**
|
||||
* An access to an enclosing instance.
|
||||
@@ -244,13 +253,10 @@ class EnclosingInstanceAccess extends InstanceAccessExt {
|
||||
|
||||
/** Gets the implicit qualifier of this in the desugared representation. */
|
||||
InstanceAccessExt getQualifier() {
|
||||
exists(ExprParent e, int i |
|
||||
result = TInstanceAccessQualifier(e, i, _, _)
|
||||
|
|
||||
this = TInstanceAccessQualifier(e, i + 1, _, _) or
|
||||
exists(RefType t |
|
||||
derivedInstanceAccess(e, i + 1, t, t)
|
||||
|
|
||||
exists(ExprParent e, int i | result = TInstanceAccessQualifier(e, i, _, _) |
|
||||
this = TInstanceAccessQualifier(e, i + 1, _, _)
|
||||
or
|
||||
exists(RefType t | derivedInstanceAccess(e, i + 1, t, t) |
|
||||
this = TEnclosingInstanceAccess(e, t) or
|
||||
this = TExplicitInstanceAccess(e)
|
||||
)
|
||||
|
||||
@@ -20,9 +20,7 @@ private Expr exprWithIntValue(int i) {
|
||||
* This includes `RValue` and `MethodAccess`.
|
||||
*/
|
||||
class IntComparableExpr extends Expr {
|
||||
IntComparableExpr() {
|
||||
this instanceof RValue or this instanceof MethodAccess
|
||||
}
|
||||
IntComparableExpr() { this instanceof RValue or this instanceof MethodAccess }
|
||||
|
||||
/** Gets an integer that is directly assigned to the expression in case of a variable; or zero. */
|
||||
int relevantInt() {
|
||||
@@ -30,7 +28,8 @@ class IntComparableExpr extends Expr {
|
||||
this = v.getAnAccess() and
|
||||
ssa.getSourceVariable() = v and
|
||||
ssa.getDefiningExpr().(VariableAssign).getSource() = exprWithIntValue(result)
|
||||
) or
|
||||
)
|
||||
or
|
||||
result = 0
|
||||
}
|
||||
}
|
||||
@@ -49,32 +48,69 @@ Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
|
||||
eqtest = result and
|
||||
eqtest.hasOperands(e, any(ConstantIntegerExpr c | c.getIntValue() = k)) and
|
||||
polarity = eqtest.polarity() and
|
||||
(branch = true and is_k = polarity or branch = false and is_k = polarity.booleanNot())
|
||||
) or
|
||||
(
|
||||
branch = true and is_k = polarity
|
||||
or
|
||||
branch = false and is_k = polarity.booleanNot()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(EqualityTest eqtest, int val, Expr c, boolean upper |
|
||||
k = e.relevantInt() and
|
||||
eqtest = result and
|
||||
eqtest.hasOperands(e, c) and
|
||||
bounded(c, any(ZeroBound zb), val, upper, _) and
|
||||
is_k = false and
|
||||
(upper = true and val < k or upper = false and val > k) and
|
||||
(
|
||||
upper = true and val < k
|
||||
or
|
||||
upper = false and val > k
|
||||
) and
|
||||
branch = eqtest.polarity()
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(ComparisonExpr comp, Expr c, int val, boolean upper |
|
||||
k = e.relevantInt() and
|
||||
comp = result and
|
||||
comp.hasOperands(e, c) and
|
||||
bounded(c, any(ZeroBound zb), val, upper, _) and
|
||||
is_k = false
|
||||
|
|
||||
comp.getLesserOperand() = c and comp.isStrict() and branch = true and val >= k and upper = false or // k <= val <= c < e, so e != k
|
||||
comp.getLesserOperand() = c and comp.isStrict() and branch = false and val < k and upper = true or
|
||||
comp.getLesserOperand() = c and not comp.isStrict() and branch = true and val > k and upper = false or
|
||||
comp.getLesserOperand() = c and not comp.isStrict() and branch = false and val <= k and upper = true or
|
||||
comp.getGreaterOperand() = c and comp.isStrict() and branch = true and val <= k and upper = true or
|
||||
comp.getGreaterOperand() = c and comp.isStrict() and branch = false and val > k and upper = false or
|
||||
comp.getGreaterOperand() = c and not comp.isStrict() and branch = true and val < k and upper = true or
|
||||
comp.getGreaterOperand() = c and not comp.isStrict() and branch = false and val >= k and upper = false
|
||||
|
|
||||
comp.getLesserOperand() = c and comp.isStrict() and branch = true and val >= k and upper = false // k <= val <= c < e, so e != k
|
||||
or
|
||||
comp.getLesserOperand() = c and comp.isStrict() and branch = false and val < k and upper = true
|
||||
or
|
||||
comp.getLesserOperand() = c and
|
||||
not comp.isStrict() and
|
||||
branch = true and
|
||||
val > k and
|
||||
upper = false
|
||||
or
|
||||
comp.getLesserOperand() = c and
|
||||
not comp.isStrict() and
|
||||
branch = false and
|
||||
val <= k and
|
||||
upper = true
|
||||
or
|
||||
comp.getGreaterOperand() = c and comp.isStrict() and branch = true and val <= k and upper = true
|
||||
or
|
||||
comp.getGreaterOperand() = c and
|
||||
comp.isStrict() and
|
||||
branch = false and
|
||||
val > k and
|
||||
upper = false
|
||||
or
|
||||
comp.getGreaterOperand() = c and
|
||||
not comp.isStrict() and
|
||||
branch = true and
|
||||
val < k and
|
||||
upper = true
|
||||
or
|
||||
comp.getGreaterOperand() = c and
|
||||
not comp.isStrict() and
|
||||
branch = false and
|
||||
val >= k and
|
||||
upper = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,11 +127,25 @@ Expr intBoundGuard(RValue x, boolean branch_with_lower_bound_k, int k) {
|
||||
comp.hasOperands(x, c) and
|
||||
c.getIntValue() = val and
|
||||
x.getVariable().getType() instanceof IntegralType
|
||||
|
|
||||
comp.getLesserOperand().getProperExpr() = c and comp.isStrict() and branch_with_lower_bound_k = true and val + 1 = k or // c < x
|
||||
comp.getLesserOperand().getProperExpr() = c and not comp.isStrict() and branch_with_lower_bound_k = true and val = k or // c <= x
|
||||
comp.getGreaterOperand().getProperExpr() = c and comp.isStrict() and branch_with_lower_bound_k = false and val = k or // x < c
|
||||
comp.getGreaterOperand().getProperExpr() = c and not comp.isStrict() and branch_with_lower_bound_k = false and val + 1 = k // x <= c
|
||||
|
|
||||
comp.getLesserOperand().getProperExpr() = c and
|
||||
comp.isStrict() and
|
||||
branch_with_lower_bound_k = true and
|
||||
val + 1 = k // c < x
|
||||
or
|
||||
comp.getLesserOperand().getProperExpr() = c and
|
||||
not comp.isStrict() and
|
||||
branch_with_lower_bound_k = true and
|
||||
val = k // c <= x
|
||||
or
|
||||
comp.getGreaterOperand().getProperExpr() = c and
|
||||
comp.isStrict() and
|
||||
branch_with_lower_bound_k = false and
|
||||
val = k // x < c
|
||||
or
|
||||
comp.getGreaterOperand().getProperExpr() = c and
|
||||
not comp.isStrict() and
|
||||
branch_with_lower_bound_k = false and
|
||||
val + 1 = k // x <= c
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,34 +26,46 @@ Expr enumConstEquality(Expr e, boolean polarity, EnumConstant c) {
|
||||
|
||||
/** 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
|
||||
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.(ParExpr).getExpr() = clearlyNotNullExpr(reason) or
|
||||
result.(CastExpr).getExpr() = clearlyNotNullExpr(reason) or
|
||||
result.(AssignExpr).getSource() = clearlyNotNullExpr(reason) or
|
||||
)
|
||||
or
|
||||
result.(ParExpr).getExpr() = clearlyNotNullExpr(reason)
|
||||
or
|
||||
result.(CastExpr).getExpr() = clearlyNotNullExpr(reason)
|
||||
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
|
||||
)
|
||||
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
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v | clearlyNotNull(v, reason) and result = v.getAUse())
|
||||
}
|
||||
|
||||
@@ -62,12 +74,14 @@ predicate clearlyNotNull(SsaVariable v, Expr reason) {
|
||||
exists(Expr src |
|
||||
src = v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() and
|
||||
src = clearlyNotNullExpr(reason)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl |
|
||||
decl = cc.getVariable() and
|
||||
decl = v.(SsaExplicitUpdate).getDefiningExpr() and
|
||||
reason = decl
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(SsaVariable captured |
|
||||
v.(SsaImplicitInit).captures(captured) and
|
||||
clearlyNotNull(captured, reason)
|
||||
@@ -75,14 +89,10 @@ predicate clearlyNotNull(SsaVariable v, Expr reason) {
|
||||
}
|
||||
|
||||
/** Gets an expression that is provably not `null`. */
|
||||
Expr clearlyNotNullExpr() {
|
||||
result = clearlyNotNullExpr(_)
|
||||
}
|
||||
Expr clearlyNotNullExpr() { result = clearlyNotNullExpr(_) }
|
||||
|
||||
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
||||
predicate clearlyNotNull(SsaVariable v) {
|
||||
clearlyNotNull(v, _)
|
||||
}
|
||||
predicate clearlyNotNull(SsaVariable v) { clearlyNotNull(v, _) }
|
||||
|
||||
/**
|
||||
* Gets an expression that directly tests whether a given expression, `e`, is null or not.
|
||||
@@ -95,30 +105,47 @@ Expr basicNullGuard(Expr e, boolean branch, boolean isnull) {
|
||||
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
|
||||
(
|
||||
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, Method m, boolean polarity |
|
||||
call = result and
|
||||
call.getAnArgument() = e and
|
||||
call.getMethod() = m and
|
||||
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.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
|
||||
exists(MethodAccess call |
|
||||
call = result and
|
||||
call.getAnArgument() = e and
|
||||
call.getMethod() instanceof EqualsMethod and
|
||||
branch = true and
|
||||
isnull = false
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(EqualityTest eqtest |
|
||||
eqtest = result and
|
||||
eqtest.hasOperands(e, clearlyNotNullExpr()) and
|
||||
isnull = false and
|
||||
branch = eqtest.polarity()
|
||||
) or
|
||||
)
|
||||
or
|
||||
result = enumConstEquality(e, branch, _) and isnull = false
|
||||
}
|
||||
|
||||
@@ -129,7 +156,8 @@ Expr basicNullGuard(Expr e, boolean branch, boolean isnull) {
|
||||
* is true, and non-null if `isnull` is false.
|
||||
*/
|
||||
Expr basicOrCustomNullGuard(Expr e, boolean branch, boolean isnull) {
|
||||
result = basicNullGuard(e, branch, isnull) or
|
||||
result = basicNullGuard(e, branch, isnull)
|
||||
or
|
||||
exists(MethodAccess call, Method m, int ix |
|
||||
call = result and
|
||||
call.getArgument(ix) = e and
|
||||
@@ -163,7 +191,9 @@ Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
* A return statement 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) {
|
||||
private predicate validReturnInCustomNullGuard(
|
||||
ReturnStmt ret, Parameter p, boolean retval, boolean isnull
|
||||
) {
|
||||
exists(Method m |
|
||||
ret.getEnclosingCallable() = m and
|
||||
p.getCallable() = m and
|
||||
@@ -173,9 +203,7 @@ private predicate validReturnInCustomNullGuard(ReturnStmt ret, Parameter p, bool
|
||||
nullGuardedReturn(ret, ssa, isnull) and
|
||||
(retval = true or retval = false)
|
||||
or
|
||||
exists(Expr res | res = ret.getResult() |
|
||||
res = nullGuard(ssa, retval, isnull)
|
||||
)
|
||||
exists(Expr res | res = ret.getResult() | res = nullGuard(ssa, retval, isnull))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -200,8 +228,10 @@ private Method customNullGuard(int index, boolean retval, boolean isnull) {
|
||||
p.getPosition() = index and
|
||||
forex(ReturnStmt ret |
|
||||
ret.getEnclosingCallable() = result and
|
||||
exists(Expr res | res = ret.getResult() | not res.(BooleanLiteral).getBooleanValue() = retval.booleanNot())
|
||||
|
|
||||
exists(Expr res | res = ret.getResult() |
|
||||
not res.(BooleanLiteral).getBooleanValue() = retval.booleanNot()
|
||||
)
|
||||
|
|
||||
validReturnInCustomNullGuard(ret, p, retval, isnull)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -56,31 +56,70 @@ Expr nullExpr() {
|
||||
private predicate unboxed(Expr e) {
|
||||
e.getType() instanceof BoxedType and
|
||||
(
|
||||
exists(ArrayAccess aa | aa.getIndexExpr() = e) or
|
||||
exists(ArrayCreationExpr ace | ace.getADimension() = e) or
|
||||
exists(LocalVariableDeclExpr decl | decl.getVariable().getType() instanceof PrimitiveType and decl.getInit() = e) or
|
||||
exists(AssignExpr assign | assign.getDest().getType() instanceof PrimitiveType and assign.getSource() = e) or
|
||||
exists(AssignOp assign | assign.getSource() = e and assign.getType() instanceof PrimitiveType) or
|
||||
exists(EqualityTest eq | eq.getAnOperand() = e and eq.getAnOperand().getType() instanceof PrimitiveType) or
|
||||
exists(BinaryExpr bin | bin.getAnOperand() = e and not bin instanceof EqualityTest and bin.getType() instanceof PrimitiveType) or
|
||||
exists(UnaryExpr un | un.getExpr() = e) or
|
||||
exists(ConditionalExpr cond | cond.getType() instanceof PrimitiveType | cond.getTrueExpr() = e or cond.getFalseExpr() = e) or
|
||||
exists(ConditionNode cond | cond.getCondition() = e) or
|
||||
exists(Parameter p | p.getType() instanceof PrimitiveType and p.getAnArgument() = e) or
|
||||
exists(ReturnStmt ret | ret.getEnclosingCallable().getReturnType() instanceof PrimitiveType and ret.getResult() = e)
|
||||
exists(ArrayAccess aa | aa.getIndexExpr() = e)
|
||||
or
|
||||
exists(ArrayCreationExpr ace | ace.getADimension() = e)
|
||||
or
|
||||
exists(LocalVariableDeclExpr decl |
|
||||
decl.getVariable().getType() instanceof PrimitiveType and decl.getInit() = e
|
||||
)
|
||||
or
|
||||
exists(AssignExpr assign |
|
||||
assign.getDest().getType() instanceof PrimitiveType and assign.getSource() = e
|
||||
)
|
||||
or
|
||||
exists(AssignOp assign | assign.getSource() = e and assign.getType() instanceof PrimitiveType)
|
||||
or
|
||||
exists(EqualityTest eq |
|
||||
eq.getAnOperand() = e and eq.getAnOperand().getType() instanceof PrimitiveType
|
||||
)
|
||||
or
|
||||
exists(BinaryExpr bin |
|
||||
bin.getAnOperand() = e and
|
||||
not bin instanceof EqualityTest and
|
||||
bin.getType() instanceof PrimitiveType
|
||||
)
|
||||
or
|
||||
exists(UnaryExpr un | un.getExpr() = e)
|
||||
or
|
||||
exists(ConditionalExpr cond | cond.getType() instanceof PrimitiveType |
|
||||
cond.getTrueExpr() = e or cond.getFalseExpr() = e
|
||||
)
|
||||
or
|
||||
exists(ConditionNode cond | cond.getCondition() = e)
|
||||
or
|
||||
exists(Parameter p | p.getType() instanceof PrimitiveType and p.getAnArgument() = e)
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
ret.getEnclosingCallable().getReturnType() instanceof PrimitiveType and ret.getResult() = e
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** An expression that is being dereferenced. These are the points where `NullPointerException`s can occur. */
|
||||
predicate dereference(Expr e) {
|
||||
exists(EnhancedForStmt for | for.getExpr() = e) or
|
||||
exists(SynchronizedStmt synch | synch.getExpr() = e) or
|
||||
exists(SwitchStmt switch | switch.getExpr() = e) or
|
||||
exists(FieldAccess fa, Field f | fa.getQualifier() = e and fa.getField() = f and not f.isStatic()) or
|
||||
exists(MethodAccess ma, Method m | ma.getQualifier() = e and ma.getMethod() = m and not m.isStatic()) or
|
||||
exists(ClassInstanceExpr cie | cie.getQualifier() = e) or
|
||||
exists(ArrayAccess aa | aa.getArray() = e) or
|
||||
exists(CastExpr cast | cast.getExpr() = e and e.getType() instanceof BoxedType and cast.getType() instanceof PrimitiveType) or
|
||||
exists(EnhancedForStmt for | for.getExpr() = e)
|
||||
or
|
||||
exists(SynchronizedStmt synch | synch.getExpr() = e)
|
||||
or
|
||||
exists(SwitchStmt switch | switch.getExpr() = e)
|
||||
or
|
||||
exists(FieldAccess fa, Field f | fa.getQualifier() = e and fa.getField() = f and not f.isStatic())
|
||||
or
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma.getQualifier() = e and ma.getMethod() = m and not m.isStatic()
|
||||
)
|
||||
or
|
||||
exists(ClassInstanceExpr cie | cie.getQualifier() = e)
|
||||
or
|
||||
exists(ArrayAccess aa | aa.getArray() = e)
|
||||
or
|
||||
exists(CastExpr cast |
|
||||
cast.getExpr() = e and
|
||||
e.getType() instanceof BoxedType and
|
||||
cast.getType() instanceof PrimitiveType
|
||||
)
|
||||
or
|
||||
unboxed(e)
|
||||
}
|
||||
|
||||
@@ -145,18 +184,15 @@ private predicate varMaybeNull(SsaVariable v, string msg, Expr reason) {
|
||||
not v instanceof SsaPhiNode and
|
||||
not clearlyNotNull(v) and
|
||||
// Comparisons in finally blocks are excluded since missing exception edges in the CFG could otherwise yield FPs.
|
||||
not exists(TryStmt try |
|
||||
try.getFinally() = e.getEnclosingStmt().getParent*()
|
||||
) and
|
||||
not exists(TryStmt try | try.getFinally() = e.getEnclosingStmt().getParent*()) and
|
||||
(
|
||||
exists(ConditionalExpr c | c.getCondition().getAChildExpr*() = e) or
|
||||
not exists(MethodAccess ma |
|
||||
ma.getAnArgument().getAChildExpr*() = e
|
||||
)
|
||||
not exists(MethodAccess ma | ma.getAnArgument().getAChildExpr*() = e)
|
||||
) and
|
||||
// Don't use a guard as reason if there is a null assignment.
|
||||
not v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = nullExpr()
|
||||
) or
|
||||
)
|
||||
or
|
||||
// A parameter might be null if there is a null argument somewhere.
|
||||
exists(Parameter p, Expr arg |
|
||||
v.(SsaImplicitInit).isParameterDefinition(p) and
|
||||
@@ -165,7 +201,8 @@ private predicate varMaybeNull(SsaVariable v, string msg, Expr reason) {
|
||||
msg = "because of $@ null argument" and
|
||||
arg = nullExpr() and
|
||||
not arg.getEnclosingCallable().getEnclosingCallable*() instanceof TestMethod
|
||||
) or
|
||||
)
|
||||
or
|
||||
// If the source of a variable is null then the variable may be null.
|
||||
exists(VariableAssign def |
|
||||
v.(SsaExplicitUpdate).getDefiningExpr() = def and
|
||||
@@ -178,14 +215,16 @@ private predicate varMaybeNull(SsaVariable v, string msg, Expr reason) {
|
||||
/** Gets an array or collection that contains at least one element. */
|
||||
private Expr nonEmptyExpr() {
|
||||
// An array creation with a known positive size is trivially non-empty.
|
||||
result.(ArrayCreationExpr).getFirstDimensionSize() > 0 or
|
||||
result.(ArrayCreationExpr).getFirstDimensionSize() > 0
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
// A use of an array variable is non-empty if...
|
||||
result = v.getAUse() and
|
||||
v.getSourceVariable().getType() instanceof Array
|
||||
|
|
||||
|
|
||||
// ...its definition is non-empty...
|
||||
v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = nonEmptyExpr() or
|
||||
v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = nonEmptyExpr()
|
||||
or
|
||||
// ...or it is guarded by a condition proving its length to be non-zero.
|
||||
exists(ConditionBlock cond, boolean branch, FieldAccess length |
|
||||
cond.controls(result.getBasicBlock(), branch) and
|
||||
@@ -193,7 +232,8 @@ private Expr nonEmptyExpr() {
|
||||
length.getField().hasName("length") and
|
||||
length.getQualifier() = v.getAUse()
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
// A use of a Collection variable is non-empty if...
|
||||
result = v.getAUse() and
|
||||
@@ -206,13 +246,16 @@ private Expr nonEmptyExpr() {
|
||||
m = ma.getMethod() and
|
||||
ma.getQualifier() = v.getSourceVariable().getAnAccess() and
|
||||
cond.controls(ma.getBasicBlock(), branch)
|
||||
|
|
||||
|
|
||||
m instanceof CollectionQueryMethod
|
||||
) and
|
||||
cond.getCondition() = c
|
||||
|
|
||||
|
|
||||
// ...and the condition proves that it is non-empty, either by using the `isEmpty` method...
|
||||
c.(MethodAccess).getMethod().hasName("isEmpty") and branch = false and c.(MethodAccess).getQualifier() = v.getAUse() or
|
||||
c.(MethodAccess).getMethod().hasName("isEmpty") and
|
||||
branch = false and
|
||||
c.(MethodAccess).getQualifier() = v.getAUse()
|
||||
or
|
||||
// ...or a check on its `size`.
|
||||
exists(MethodAccess size |
|
||||
c = integerGuard(size, branch, 0, false) and
|
||||
@@ -250,7 +293,9 @@ private predicate leavingFinally(BasicBlock bb1, BasicBlock bb2, boolean normale
|
||||
bb1.getABBSuccessor() = bb2 and
|
||||
bb1.getEnclosingStmt().getParent*() = finally and
|
||||
not bb2.getEnclosingStmt().getParent*() = finally and
|
||||
if bb1.getLastNode().getANormalSuccessor() = bb2.getFirstNode() then normaledge = true else normaledge = false
|
||||
if bb1.getLastNode().getANormalSuccessor() = bb2.getFirstNode()
|
||||
then normaledge = true
|
||||
else normaledge = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -269,13 +314,18 @@ private predicate ssaSourceVarMaybeNull(SsaSourceVariable v) {
|
||||
* no knowledge is assumed of any potentially waiting completions. `midstoredcompletion` is the flag before
|
||||
* the step and `storedcompletion` is the flag after the step.
|
||||
*/
|
||||
private predicate nullVarStep(SsaVariable midssa, BasicBlock mid, boolean midstoredcompletion, SsaVariable ssa, BasicBlock bb, boolean storedcompletion) {
|
||||
private predicate nullVarStep(
|
||||
SsaVariable midssa, BasicBlock mid, boolean midstoredcompletion, SsaVariable ssa, BasicBlock bb,
|
||||
boolean storedcompletion
|
||||
) {
|
||||
exists(SsaSourceVariable v |
|
||||
ssaSourceVarMaybeNull(v) and
|
||||
midssa.getSourceVariable() = v
|
||||
|
|
||||
ssa.(SsaPhiNode).getAPhiInput() = midssa and ssa.getBasicBlock() = bb or
|
||||
ssa = midssa and not exists(SsaPhiNode phi | phi.getSourceVariable() = v and phi.getBasicBlock() = bb)
|
||||
|
|
||||
ssa.(SsaPhiNode).getAPhiInput() = midssa and ssa.getBasicBlock() = bb
|
||||
or
|
||||
ssa = midssa and
|
||||
not exists(SsaPhiNode phi | phi.getSourceVariable() = v and phi.getBasicBlock() = bb)
|
||||
) and
|
||||
(midstoredcompletion = true or midstoredcompletion = false) and
|
||||
midssa.isLiveAtEndOfBlock(mid) and
|
||||
@@ -283,24 +333,31 @@ private predicate nullVarStep(SsaVariable midssa, BasicBlock mid, boolean midsto
|
||||
not assertFail(mid, _) and
|
||||
bb = mid.getABBSuccessor() and
|
||||
not impossibleEdge(mid, bb) and
|
||||
not exists(boolean branch |
|
||||
nullGuard(midssa, branch, false).hasBranchEdge(mid, bb, branch)
|
||||
) and
|
||||
not exists(boolean branch | nullGuard(midssa, branch, false).hasBranchEdge(mid, bb, branch)) and
|
||||
not (leavingFinally(mid, bb, true) and midstoredcompletion = true) and
|
||||
if bb.getFirstNode() = any(TryStmt try | | try.getFinally()) then
|
||||
(if bb.getFirstNode() = mid.getLastNode().getANormalSuccessor() then storedcompletion = false else storedcompletion = true)
|
||||
else if leavingFinally(mid, bb, _) then
|
||||
storedcompletion = false
|
||||
else
|
||||
storedcompletion = midstoredcompletion
|
||||
if bb.getFirstNode() = any(TryStmt try | | try.getFinally())
|
||||
then (
|
||||
if bb.getFirstNode() = mid.getLastNode().getANormalSuccessor()
|
||||
then storedcompletion = false
|
||||
else storedcompletion = true
|
||||
) else
|
||||
if leavingFinally(mid, bb, _)
|
||||
then storedcompletion = false
|
||||
else storedcompletion = midstoredcompletion
|
||||
}
|
||||
|
||||
/**
|
||||
* The transitive closure of `nullVarStep` originating from `varMaybeNull`. That is, those `BasicBlock`s
|
||||
* for which the SSA variable is suspected of being `null`.
|
||||
*/
|
||||
private predicate varMaybeNullInBlock(SsaVariable ssa, SsaSourceVariable v, BasicBlock bb, boolean storedcompletion) {
|
||||
varMaybeNull(ssa, _, _) and bb = ssa.getBasicBlock() and storedcompletion = false and v = ssa.getSourceVariable() or
|
||||
private predicate varMaybeNullInBlock(
|
||||
SsaVariable ssa, SsaSourceVariable v, BasicBlock bb, boolean storedcompletion
|
||||
) {
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
v = ssa.getSourceVariable()
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion |
|
||||
varMaybeNullInBlock(midssa, v, mid, midstoredcompletion) and
|
||||
nullVarStep(midssa, mid, midstoredcompletion, ssa, bb, storedcompletion)
|
||||
@@ -318,9 +375,14 @@ private predicate nullDerefCandidateVariable(SsaSourceVariable v) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate varMaybeNullInBlock_origin(SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion) {
|
||||
private predicate varMaybeNullInBlock_origin(
|
||||
SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion
|
||||
) {
|
||||
nullDerefCandidateVariable(ssa.getSourceVariable()) and
|
||||
varMaybeNull(ssa, _, _) and bb = ssa.getBasicBlock() and storedcompletion = false and origin = ssa
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
origin = ssa
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion |
|
||||
varMaybeNullInBlock_origin(origin, midssa, mid, midstoredcompletion) and
|
||||
@@ -370,9 +432,14 @@ private predicate varConditionallyNull(SsaExplicitUpdate v, ConditionBlock cond,
|
||||
exists(ConditionalExpr condexpr |
|
||||
v.getDefiningExpr().(VariableAssign).getSource().getProperExpr() = condexpr and
|
||||
condexpr.getCondition().getProperExpr() = cond.getCondition()
|
||||
|
|
||||
condexpr.getTrueExpr() = nullExpr() and branch = true and not condexpr.getFalseExpr() = nullExpr() or
|
||||
condexpr.getFalseExpr() = nullExpr() and branch = false and not condexpr.getTrueExpr() = nullExpr()
|
||||
|
|
||||
condexpr.getTrueExpr() = nullExpr() and
|
||||
branch = true and
|
||||
not condexpr.getFalseExpr() = nullExpr()
|
||||
or
|
||||
condexpr.getFalseExpr() = nullExpr() and
|
||||
branch = false and
|
||||
not condexpr.getTrueExpr() = nullExpr()
|
||||
)
|
||||
or
|
||||
v.getDefiningExpr().(VariableAssign).getSource() = nullExpr() and
|
||||
@@ -386,12 +453,17 @@ private predicate varConditionallyNull(SsaExplicitUpdate v, ConditionBlock cond,
|
||||
*/
|
||||
private predicate interestingCond(SsaSourceVariable npecand, ConditionBlock cond) {
|
||||
nullDerefCandidateVariable(npecand) and
|
||||
(varMaybeNullInBlock(_, npecand, cond, _) or varConditionallyNull(npecand.getAnSsaVariable(), cond, _)) and
|
||||
(
|
||||
varMaybeNullInBlock(_, npecand, cond, _) or
|
||||
varConditionallyNull(npecand.getAnSsaVariable(), cond, _)
|
||||
) and
|
||||
not cond.getCondition().getAChildExpr*() = npecand.getAnAccess()
|
||||
}
|
||||
|
||||
/** A pair of correlated conditions for a given NPE candidate. */
|
||||
private predicate correlatedConditions(SsaSourceVariable npecand, ConditionBlock cond1, ConditionBlock cond2, boolean inverted) {
|
||||
private predicate correlatedConditions(
|
||||
SsaSourceVariable npecand, ConditionBlock cond1, ConditionBlock cond2, boolean inverted
|
||||
) {
|
||||
interestingCond(npecand, cond1) and
|
||||
interestingCond(npecand, cond2) and
|
||||
cond1 != cond2 and
|
||||
@@ -400,14 +472,16 @@ private predicate correlatedConditions(SsaSourceVariable npecand, ConditionBlock
|
||||
cond1.getCondition() = v.getAUse() and
|
||||
cond2.getCondition() = v.getAUse() and
|
||||
inverted = false
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, boolean branch1, boolean branch2 |
|
||||
cond1.getCondition() = nullGuard(v, branch1, true) and
|
||||
cond1.getCondition() = nullGuard(v, branch1.booleanNot(), false) and
|
||||
cond2.getCondition() = nullGuard(v, branch2, true) and
|
||||
cond2.getCondition() = nullGuard(v, branch2.booleanNot(), false) and
|
||||
inverted = branch1.booleanXor(branch2)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, RValue rv1, RValue rv2, int k, boolean branch1, boolean branch2 |
|
||||
rv1 = v.getAUse() and
|
||||
rv2 = v.getAUse() and
|
||||
@@ -416,12 +490,14 @@ private predicate correlatedConditions(SsaSourceVariable npecand, ConditionBlock
|
||||
cond2.getCondition() = integerGuard(rv2, branch2, k, true) and
|
||||
cond2.getCondition() = integerGuard(rv2, branch2.booleanNot(), k, false) and
|
||||
inverted = branch1.booleanXor(branch2)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, int k, boolean branch1, boolean branch2 |
|
||||
cond1.getCondition() = intBoundGuard(v.getAUse(), branch1, k) and
|
||||
cond2.getCondition() = intBoundGuard(v.getAUse(), branch2, k) and
|
||||
inverted = branch1.booleanXor(branch2)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, EnumConstant c, boolean pol1, boolean pol2 |
|
||||
cond1.getCondition() = enumConstEquality(v.getAUse(), pol1, c) and
|
||||
cond2.getCondition() = enumConstEquality(v.getAUse(), pol2, c) and
|
||||
@@ -435,27 +511,38 @@ private predicate correlatedConditions(SsaSourceVariable npecand, ConditionBlock
|
||||
* this time restricted based on pairs of correlated conditions consistent with `cond1`
|
||||
* evaluating to `branch`.
|
||||
*/
|
||||
private predicate varMaybeNullInBlock_corrCond(SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion, ConditionBlock cond1, boolean branch) {
|
||||
private predicate varMaybeNullInBlock_corrCond(
|
||||
SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion,
|
||||
ConditionBlock cond1, boolean branch
|
||||
) {
|
||||
exists(SsaSourceVariable npecand | npecand = ssa.getSourceVariable() |
|
||||
nullDerefCandidateVariable(npecand) and correlatedConditions(npecand, cond1, _, _)
|
||||
) and
|
||||
(
|
||||
varConditionallyNull(ssa, cond1, branch) or
|
||||
not varConditionallyNull(ssa, cond1, _) and (branch = true or branch = false)
|
||||
varConditionallyNull(ssa, cond1, branch)
|
||||
or
|
||||
not varConditionallyNull(ssa, cond1, _) and
|
||||
(branch = true or branch = false)
|
||||
) and
|
||||
varMaybeNull(ssa, _, _) and bb = ssa.getBasicBlock() and storedcompletion = false and origin = ssa
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
origin = ssa
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion |
|
||||
varMaybeNullInBlock_corrCond(origin, midssa, mid, midstoredcompletion, cond1, branch) and
|
||||
(
|
||||
cond1 = mid and cond1.getTestSuccessor(branch) = bb or
|
||||
cond1 = mid and cond1.getTestSuccessor(branch) = bb
|
||||
or
|
||||
exists(ConditionBlock cond2, boolean inverted, boolean branch2 |
|
||||
cond2 = mid and
|
||||
correlatedConditions(_, cond1, cond2, inverted) and
|
||||
cond2.getTestSuccessor(branch2) = bb and
|
||||
branch = branch2.booleanXor(inverted)
|
||||
) or
|
||||
cond1 != mid and not exists(ConditionBlock cond2 | cond2 = mid and correlatedConditions(_, cond1, cond2, _))
|
||||
)
|
||||
or
|
||||
cond1 != mid and
|
||||
not exists(ConditionBlock cond2 | cond2 = mid and correlatedConditions(_, cond1, cond2, _))
|
||||
) and
|
||||
nullVarStep(midssa, mid, midstoredcompletion, ssa, bb, storedcompletion)
|
||||
)
|
||||
@@ -479,56 +566,78 @@ newtype TrackVarKind =
|
||||
TrackVarKindInt()
|
||||
|
||||
/** A variable that might be relevant as a tracking variable for the NPE candidate. */
|
||||
private predicate trackingVar(SsaSourceVariable npecand, SsaExplicitUpdate trackssa, SsaSourceVariable trackvar, TrackVarKind kind, Expr init) {
|
||||
private predicate trackingVar(
|
||||
SsaSourceVariable npecand, SsaExplicitUpdate trackssa, SsaSourceVariable trackvar,
|
||||
TrackVarKind kind, Expr init
|
||||
) {
|
||||
exists(ConditionBlock cond |
|
||||
interestingCond(npecand, cond) and
|
||||
varMaybeNullInBlock(_, npecand, cond, _) and
|
||||
cond.getCondition().getAChildExpr*() = trackvar.getAnAccess() and
|
||||
trackssa.getSourceVariable() = trackvar and
|
||||
trackssa.getDefiningExpr().(VariableAssign).getSource() = init
|
||||
|
|
||||
init instanceof NullLiteral and kind = TrackVarKindNull() or
|
||||
init = clearlyNotNullExpr() and kind = TrackVarKindNull() or
|
||||
init instanceof BooleanLiteral and kind = TrackVarKindBool() or
|
||||
init.(VarAccess).getVariable() instanceof EnumConstant and kind = TrackVarKindEnum() or
|
||||
|
|
||||
init instanceof NullLiteral and kind = TrackVarKindNull()
|
||||
or
|
||||
init = clearlyNotNullExpr() and kind = TrackVarKindNull()
|
||||
or
|
||||
init instanceof BooleanLiteral and kind = TrackVarKindBool()
|
||||
or
|
||||
init.(VarAccess).getVariable() instanceof EnumConstant and kind = TrackVarKindEnum()
|
||||
or
|
||||
exists(init.(ConstantIntegerExpr).getIntValue()) and kind = TrackVarKindInt()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an expression that tests the value of a given tracking variable. */
|
||||
private Expr trackingVarGuard(SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, boolean branch, boolean isA) {
|
||||
private Expr trackingVarGuard(
|
||||
SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, boolean branch, boolean isA
|
||||
) {
|
||||
exists(Expr init | trackingVar(_, trackssa, trackvar, kind, init) |
|
||||
result = basicOrCustomNullGuard(trackvar.getAnAccess(), branch, isA) and kind = TrackVarKindNull() or
|
||||
result = trackvar.getAnAccess() and kind = TrackVarKindBool() and (branch = true or branch = false) and isA = branch or
|
||||
result = basicOrCustomNullGuard(trackvar.getAnAccess(), branch, isA) and
|
||||
kind = TrackVarKindNull()
|
||||
or
|
||||
result = trackvar.getAnAccess() and
|
||||
kind = TrackVarKindBool() and
|
||||
(branch = true or branch = false) and
|
||||
isA = branch
|
||||
or
|
||||
exists(boolean polarity, EnumConstant c, EnumConstant initc |
|
||||
initc.getAnAccess() = init and
|
||||
kind = TrackVarKindEnum() and
|
||||
result = enumConstEquality(trackvar.getAnAccess(), polarity, c) and
|
||||
(
|
||||
initc = c and branch = polarity.booleanNot() and isA = false or
|
||||
initc = c and branch = polarity and isA = true or
|
||||
initc = c and branch = polarity.booleanNot() and isA = false
|
||||
or
|
||||
initc = c and branch = polarity and isA = true
|
||||
or
|
||||
initc != c and branch = polarity and isA = false
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(int k |
|
||||
init.(ConstantIntegerExpr).getIntValue() = k and
|
||||
kind = TrackVarKindInt()
|
||||
|
|
||||
result = integerGuard(trackvar.getAnAccess(), branch, k, isA) or
|
||||
|
|
||||
result = integerGuard(trackvar.getAnAccess(), branch, k, isA)
|
||||
or
|
||||
exists(int k2 |
|
||||
result = integerGuard(trackvar.getAnAccess(), branch.booleanNot(), k2, true) and
|
||||
isA = false and
|
||||
k2 != k
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(int bound, boolean branch_with_lower_bound |
|
||||
result = intBoundGuard(trackvar.getAnAccess(), branch_with_lower_bound, bound) and
|
||||
isA = false
|
||||
|
|
||||
branch = branch_with_lower_bound and k < bound or
|
||||
|
|
||||
branch = branch_with_lower_bound and k < bound
|
||||
or
|
||||
branch = branch_with_lower_bound.booleanNot() and bound <= k
|
||||
)
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(EqualityTest eqtest, boolean branch0, boolean polarity, BooleanLiteral boollit |
|
||||
eqtest = result and
|
||||
eqtest.hasOperands(trackingVarGuard(trackssa, trackvar, kind, branch0, isA), boollit) and
|
||||
@@ -538,19 +647,36 @@ private Expr trackingVarGuard(SsaVariable trackssa, SsaSourceVariable trackvar,
|
||||
}
|
||||
|
||||
/** An update to a tracking variable that is contained fully in either A or B. */
|
||||
private predicate isReset(SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, SsaExplicitUpdate update, boolean isA) {
|
||||
private predicate isReset(
|
||||
SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, SsaExplicitUpdate update,
|
||||
boolean isA
|
||||
) {
|
||||
exists(Expr init, Expr e |
|
||||
trackingVar(_, trackssa, trackvar, kind, init) and
|
||||
update.getSourceVariable() = trackvar and
|
||||
e = update.getDefiningExpr().(VariableAssign).getSource()
|
||||
|
|
||||
e instanceof NullLiteral and kind = TrackVarKindNull() and isA = true or
|
||||
e = clearlyNotNullExpr() and kind = TrackVarKindNull() and isA = false or
|
||||
e.(BooleanLiteral).getBooleanValue() = isA and kind = TrackVarKindBool() or
|
||||
e.(VarAccess).getVariable().(EnumConstant) = init.(VarAccess).getVariable() and kind = TrackVarKindEnum() and isA = true or
|
||||
e.(VarAccess).getVariable().(EnumConstant) != init.(VarAccess).getVariable() and kind = TrackVarKindEnum() and isA = false or
|
||||
e.(ConstantIntegerExpr).getIntValue() = init.(ConstantIntegerExpr).getIntValue() and kind = TrackVarKindInt() and isA = true or
|
||||
e.(ConstantIntegerExpr).getIntValue() != init.(ConstantIntegerExpr).getIntValue() and kind = TrackVarKindInt() and isA = false
|
||||
|
|
||||
e instanceof NullLiteral and kind = TrackVarKindNull() and isA = true
|
||||
or
|
||||
e = clearlyNotNullExpr() and kind = TrackVarKindNull() and isA = false
|
||||
or
|
||||
e.(BooleanLiteral).getBooleanValue() = isA and kind = TrackVarKindBool()
|
||||
or
|
||||
e.(VarAccess).getVariable().(EnumConstant) = init.(VarAccess).getVariable() and
|
||||
kind = TrackVarKindEnum() and
|
||||
isA = true
|
||||
or
|
||||
e.(VarAccess).getVariable().(EnumConstant) != init.(VarAccess).getVariable() and
|
||||
kind = TrackVarKindEnum() and
|
||||
isA = false
|
||||
or
|
||||
e.(ConstantIntegerExpr).getIntValue() = init.(ConstantIntegerExpr).getIntValue() and
|
||||
kind = TrackVarKindInt() and
|
||||
isA = true
|
||||
or
|
||||
e.(ConstantIntegerExpr).getIntValue() != init.(ConstantIntegerExpr).getIntValue() and
|
||||
kind = TrackVarKindInt() and
|
||||
isA = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -560,10 +686,17 @@ newtype TrackedValue =
|
||||
TrackedValueB() or
|
||||
TrackedValueUnknown()
|
||||
|
||||
private TrackedValue trackValAorB(boolean isA) { isA = true and result = TrackedValueA() or isA = false and result = TrackedValueB() }
|
||||
private TrackedValue trackValAorB(boolean isA) {
|
||||
isA = true and result = TrackedValueA()
|
||||
or
|
||||
isA = false and result = TrackedValueB()
|
||||
}
|
||||
|
||||
/** A control flow edge passing through a condition that implies a specific value for a tracking variable. */
|
||||
private predicate stepImplies(BasicBlock bb1, BasicBlock bb2, SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, boolean isA) {
|
||||
private predicate stepImplies(
|
||||
BasicBlock bb1, BasicBlock bb2, SsaVariable trackssa, SsaSourceVariable trackvar,
|
||||
TrackVarKind kind, boolean isA
|
||||
) {
|
||||
exists(ConditionBlock cond, boolean branch |
|
||||
cond = bb1 and
|
||||
cond.getTestSuccessor(branch) = bb2 and
|
||||
@@ -575,7 +708,10 @@ private predicate stepImplies(BasicBlock bb1, BasicBlock bb2, SsaVariable tracks
|
||||
* This is again the transitive closure of `nullVarStep` similarly to `varMaybeNullInBlock`, but
|
||||
* this time restricted based on a tracking variable.
|
||||
*/
|
||||
private predicate varMaybeNullInBlock_trackVar(SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion, SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, TrackedValue trackvalue) {
|
||||
private predicate varMaybeNullInBlock_trackVar(
|
||||
SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion,
|
||||
SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, TrackedValue trackvalue
|
||||
) {
|
||||
exists(SsaSourceVariable npecand | npecand = ssa.getSourceVariable() |
|
||||
nullDerefCandidateVariable(npecand) and trackingVar(npecand, trackssa, trackvar, kind, _)
|
||||
) and
|
||||
@@ -583,8 +719,10 @@ private predicate varMaybeNullInBlock_trackVar(SsaVariable origin, SsaVariable s
|
||||
exists(SsaVariable init, boolean isA |
|
||||
init.getSourceVariable() = trackvar and
|
||||
init.isLiveAtEndOfBlock(bb) and
|
||||
isReset(trackssa, trackvar, kind, init, isA) and trackvalue = trackValAorB(isA)
|
||||
) or
|
||||
isReset(trackssa, trackvar, kind, init, isA) and
|
||||
trackvalue = trackValAorB(isA)
|
||||
)
|
||||
or
|
||||
trackvalue = TrackedValueUnknown() and
|
||||
not exists(SsaVariable init |
|
||||
init.getSourceVariable() = trackvar and
|
||||
@@ -592,13 +730,18 @@ private predicate varMaybeNullInBlock_trackVar(SsaVariable origin, SsaVariable s
|
||||
isReset(trackssa, trackvar, kind, init, _)
|
||||
)
|
||||
) and
|
||||
varMaybeNull(ssa, _, _) and bb = ssa.getBasicBlock() and storedcompletion = false and origin = ssa
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
origin = ssa
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion, TrackedValue trackvalue0 |
|
||||
varMaybeNullInBlock_trackVar(origin, midssa, mid, midstoredcompletion, trackssa, trackvar, kind, trackvalue0) and
|
||||
varMaybeNullInBlock_trackVar(origin, midssa, mid, midstoredcompletion, trackssa, trackvar, kind,
|
||||
trackvalue0) and
|
||||
nullVarStep(midssa, mid, midstoredcompletion, ssa, bb, storedcompletion) and
|
||||
(
|
||||
trackvalue0 = TrackedValueUnknown() or
|
||||
trackvalue0 = TrackedValueUnknown()
|
||||
or
|
||||
// A step that implies a value that contradicts the current value is not allowed.
|
||||
exists(boolean isA | trackvalue0 = trackValAorB(isA) |
|
||||
not stepImplies(mid, bb, trackssa, trackvar, kind, isA.booleanNot())
|
||||
@@ -611,7 +754,10 @@ private predicate varMaybeNullInBlock_trackVar(SsaVariable origin, SsaVariable s
|
||||
update.getBasicBlock() = bb
|
||||
) and
|
||||
(
|
||||
exists(boolean isA | stepImplies(mid, bb, trackssa, trackvar, kind, isA) | trackvalue = trackValAorB(isA)) or
|
||||
exists(boolean isA | stepImplies(mid, bb, trackssa, trackvar, kind, isA) |
|
||||
trackvalue = trackValAorB(isA)
|
||||
)
|
||||
or
|
||||
not stepImplies(mid, bb, trackssa, trackvar, kind, _) and trackvalue = trackvalue0
|
||||
)
|
||||
or
|
||||
@@ -619,8 +765,11 @@ private predicate varMaybeNullInBlock_trackVar(SsaVariable origin, SsaVariable s
|
||||
exists(SsaUpdate update |
|
||||
update.getSourceVariable() = trackvar and
|
||||
update.getBasicBlock() = bb
|
||||
|
|
||||
exists(boolean isA | isReset(trackssa, trackvar, kind, update, isA) | trackvalue = trackValAorB(isA)) or
|
||||
|
|
||||
exists(boolean isA | isReset(trackssa, trackvar, kind, update, isA) |
|
||||
trackvalue = trackValAorB(isA)
|
||||
)
|
||||
or
|
||||
not isReset(trackssa, trackvar, kind, update, _) and trackvalue = TrackedValueUnknown()
|
||||
)
|
||||
)
|
||||
@@ -636,14 +785,12 @@ predicate nullDeref(SsaSourceVariable v, VarAccess va, string msg, Expr reason)
|
||||
varMaybeNull(origin, msg, reason) and
|
||||
ssa.getSourceVariable() = v and
|
||||
firstVarDereferenceInBlock(bb, ssa, va) and
|
||||
forall(ConditionBlock cond |
|
||||
correlatedConditions(v, cond, _, _)
|
||||
|
|
||||
forall(ConditionBlock cond | correlatedConditions(v, cond, _, _) |
|
||||
varMaybeNullInBlock_corrCond(origin, ssa, bb, _, cond, _)
|
||||
) and
|
||||
forall(SsaVariable guardssa, SsaSourceVariable guardvar, TrackVarKind kind |
|
||||
trackingVar(v, guardssa, guardvar, kind, _)
|
||||
|
|
||||
|
|
||||
varMaybeNullInBlock_trackVar(origin, ssa, bb, _, guardssa, guardvar, kind, _)
|
||||
)
|
||||
)
|
||||
@@ -656,18 +803,17 @@ predicate alwaysNullDeref(SsaSourceVariable v, VarAccess va) {
|
||||
exists(BasicBlock bb, SsaVariable ssa |
|
||||
forall(SsaVariable def | def = ssa.getAnUltimateDefinition() |
|
||||
def.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = alwaysNullExpr()
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(boolean branch |
|
||||
nullGuard(ssa, branch, true).directlyControls(bb, branch) and
|
||||
not clearlyNotNull(ssa)
|
||||
)
|
||||
|
|
||||
|
|
||||
// Exclude fields as they might not have an accurate ssa representation.
|
||||
not v.getVariable() instanceof Field and
|
||||
firstVarDereferenceInBlock(bb, ssa, va) and
|
||||
ssa.getSourceVariable() = v and
|
||||
not exists(boolean branch |
|
||||
nullGuard(ssa, branch, false).directlyControls(bb, branch)
|
||||
)
|
||||
not exists(boolean branch | nullGuard(ssa, branch, false).directlyControls(bb, branch))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,9 +22,14 @@ private predicate fieldAccessInCallable(FieldAccess fa, Field f, Callable c) {
|
||||
|
||||
cached
|
||||
private newtype TSsaSourceVariable =
|
||||
TLocalVar(Callable c, LocalScopeVariable v) { c = v.getCallable() or c = v.getAnAccess().getEnclosingCallable() } or
|
||||
TLocalVar(Callable c, LocalScopeVariable v) {
|
||||
c = v.getCallable() or c = v.getAnAccess().getEnclosingCallable()
|
||||
} or
|
||||
TPlainField(Callable c, Field f) {
|
||||
exists(FieldRead fr | fieldAccessInCallable(fr, f, c) and (fr.isOwnFieldAccess() or f.isStatic()))
|
||||
exists(FieldRead fr |
|
||||
fieldAccessInCallable(fr, f, c) and
|
||||
(fr.isOwnFieldAccess() or f.isStatic())
|
||||
)
|
||||
} or
|
||||
TEnclosingField(Callable c, Field f, RefType t) {
|
||||
exists(FieldRead fr | fieldAccessInCallable(fr, f, c) and fr.isEnclosingFieldAccess(t))
|
||||
@@ -58,13 +63,21 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
*/
|
||||
cached
|
||||
VarAccess getAnAccess() {
|
||||
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) and result = v.getAnAccess() and result.getEnclosingCallable() = c) or
|
||||
exists(LocalScopeVariable v, Callable c |
|
||||
this = TLocalVar(c, v) and result = v.getAnAccess() and result.getEnclosingCallable() = c
|
||||
)
|
||||
or
|
||||
exists(Field f, Callable c | fieldAccessInCallable(result, f, c) |
|
||||
(result.(FieldAccess).isOwnFieldAccess() or f.isStatic()) and this = TPlainField(c, f)
|
||||
(result.(FieldAccess).isOwnFieldAccess() or f.isStatic()) and
|
||||
this = TPlainField(c, f)
|
||||
or
|
||||
exists(RefType t | this = TEnclosingField(c, f, t) and result.(FieldAccess).isEnclosingFieldAccess(t))
|
||||
exists(RefType t |
|
||||
this = TEnclosingField(c, f, t) and result.(FieldAccess).isEnclosingFieldAccess(t)
|
||||
)
|
||||
or
|
||||
exists(SsaSourceVariable q | result.getQualifier() = q.getAnAccess() and this = TQualifiedField(c, q, f))
|
||||
exists(SsaSourceVariable q |
|
||||
result.getQualifier() = q.getAnAccess() and this = TQualifiedField(c, q, f)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,8 +91,11 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
|
||||
string toString() {
|
||||
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) |
|
||||
if c = v.getCallable() then result = v.getName() else result = c.getName() + "(..)." + v.getName()
|
||||
) or
|
||||
if c = v.getCallable()
|
||||
then result = v.getName()
|
||||
else result = c.getName() + "(..)." + v.getName()
|
||||
)
|
||||
or
|
||||
result = this.(SsaSourceField).ppQualifier() + "." + getVariable().toString()
|
||||
}
|
||||
|
||||
@@ -89,24 +105,23 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
* not have a specific source code location.
|
||||
*/
|
||||
private VarAccess getFirstAccess() {
|
||||
result = min(this.getAnAccess() as a order by
|
||||
a.getLocation().getStartLine(), a.getLocation().getStartColumn())
|
||||
result = min(this.getAnAccess() as a
|
||||
order by
|
||||
a.getLocation().getStartLine(), a.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation()) or
|
||||
exists(LocalScopeVariable v | this = TLocalVar(_, v) and result = v.getLocation())
|
||||
or
|
||||
this instanceof SsaSourceField and result = getFirstAccess().getLocation()
|
||||
}
|
||||
|
||||
/** Gets the type of this variable. */
|
||||
Type getType() {
|
||||
result = this.getVariable().getType()
|
||||
}
|
||||
Type getType() { result = this.getVariable().getType() }
|
||||
|
||||
/** Gets the qualifier, if any. */
|
||||
SsaSourceVariable getQualifier() {
|
||||
this = TQualifiedField(_, result, _)
|
||||
}
|
||||
SsaSourceVariable getQualifier() { this = TQualifiedField(_, result, _) }
|
||||
|
||||
/** Gets an SSA variable that has this variable as its underlying source variable. */
|
||||
SsaVariable getAnSsaVariable() { result.getSourceVariable() = this }
|
||||
@@ -122,26 +137,17 @@ class SsaSourceField extends SsaSourceVariable {
|
||||
}
|
||||
|
||||
/** Gets the field corresponding to this named field. */
|
||||
Field getField() {
|
||||
result = getVariable()
|
||||
}
|
||||
Field getField() { result = getVariable() }
|
||||
|
||||
/** Gets a string representation of the qualifier. */
|
||||
string ppQualifier() {
|
||||
exists(Field f | this = TPlainField(_, f) |
|
||||
if f.isStatic() then
|
||||
result = f.getDeclaringType().getQualifiedName()
|
||||
else
|
||||
result = "this"
|
||||
if f.isStatic() then result = f.getDeclaringType().getQualifiedName() else result = "this"
|
||||
)
|
||||
or
|
||||
exists(Field f, RefType t | this = TEnclosingField(_, f, t) |
|
||||
result = t.toString() + ".this"
|
||||
)
|
||||
exists(Field f, RefType t | this = TEnclosingField(_, f, t) | result = t.toString() + ".this")
|
||||
or
|
||||
exists(SsaSourceVariable q | this = TQualifiedField(_, q, _) |
|
||||
result = q.toString()
|
||||
)
|
||||
exists(SsaSourceVariable q | this = TQualifiedField(_, q, _) | result = q.toString())
|
||||
}
|
||||
|
||||
/** Holds if the field itself or any of the fields part of the qualifier are volatile. */
|
||||
@@ -152,7 +158,6 @@ class SsaSourceField extends SsaSourceVariable {
|
||||
}
|
||||
|
||||
private module TrackedVariablesImpl {
|
||||
|
||||
/** Gets the number of accesses of `f`. */
|
||||
private int numberOfAccesses(SsaSourceField f) {
|
||||
result = strictcount(FieldAccess fa | fa = f.getAnAccess())
|
||||
@@ -168,9 +173,7 @@ private module TrackedVariablesImpl {
|
||||
}
|
||||
|
||||
/** Holds if `f` is accessed more than once or inside a loop. */
|
||||
private predicate multiAccessed(SsaSourceField f) {
|
||||
loopAccessed(f) or 1 < numberOfAccesses(f)
|
||||
}
|
||||
private predicate multiAccessed(SsaSourceField f) { loopAccessed(f) or 1 < numberOfAccesses(f) }
|
||||
|
||||
/**
|
||||
* Holds if `f` is a field that is interesting as a basis for SSA.
|
||||
@@ -186,9 +189,7 @@ private module TrackedVariablesImpl {
|
||||
* memory and we are forced to assume that the value can change at any point.
|
||||
*/
|
||||
cached
|
||||
predicate trackField(SsaSourceField f) {
|
||||
multiAccessed(f) and not f.isVolatile()
|
||||
}
|
||||
predicate trackField(SsaSourceField f) { multiAccessed(f) and not f.isVolatile() }
|
||||
|
||||
/**
|
||||
* The variables that form the basis of the non-trivial SSA construction.
|
||||
@@ -202,28 +203,30 @@ private module TrackedVariablesImpl {
|
||||
}
|
||||
}
|
||||
|
||||
class TrackedField extends TrackedVar, SsaSourceField {
|
||||
}
|
||||
|
||||
class TrackedField extends TrackedVar, SsaSourceField { }
|
||||
}
|
||||
private import TrackedVariablesImpl
|
||||
|
||||
private cached module SsaImpl {
|
||||
|
||||
cached
|
||||
private module SsaImpl {
|
||||
/** Gets the destination variable of an update of a tracked variable. */
|
||||
cached
|
||||
TrackedVar getDestVar(VariableUpdate upd) {
|
||||
result.getAnAccess() = upd.(Assignment).getDest() or
|
||||
result.getAnAccess() = upd.(Assignment).getDest()
|
||||
or
|
||||
exists(LocalVariableDecl v | v = upd.(LocalVariableDeclExpr).getVariable() |
|
||||
result = TLocalVar(v.getCallable(), v)
|
||||
) or
|
||||
)
|
||||
or
|
||||
result.getAnAccess() = upd.(UnaryAssignExpr).getExpr()
|
||||
}
|
||||
|
||||
/** Holds if `n` must update the locally tracked variable `v`. */
|
||||
cached
|
||||
predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and b.getNode(i) = n or
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
|
||||
b.getNode(i) = n
|
||||
or
|
||||
certainVariableUpdate(v.getQualifier(), n, b, i)
|
||||
}
|
||||
|
||||
@@ -251,7 +254,9 @@ private cached module SsaImpl {
|
||||
* scope, and `closurevar` is the variable in the closure.
|
||||
*/
|
||||
private ControlFlowNode captureNode(TrackedVar capturedvar, TrackedVar closurevar) {
|
||||
exists(LocalScopeVariable v, Callable inner, Callable outer, NestedClass innerclass, VarAccess va |
|
||||
exists(
|
||||
LocalScopeVariable v, Callable inner, Callable outer, NestedClass innerclass, VarAccess va
|
||||
|
|
||||
va.getVariable() = v and
|
||||
inner = va.getEnclosingCallable() and
|
||||
outer = v.getCallable() and
|
||||
@@ -270,7 +275,9 @@ private cached module SsaImpl {
|
||||
}
|
||||
|
||||
/** Holds if the value of `v` is captured in `b` at index `i`. */
|
||||
private predicate variableCapture(TrackedVar capturedvar, TrackedVar closurevar, BasicBlock b, int i) {
|
||||
private predicate variableCapture(
|
||||
TrackedVar capturedvar, TrackedVar closurevar, BasicBlock b, int i
|
||||
) {
|
||||
b.getNode(i) = captureNode(capturedvar, closurevar)
|
||||
}
|
||||
|
||||
@@ -285,13 +292,13 @@ private cached module SsaImpl {
|
||||
|
||||
private predicate liveAtEntry(TrackedVar v, BasicBlock b) {
|
||||
exists(int i | variableUseOrCapture(v, b, i) |
|
||||
not exists(int j | certainVariableUpdate(v, _, b, j) | j < i))
|
||||
not exists(int j | certainVariableUpdate(v, _, b, j) | j < i)
|
||||
)
|
||||
or
|
||||
liveAtExit(v, b) and not certainVariableUpdate(v, _, b, _)
|
||||
}
|
||||
private predicate liveAtExit(TrackedVar v, BasicBlock b) {
|
||||
liveAtEntry(v, b.getABBSuccessor())
|
||||
}
|
||||
|
||||
private predicate liveAtExit(TrackedVar v, BasicBlock b) { liveAtEntry(v, b.getABBSuccessor()) }
|
||||
|
||||
/*
|
||||
* The SSA construction for a field `f` relies on implicit update nodes at
|
||||
@@ -316,8 +323,10 @@ private cached module SsaImpl {
|
||||
* update, since it is an initialization and therefore cannot alias.
|
||||
*/
|
||||
private predicate init(FieldWrite fw) {
|
||||
fw.getEnclosingCallable() instanceof InitializerMethod or
|
||||
fw.getEnclosingCallable() instanceof Constructor and fw.isOwnFieldAccess() or
|
||||
fw.getEnclosingCallable() instanceof InitializerMethod
|
||||
or
|
||||
fw.getEnclosingCallable() instanceof Constructor and fw.isOwnFieldAccess()
|
||||
or
|
||||
exists(LocalVariableDecl v |
|
||||
v.getAnAccess() = fw.getQualifier() and
|
||||
forex(VariableAssign va | va.getDestVar() = v and exists(va.getSource()) |
|
||||
@@ -377,7 +386,8 @@ private cached module SsaImpl {
|
||||
ma.getQualifier() instanceof SuperAccess
|
||||
) and
|
||||
c1.getDeclaringType() = t1 and
|
||||
if t1 instanceof InnerClass then
|
||||
if t1 instanceof InnerClass
|
||||
then
|
||||
innerclassSupertypeStar(t1, ma.getCallee().getSourceDeclaration().getDeclaringType()) and
|
||||
not exists(ma.getQualifier().(ThisAccess).getQualifier()) and
|
||||
not exists(ma.getQualifier().(SuperAccess).getQualifier())
|
||||
@@ -386,8 +396,10 @@ private cached module SsaImpl {
|
||||
}
|
||||
|
||||
private Callable tgt(Call c) {
|
||||
result = viableImpl(c) or
|
||||
result = getRunnerTarget(c) or
|
||||
result = viableImpl(c)
|
||||
or
|
||||
result = getRunnerTarget(c)
|
||||
or
|
||||
c instanceof ConstructorCall and result = c.getCallee().getSourceDeclaration()
|
||||
}
|
||||
|
||||
@@ -436,7 +448,11 @@ private cached module SsaImpl {
|
||||
*/
|
||||
private predicate callDefUseRank(TrackedField f, BasicBlock b, int rankix, int i) {
|
||||
updateCandidate(f, _, b, _) and
|
||||
i = rank[rankix](int j | certainVariableUpdate(f, _, b, j) or variableUseOrCapture(f, b, j) or updateCandidate(f, _, b, j))
|
||||
i = rank[rankix](int j |
|
||||
certainVariableUpdate(f, _, b, j) or
|
||||
variableUseOrCapture(f, b, j) or
|
||||
updateCandidate(f, _, b, j)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,7 +466,7 @@ private cached module SsaImpl {
|
||||
or
|
||||
variableUseOrCapture(f, b, i)
|
||||
or
|
||||
exists(int j | liveAtRank(f, b, rankix+1, j) and not certainVariableUpdate(f, _, b, j))
|
||||
exists(int j | liveAtRank(f, b, rankix + 1, j) and not certainVariableUpdate(f, _, b, j))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -491,16 +507,13 @@ private cached module SsaImpl {
|
||||
|
||||
/** A restriction of the call graph to the parts that are relevant for `updatesNamedFieldPart1`. */
|
||||
private class PrunedCallable extends Callable {
|
||||
PrunedCallable() {
|
||||
pruneFromLeft(this) and pruneFromRight(this)
|
||||
}
|
||||
PrunedCallable() { pruneFromLeft(this) and pruneFromRight(this) }
|
||||
}
|
||||
|
||||
private predicate callEdgePruned(PrunedCallable c1, PrunedCallable c2) {
|
||||
callEdge(c1, c2)
|
||||
}
|
||||
private predicate callEdgePruned(PrunedCallable c1, PrunedCallable c2) { callEdge(c1, c2) }
|
||||
|
||||
private predicate callEdgePlus(PrunedCallable c1, PrunedCallable c2) = fastTC(callEdgePruned/2)(c1, c2)
|
||||
private predicate callEdgePlus(PrunedCallable c1, PrunedCallable c2) =
|
||||
fastTC(callEdgePruned/2)(c1, c2)
|
||||
|
||||
pragma[noinline]
|
||||
private predicate updatesNamedFieldPrefix(Call call, TrackedField f, Callable c1, Field field) {
|
||||
@@ -510,9 +523,7 @@ private cached module SsaImpl {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate generalSetterProj(Callable c, Field f) {
|
||||
generalSetter(c, f, _)
|
||||
}
|
||||
private predicate generalSetterProj(Callable c, Field f) { generalSetter(c, f, _) }
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of `f` on some instance, which may or
|
||||
@@ -547,7 +558,9 @@ private cached module SsaImpl {
|
||||
/** Holds if `n` might update the locally tracked variable `v`. */
|
||||
cached
|
||||
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(Call c | c = n | updatesNamedField(c, v, _)) and b.getNode(i) = n or
|
||||
exists(Call c | c = n | updatesNamedField(c, v, _)) and
|
||||
b.getNode(i) = n
|
||||
or
|
||||
uncertainVariableUpdate(v.getQualifier(), n, b, i)
|
||||
}
|
||||
|
||||
@@ -568,12 +581,11 @@ private cached module SsaImpl {
|
||||
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
|
||||
cached
|
||||
predicate hasEntryDef(TrackedVar v, BasicBlock b) {
|
||||
exists(LocalScopeVariable l, Callable c |
|
||||
v = TLocalVar(c, l) and c.getBody() = b
|
||||
|
|
||||
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
|
||||
l instanceof Parameter or
|
||||
l.getCallable() != c
|
||||
) or
|
||||
)
|
||||
or
|
||||
v instanceof SsaSourceField and v.getEnclosingCallable().getBody() = b and liveAtEntry(v, b)
|
||||
}
|
||||
|
||||
@@ -586,8 +598,8 @@ private cached module SsaImpl {
|
||||
* dominator tree. Thus, reaching definitions can be calculated in terms of
|
||||
* dominance.
|
||||
*/
|
||||
cached module SsaDefReaches {
|
||||
|
||||
cached
|
||||
module SsaDefReaches {
|
||||
/**
|
||||
* Holds if `rankix` is the rank the index `i` at which there is an SSA definition or use of
|
||||
* `v` in the basic block `b`.
|
||||
@@ -597,7 +609,9 @@ private cached module SsaImpl {
|
||||
* basic blocks.
|
||||
*/
|
||||
private predicate defUseRank(TrackedVar v, BasicBlock b, int rankix, int i) {
|
||||
i = rank[rankix](int j | any(TrackedSsaDef def).definesAt(v, b, j) or variableUseOrCapture(v, b, j))
|
||||
i = rank[rankix](int j |
|
||||
any(TrackedSsaDef def).definesAt(v, b, j) or variableUseOrCapture(v, b, j)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the maximum rank index for the given variable and basic block. */
|
||||
@@ -615,8 +629,11 @@ private cached module SsaImpl {
|
||||
|
||||
/** Holds if the SSA definition reaches the rank index `rankix` in its own basic block `b`. */
|
||||
private predicate ssaDefReachesRank(TrackedVar v, TrackedSsaDef def, BasicBlock b, int rankix) {
|
||||
ssaDefRank(v, def, b, rankix) or
|
||||
ssaDefReachesRank(v, def, b, rankix-1) and rankix <= lastRank(v, b) and not ssaDefRank(v, _, b, rankix)
|
||||
ssaDefRank(v, def, b, rankix)
|
||||
or
|
||||
ssaDefReachesRank(v, def, b, rankix - 1) and
|
||||
rankix <= lastRank(v, b) and
|
||||
not ssaDefRank(v, _, b, rankix)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -627,7 +644,8 @@ private cached module SsaImpl {
|
||||
predicate ssaDefReachesEndOfBlock(TrackedVar v, TrackedSsaDef def, BasicBlock b) {
|
||||
liveAtExit(v, b) and
|
||||
(
|
||||
ssaDefReachesRank(v, def, b, lastRank(v, b)) or
|
||||
ssaDefReachesRank(v, def, b, lastRank(v, b))
|
||||
or
|
||||
exists(BasicBlock idom |
|
||||
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
ssaDefReachesEndOfBlock(v, def, idom) and
|
||||
@@ -654,7 +672,8 @@ private cached module SsaImpl {
|
||||
*/
|
||||
cached
|
||||
predicate ssaDefReachesUse(TrackedVar v, TrackedSsaDef def, RValue use) {
|
||||
ssaDefReachesUseWithinBlock(v, def, use) or
|
||||
ssaDefReachesUseWithinBlock(v, def, use)
|
||||
or
|
||||
exists(BasicBlock b |
|
||||
variableUse(v, use, b, _) and
|
||||
ssaDefReachesEndOfBlock(v, def, b.getABBPredecessor()) and
|
||||
@@ -667,7 +686,9 @@ private cached module SsaImpl {
|
||||
* `closurevar` in the same basic block without crossing another SSA
|
||||
* definition of `v`.
|
||||
*/
|
||||
private predicate ssaDefReachesCaptureWithinBlock(TrackedVar v, TrackedSsaDef def, TrackedVar closurevar) {
|
||||
private predicate ssaDefReachesCaptureWithinBlock(
|
||||
TrackedVar v, TrackedSsaDef def, TrackedVar closurevar
|
||||
) {
|
||||
exists(BasicBlock b, int rankix, int i |
|
||||
ssaDefReachesRank(v, def, b, rankix) and
|
||||
defUseRank(v, b, rankix, i) and
|
||||
@@ -681,7 +702,8 @@ private cached module SsaImpl {
|
||||
*/
|
||||
cached
|
||||
predicate ssaDefReachesCapture(TrackedVar v, TrackedSsaDef def, TrackedVar closurevar) {
|
||||
ssaDefReachesCaptureWithinBlock(v, def, closurevar) or
|
||||
ssaDefReachesCaptureWithinBlock(v, def, closurevar)
|
||||
or
|
||||
exists(BasicBlock b |
|
||||
variableCapture(v, closurevar, b, _) and
|
||||
ssaDefReachesEndOfBlock(v, def, b.getABBPredecessor()) and
|
||||
@@ -693,10 +715,12 @@ private cached module SsaImpl {
|
||||
* Holds if the SSA definition of `v` at `def` reaches `redef` in the same basic block
|
||||
* without crossing another SSA definition of `v`.
|
||||
*/
|
||||
private predicate ssaDefReachesUncertainDefWithinBlock(TrackedVar v, TrackedSsaDef def, SsaUncertainImplicitUpdate redef) {
|
||||
private predicate ssaDefReachesUncertainDefWithinBlock(
|
||||
TrackedVar v, TrackedSsaDef def, SsaUncertainImplicitUpdate redef
|
||||
) {
|
||||
exists(BasicBlock b, int rankix, int i |
|
||||
ssaDefReachesRank(v, def, b, rankix) and
|
||||
defUseRank(v, b, rankix+1, i) and
|
||||
defUseRank(v, b, rankix + 1, i) and
|
||||
redef.(TrackedSsaDef).definesAt(v, b, i)
|
||||
)
|
||||
}
|
||||
@@ -706,19 +730,20 @@ private cached module SsaImpl {
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
cached
|
||||
predicate ssaDefReachesUncertainDef(TrackedVar v, TrackedSsaDef def, SsaUncertainImplicitUpdate redef) {
|
||||
ssaDefReachesUncertainDefWithinBlock(v, def, redef) or
|
||||
predicate ssaDefReachesUncertainDef(
|
||||
TrackedVar v, TrackedSsaDef def, SsaUncertainImplicitUpdate redef
|
||||
) {
|
||||
ssaDefReachesUncertainDefWithinBlock(v, def, redef)
|
||||
or
|
||||
exists(BasicBlock b |
|
||||
redef.(TrackedSsaDef).definesAt(v, b, _) and
|
||||
ssaDefReachesEndOfBlock(v, def, b.getABBPredecessor()) and
|
||||
not ssaDefReachesUncertainDefWithinBlock(v, _, redef)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private module AdjacentUsesImpl {
|
||||
|
||||
/**
|
||||
* Holds if `rankix` is the rank the index `i` at which there is an SSA definition or explicit use of
|
||||
* `v` in the basic block `b`.
|
||||
@@ -733,9 +758,7 @@ private cached module SsaImpl {
|
||||
}
|
||||
|
||||
/** Holds if `v` is defined or used in `b`. */
|
||||
private predicate varOccursInBlock(TrackedVar v, BasicBlock b) {
|
||||
defUseRank(v, b, _, _)
|
||||
}
|
||||
private predicate varOccursInBlock(TrackedVar v, BasicBlock b) { defUseRank(v, b, _, _) }
|
||||
|
||||
/** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */
|
||||
private predicate blockPrecedesVar(TrackedVar v, BasicBlock b) {
|
||||
@@ -748,7 +771,8 @@ private cached module SsaImpl {
|
||||
* between `b1` and `b2`.
|
||||
*/
|
||||
private predicate varBlockReaches(TrackedVar v, BasicBlock b1, BasicBlock b2) {
|
||||
varOccursInBlock(v, b1) and b2 = b1.getABBSuccessor() or
|
||||
varOccursInBlock(v, b1) and b2 = b1.getABBSuccessor()
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(v, b1, mid) and
|
||||
b2 = mid.getABBSuccessor() and
|
||||
@@ -774,13 +798,13 @@ private cached module SsaImpl {
|
||||
exists(int rankix |
|
||||
b1 = b2 and
|
||||
defUseRank(v, b1, rankix, i1) and
|
||||
defUseRank(v, b2, rankix+1, i2)
|
||||
) or
|
||||
defUseRank(v, b2, rankix + 1, i2)
|
||||
)
|
||||
or
|
||||
defUseRank(v, b1, lastRank(v, b1), i1) and
|
||||
varBlockStep(v, b1, b2) and
|
||||
defUseRank(v, b2, 1, i2)
|
||||
}
|
||||
|
||||
}
|
||||
private import AdjacentUsesImpl
|
||||
|
||||
@@ -794,10 +818,11 @@ private cached module SsaImpl {
|
||||
adjacentVarRefs(v, b1, i1, b2, i2) and
|
||||
def.definesAt(v, b1, i1) and
|
||||
variableUse(v, use, b2, i2)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(TrackedVar v, TrackedSsaDef redef, BasicBlock b1, int i1, BasicBlock b2, int i2 |
|
||||
redef instanceof SsaUncertainImplicitUpdate or redef instanceof SsaPhiNode
|
||||
|
|
||||
|
|
||||
adjacentVarRefs(v, b1, i1, b2, i2) and
|
||||
def.definesAt(v, b1, i1) and
|
||||
redef.definesAt(v, b2, i2) and
|
||||
@@ -805,8 +830,8 @@ private cached module SsaImpl {
|
||||
)
|
||||
}
|
||||
|
||||
cached module SsaPublic {
|
||||
|
||||
cached
|
||||
module SsaPublic {
|
||||
/**
|
||||
* Holds if `use1` and `use2` form an adjacent use-use-pair of the same SSA
|
||||
* variable, that is, the value read in `use1` can reach `use2` without passing
|
||||
@@ -829,7 +854,8 @@ private cached module SsaImpl {
|
||||
*/
|
||||
cached
|
||||
predicate adjacentUseUse(RValue use1, RValue use2) {
|
||||
adjacentUseUseSameVar(use1, use2) or
|
||||
adjacentUseUseSameVar(use1, use2)
|
||||
or
|
||||
exists(TrackedVar v, TrackedSsaDef def, BasicBlock b1, int i1, BasicBlock b2, int i2 |
|
||||
adjacentVarRefs(v, b1, i1, b2, i2) and
|
||||
variableUse(v, use1, b1, i1) and
|
||||
@@ -838,9 +864,7 @@ private cached module SsaImpl {
|
||||
(def instanceof SsaUncertainImplicitUpdate or def instanceof SsaPhiNode)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
private import SsaImpl
|
||||
private import SsaDefReaches
|
||||
@@ -849,8 +873,12 @@ import SsaPublic
|
||||
cached
|
||||
private newtype TSsaVariable =
|
||||
TSsaPhiNode(TrackedVar v, BasicBlock b) { phiNode(v, b) } or
|
||||
TSsaCertainUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) { certainVariableUpdate(v, n, b, i) } or
|
||||
TSsaUncertainUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) { uncertainVariableUpdate(v, n, b, i) } or
|
||||
TSsaCertainUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
certainVariableUpdate(v, n, b, i)
|
||||
} or
|
||||
TSsaUncertainUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
uncertainVariableUpdate(v, n, b, i)
|
||||
} or
|
||||
TSsaEntryDef(TrackedVar v, BasicBlock b) { hasEntryDef(v, b) } or
|
||||
TSsaUntracked(SsaSourceField nf, ControlFlowNode n) {
|
||||
n = nf.getAnAccess().(FieldRead) and not trackField(nf)
|
||||
@@ -860,18 +888,19 @@ private newtype TSsaVariable =
|
||||
* An SSA definition excluding those variables that use a trivial SSA construction.
|
||||
*/
|
||||
private class TrackedSsaDef extends SsaVariable {
|
||||
TrackedSsaDef() {
|
||||
not this = TSsaUntracked(_, _)
|
||||
}
|
||||
TrackedSsaDef() { not this = TSsaUntracked(_, _) }
|
||||
|
||||
/**
|
||||
* Holds if this SSA definition occurs at the specified position.
|
||||
* Phi nodes are placed at index -1.
|
||||
*/
|
||||
predicate definesAt(TrackedVar v, BasicBlock b, int i) {
|
||||
this = TSsaPhiNode(v, b) and i = -1 or
|
||||
this = TSsaCertainUpdate(v, _, b, i) or
|
||||
this = TSsaUncertainUpdate(v, _, b, i) or
|
||||
this = TSsaPhiNode(v, b) and i = -1
|
||||
or
|
||||
this = TSsaCertainUpdate(v, _, b, i)
|
||||
or
|
||||
this = TSsaUncertainUpdate(v, _, b, i)
|
||||
or
|
||||
this = TSsaEntryDef(v, b) and i = 0
|
||||
}
|
||||
}
|
||||
@@ -926,9 +955,7 @@ class SsaVariable extends TSsaVariable {
|
||||
}
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) {
|
||||
ssaDefReachesEndOfBlock(_, this, b)
|
||||
}
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(_, this, b) }
|
||||
|
||||
/**
|
||||
* Gets an SSA variable whose value can flow to this one in one step. This
|
||||
@@ -959,14 +986,10 @@ class SsaUpdate extends SsaVariable {
|
||||
/** An SSA variable that is defined by a `VariableUpdate`. */
|
||||
class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate {
|
||||
SsaExplicitUpdate() {
|
||||
exists(VariableUpdate upd |
|
||||
upd = this.getCFGNode() and getDestVar(upd) = getSourceVariable()
|
||||
)
|
||||
exists(VariableUpdate upd | upd = this.getCFGNode() and getDestVar(upd) = getSourceVariable())
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "SSA def(" + getSourceVariable() + ")"
|
||||
}
|
||||
override string toString() { result = "SSA def(" + getSourceVariable() + ")" }
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
VariableUpdate getDefiningExpr() {
|
||||
@@ -981,24 +1004,26 @@ class SsaExplicitUpdate extends SsaUpdate, TSsaCertainUpdate {
|
||||
* occurs just prior to a `FieldRead` of an untracked field.
|
||||
*/
|
||||
class SsaImplicitUpdate extends SsaUpdate {
|
||||
SsaImplicitUpdate() {
|
||||
not this instanceof SsaExplicitUpdate
|
||||
}
|
||||
SsaImplicitUpdate() { not this instanceof SsaExplicitUpdate }
|
||||
|
||||
override string toString() {
|
||||
result = "SSA impl upd[" + getKind() + "](" + getSourceVariable() + ")"
|
||||
}
|
||||
|
||||
private string getKind() {
|
||||
this = TSsaUntracked(_, _) and result = "untracked" or
|
||||
certainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _) and result = "explicit qualifier" or
|
||||
if uncertainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _) then
|
||||
if exists(getANonLocalUpdate()) then
|
||||
result = "nonlocal + nonlocal qualifier"
|
||||
else
|
||||
result = "nonlocal qualifier"
|
||||
else
|
||||
(exists(getANonLocalUpdate()) and result = "nonlocal")
|
||||
this = TSsaUntracked(_, _) and result = "untracked"
|
||||
or
|
||||
certainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _) and
|
||||
result = "explicit qualifier"
|
||||
or
|
||||
if uncertainVariableUpdate(getSourceVariable().getQualifier(), getCFGNode(), _, _)
|
||||
then
|
||||
if exists(getANonLocalUpdate())
|
||||
then result = "nonlocal + nonlocal qualifier"
|
||||
else result = "nonlocal qualifier"
|
||||
else (
|
||||
exists(getANonLocalUpdate()) and result = "nonlocal"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1036,9 +1061,7 @@ class SsaUncertainImplicitUpdate extends SsaImplicitUpdate, TSsaUncertainUpdate
|
||||
* Gets the immediately preceding definition. Since this update is uncertain
|
||||
* the value from the preceding definition might still be valid.
|
||||
*/
|
||||
SsaVariable getPriorDef() {
|
||||
ssaDefReachesUncertainDef(_, result, this)
|
||||
}
|
||||
SsaVariable getPriorDef() { ssaDefReachesUncertainDef(_, result, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1046,9 +1069,7 @@ class SsaUncertainImplicitUpdate extends SsaImplicitUpdate, TSsaUncertainUpdate
|
||||
* includes initial values of parameters, fields, and closure variables.
|
||||
*/
|
||||
class SsaImplicitInit extends SsaVariable, TSsaEntryDef {
|
||||
override string toString() {
|
||||
result = "SSA init(" + getSourceVariable() + ")"
|
||||
}
|
||||
override string toString() { result = "SSA init(" + getSourceVariable() + ")" }
|
||||
|
||||
/** Holds if this is a closure variable that captures the value of `capturedvar`. */
|
||||
predicate captures(SsaVariable capturedvar) {
|
||||
@@ -1065,9 +1086,7 @@ class SsaImplicitInit extends SsaVariable, TSsaEntryDef {
|
||||
|
||||
/** An SSA phi node. */
|
||||
class SsaPhiNode extends SsaVariable, TSsaPhiNode {
|
||||
override string toString() {
|
||||
result = "SSA phi(" + getSourceVariable() + ")"
|
||||
}
|
||||
override string toString() { result = "SSA phi(" + getSourceVariable() + ")" }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
SsaVariable getAPhiInput() {
|
||||
@@ -1096,9 +1115,13 @@ library class RefTypeCastExpr extends CastExpr {
|
||||
* The `VarAccess` represents the access to `v` that `result` has the same value as.
|
||||
*/
|
||||
Expr sameValue(SsaVariable v, VarAccess va) {
|
||||
result = v.getAUse() and result = va or
|
||||
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitUpdate).getDefiningExpr() or
|
||||
result.(AssignExpr).getSource() = sameValue(v, va) or
|
||||
result.(ParExpr).getExpr() = sameValue(v, va) or
|
||||
result = v.getAUse() and result = va
|
||||
or
|
||||
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitUpdate).getDefiningExpr()
|
||||
or
|
||||
result.(AssignExpr).getSource() = sameValue(v, va)
|
||||
or
|
||||
result.(ParExpr).getExpr() = sameValue(v, va)
|
||||
or
|
||||
result.(RefTypeCastExpr).getExpr() = sameValue(v, va)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* The analysis is implemented as an abstract interpretation over the
|
||||
* three-valued domain `{negative, zero, positive}`.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import SSA
|
||||
private import RangeUtils
|
||||
@@ -13,104 +14,173 @@ private import semmle.code.java.Reflection
|
||||
private import semmle.code.java.Collections
|
||||
private import semmle.code.java.Maps
|
||||
|
||||
private newtype TSign = TNeg() or TZero() or TPos()
|
||||
private newtype TSign =
|
||||
TNeg() or
|
||||
TZero() or
|
||||
TPos()
|
||||
|
||||
private class Sign extends TSign {
|
||||
string toString() {
|
||||
result = "-" and this = TNeg() or
|
||||
result = "0" and this = TZero() or
|
||||
result = "-" and this = TNeg()
|
||||
or
|
||||
result = "0" and this = TZero()
|
||||
or
|
||||
result = "+" and this = TPos()
|
||||
}
|
||||
|
||||
Sign inc() {
|
||||
this = TNeg() and result = TNeg() or
|
||||
this = TNeg() and result = TZero() or
|
||||
this = TZero() and result = TPos() or
|
||||
this = TNeg() and result = TNeg()
|
||||
or
|
||||
this = TNeg() and result = TZero()
|
||||
or
|
||||
this = TZero() and result = TPos()
|
||||
or
|
||||
this = TPos() and result = TPos()
|
||||
}
|
||||
Sign dec() {
|
||||
result.inc() = this
|
||||
}
|
||||
|
||||
Sign dec() { result.inc() = this }
|
||||
|
||||
Sign neg() {
|
||||
this = TNeg() and result = TPos() or
|
||||
this = TZero() and result = TZero() or
|
||||
this = TNeg() and result = TPos()
|
||||
or
|
||||
this = TZero() and result = TZero()
|
||||
or
|
||||
this = TPos() and result = TNeg()
|
||||
}
|
||||
|
||||
Sign bitnot() {
|
||||
this = TNeg() and result = TPos() or
|
||||
this = TNeg() and result = TZero() or
|
||||
this = TZero() and result = TNeg() or
|
||||
this = TNeg() and result = TPos()
|
||||
or
|
||||
this = TNeg() and result = TZero()
|
||||
or
|
||||
this = TZero() and result = TNeg()
|
||||
or
|
||||
this = TPos() and result = TNeg()
|
||||
}
|
||||
|
||||
Sign add(Sign s) {
|
||||
this = TZero() and result = s or
|
||||
s = TZero() and result = this or
|
||||
this = s and this = result or
|
||||
this = TPos() and s = TNeg() or
|
||||
this = TZero() and result = s
|
||||
or
|
||||
s = TZero() and result = this
|
||||
or
|
||||
this = s and this = result
|
||||
or
|
||||
this = TPos() and s = TNeg()
|
||||
or
|
||||
this = TNeg() and s = TPos()
|
||||
}
|
||||
|
||||
Sign mul(Sign s) {
|
||||
result = TZero() and this = TZero() or
|
||||
result = TZero() and s = TZero() or
|
||||
result = TNeg() and this = TPos() and s = TNeg() or
|
||||
result = TNeg() and this = TNeg() and s = TPos() or
|
||||
result = TPos() and this = TPos() and s = TPos() or
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = TZero() and s = TZero()
|
||||
or
|
||||
result = TNeg() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TNeg()
|
||||
}
|
||||
|
||||
Sign div(Sign s) {
|
||||
result = TZero() and s = TNeg() or
|
||||
result = TZero() and s = TPos() or
|
||||
result = TNeg() and this = TPos() and s = TNeg() or
|
||||
result = TNeg() and this = TNeg() and s = TPos() or
|
||||
result = TPos() and this = TPos() and s = TPos() or
|
||||
result = TZero() and s = TNeg()
|
||||
or
|
||||
result = TZero() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TNeg()
|
||||
}
|
||||
|
||||
Sign rem(Sign s) {
|
||||
result = TZero() and s = TNeg() or
|
||||
result = TZero() and s = TPos() or
|
||||
result = this and s = TNeg() or
|
||||
result = TZero() and s = TNeg()
|
||||
or
|
||||
result = TZero() and s = TPos()
|
||||
or
|
||||
result = this and s = TNeg()
|
||||
or
|
||||
result = this and s = TPos()
|
||||
}
|
||||
|
||||
Sign bitand(Sign s) {
|
||||
result = TZero() and this = TZero() or
|
||||
result = TZero() and s = TZero() or
|
||||
result = TZero() and this = TPos() or
|
||||
result = TZero() and s = TPos() or
|
||||
result = TNeg() and this = TNeg() and s = TNeg() or
|
||||
result = TPos() and this = TNeg() and s = TPos() or
|
||||
result = TPos() and this = TPos() and s = TNeg() or
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = TZero() and s = TZero()
|
||||
or
|
||||
result = TZero() and this = TPos()
|
||||
or
|
||||
result = TZero() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
}
|
||||
|
||||
Sign bitor(Sign s) {
|
||||
result = TZero() and this = TZero() and s = TZero() or
|
||||
result = TNeg() and this = TNeg() or
|
||||
result = TNeg() and s = TNeg() or
|
||||
result = TPos() and this = TPos() and s = TZero() or
|
||||
result = TPos() and this = TZero() and s = TPos() or
|
||||
result = TZero() and this = TZero() and s = TZero()
|
||||
or
|
||||
result = TNeg() and this = TNeg()
|
||||
or
|
||||
result = TNeg() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TZero()
|
||||
or
|
||||
result = TPos() and this = TZero() and s = TPos()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
}
|
||||
|
||||
Sign bitxor(Sign s) {
|
||||
result = TZero() and this = s or
|
||||
result = this and s = TZero() or
|
||||
result = s and this = TZero() or
|
||||
result = TPos() and this = TPos() and s = TPos() or
|
||||
result = TNeg() and this = TNeg() and s = TPos() or
|
||||
result = TNeg() and this = TPos() and s = TNeg() or
|
||||
result = TZero() and this = s
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
result = s and this = TZero()
|
||||
or
|
||||
result = TPos() and this = TPos() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TNeg() and s = TPos()
|
||||
or
|
||||
result = TNeg() and this = TPos() and s = TNeg()
|
||||
or
|
||||
result = TPos() and this = TNeg() and s = TNeg()
|
||||
}
|
||||
|
||||
Sign lshift(Sign s) {
|
||||
result = TZero() and this = TZero() or
|
||||
result = this and s = TZero() or
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
this != TZero() and s != TZero()
|
||||
}
|
||||
|
||||
Sign rshift(Sign s) {
|
||||
result = TZero() and this = TZero() or
|
||||
result = this and s = TZero() or
|
||||
result = TNeg() and this = TNeg() or
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
result = TNeg() and this = TNeg()
|
||||
or
|
||||
result != TNeg() and this = TPos() and s != TZero()
|
||||
}
|
||||
|
||||
Sign urshift(Sign s) {
|
||||
result = TZero() and this = TZero() or
|
||||
result = this and s = TZero() or
|
||||
result != TZero() and this = TNeg() and s != TZero() or
|
||||
result = TZero() and this = TZero()
|
||||
or
|
||||
result = this and s = TZero()
|
||||
or
|
||||
result != TZero() and this = TNeg() and s != TZero()
|
||||
or
|
||||
result != TNeg() and this = TPos() and s != TZero()
|
||||
}
|
||||
}
|
||||
@@ -118,30 +188,39 @@ private class Sign extends TSign {
|
||||
/** Gets the sign of `e` if this can be directly determined. */
|
||||
private Sign certainExprSign(Expr e) {
|
||||
exists(int i | e.(ConstantIntegerExpr).getIntValue() = i |
|
||||
i < 0 and result = TNeg() or
|
||||
i = 0 and result = TZero() or
|
||||
i < 0 and result = TNeg()
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
) or
|
||||
)
|
||||
or
|
||||
not exists(e.(ConstantIntegerExpr).getIntValue()) and
|
||||
(
|
||||
exists(float f |
|
||||
f = e.(LongLiteral).getValue().toFloat() or
|
||||
f = e.(FloatingPointLiteral).getValue().toFloat() or
|
||||
f = e.(DoubleLiteral).getValue().toFloat()
|
||||
|
|
||||
f < 0 and result = TNeg() or
|
||||
f = 0 and result = TZero() or
|
||||
|
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(string charlit | charlit = e.(CharacterLiteral).getValue() |
|
||||
if charlit = "\\0" or charlit = "\\u0000" then
|
||||
result = TZero()
|
||||
else
|
||||
result = TPos()
|
||||
) or
|
||||
e.(MethodAccess).getMethod() instanceof StringLengthMethod and (result = TPos() or result = TZero()) or
|
||||
e.(MethodAccess).getMethod() instanceof CollectionSizeMethod and (result = TPos() or result = TZero()) or
|
||||
e.(MethodAccess).getMethod() instanceof MapSizeMethod and (result = TPos() or result = TZero())
|
||||
if charlit = "\\0" or charlit = "\\u0000" then result = TZero() else result = TPos()
|
||||
)
|
||||
or
|
||||
e.(MethodAccess).getMethod() instanceof StringLengthMethod and
|
||||
(result = TPos() or result = TZero())
|
||||
or
|
||||
e.(MethodAccess).getMethod() instanceof CollectionSizeMethod and
|
||||
(result = TPos() or result = TZero())
|
||||
or
|
||||
e.(MethodAccess).getMethod() instanceof MapSizeMethod and
|
||||
(result = TPos() or result = TZero())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -149,15 +228,20 @@ private Sign certainExprSign(Expr e) {
|
||||
private predicate unknownSign(Expr e) {
|
||||
not exists(e.(ConstantIntegerExpr).getIntValue()) and
|
||||
(
|
||||
exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt())) or
|
||||
exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat())) or
|
||||
exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt()))
|
||||
or
|
||||
exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat()))
|
||||
or
|
||||
exists(CastExpr cast, Type fromtyp |
|
||||
cast = e and
|
||||
fromtyp = cast.getExpr().getType() and
|
||||
not fromtyp instanceof NumericOrCharType
|
||||
) or
|
||||
e instanceof ArrayAccess and e.getType() instanceof NumericOrCharType or
|
||||
e instanceof MethodAccess and e.getType() instanceof NumericOrCharType or
|
||||
)
|
||||
or
|
||||
e instanceof ArrayAccess and e.getType() instanceof NumericOrCharType
|
||||
or
|
||||
e instanceof MethodAccess and e.getType() instanceof NumericOrCharType
|
||||
or
|
||||
e instanceof ClassInstanceExpr and e.getType() instanceof NumericOrCharType
|
||||
)
|
||||
}
|
||||
@@ -171,7 +255,7 @@ private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos
|
||||
pos.hasReadOfVar(v) and
|
||||
guardControlsSsaRead(comp, pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = ssaRead(v, 0) and
|
||||
@@ -193,7 +277,7 @@ private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos
|
||||
pos.hasReadOfVar(v) and
|
||||
guardControlsSsaRead(comp, pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = ssaRead(v, 0) and
|
||||
@@ -263,11 +347,16 @@ private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound) or
|
||||
lowerBound(bound, v, pos, false) and TZero() = exprSign(bound) or
|
||||
upperBound(bound, v, pos, _) and TPos() = exprSign(bound) or
|
||||
upperBound(bound, v, pos, false) and TZero() = exprSign(bound) or
|
||||
eqBound(bound, v, pos, true) and TZero() = exprSign(bound) or
|
||||
lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound)
|
||||
or
|
||||
lowerBound(bound, v, pos, false) and TZero() = exprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, _) and TPos() = exprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, false) and TZero() = exprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, true) and TZero() = exprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, false) and TZero() != exprSign(bound)
|
||||
}
|
||||
|
||||
@@ -276,8 +365,10 @@ private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos) or
|
||||
s = TNeg() and negBound(_, v, pos) or
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
or
|
||||
s = TZero() and zeroBound(_, v, pos)
|
||||
}
|
||||
|
||||
@@ -296,9 +387,14 @@ private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) {
|
||||
}
|
||||
|
||||
private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) {
|
||||
result = TPos() and forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos)) or
|
||||
result = TNeg() and forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos)) or
|
||||
result = TZero() and forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
result = TPos() and
|
||||
forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TZero() and
|
||||
forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
@@ -313,17 +409,27 @@ private Sign ssaSign(SsaVariable v, SsaReadPosition pos) {
|
||||
pragma[nomagic]
|
||||
private Sign ssaDefSign(SsaVariable v) {
|
||||
exists(VariableUpdate def | def = v.(SsaExplicitUpdate).getDefiningExpr() |
|
||||
result = exprSign(def.(VariableAssign).getSource()) or
|
||||
exists(EnhancedForStmt for | def = for.getVariable()) or
|
||||
result = exprSign(def.(PostIncExpr).getExpr()).inc() or
|
||||
result = exprSign(def.(PreIncExpr).getExpr()).inc() or
|
||||
result = exprSign(def.(PostDecExpr).getExpr()).dec() or
|
||||
result = exprSign(def.(PreDecExpr).getExpr()).dec() or
|
||||
result = exprSign(def.(VariableAssign).getSource())
|
||||
or
|
||||
exists(EnhancedForStmt for | def = for.getVariable())
|
||||
or
|
||||
result = exprSign(def.(PostIncExpr).getExpr()).inc()
|
||||
or
|
||||
result = exprSign(def.(PreIncExpr).getExpr()).inc()
|
||||
or
|
||||
result = exprSign(def.(PostDecExpr).getExpr()).dec()
|
||||
or
|
||||
result = exprSign(def.(PreDecExpr).getExpr()).dec()
|
||||
or
|
||||
exists(AssignOp a | a = def and result = exprSign(a))
|
||||
) or
|
||||
result = fieldSign(v.(SsaImplicitUpdate).getSourceVariable().getVariable()) or
|
||||
result = fieldSign(v.(SsaImplicitInit).getSourceVariable().getVariable()) or
|
||||
exists(Parameter p | v.(SsaImplicitInit).isParameterDefinition(p)) or
|
||||
)
|
||||
or
|
||||
result = fieldSign(v.(SsaImplicitUpdate).getSourceVariable().getVariable())
|
||||
or
|
||||
result = fieldSign(v.(SsaImplicitInit).getSourceVariable().getVariable())
|
||||
or
|
||||
exists(Parameter p | v.(SsaImplicitInit).isParameterDefinition(p))
|
||||
or
|
||||
exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge |
|
||||
v = phi and
|
||||
edge.phiInput(phi, inp) and
|
||||
@@ -333,86 +439,136 @@ private Sign ssaDefSign(SsaVariable v) {
|
||||
|
||||
/** Gets a possible sign for `f`. */
|
||||
private Sign fieldSign(Field f) {
|
||||
result = exprSign(f.getAnAssignedValue()) or
|
||||
exists(PostIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc()) or
|
||||
exists(PreIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc()) or
|
||||
exists(PostDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec()) or
|
||||
exists(PreDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec()) or
|
||||
exists(AssignOp a | a.getDest() = f.getAnAccess() | result = exprSign(a)) or
|
||||
result = exprSign(f.getAnAssignedValue())
|
||||
or
|
||||
exists(PostIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
|
||||
or
|
||||
exists(PreIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
|
||||
or
|
||||
exists(PostDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
|
||||
or
|
||||
exists(PreDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
|
||||
or
|
||||
exists(AssignOp a | a.getDest() = f.getAnAccess() | result = exprSign(a))
|
||||
or
|
||||
exists(ReflectiveFieldAccess rfa | rfa.inferAccessedField() = f)
|
||||
or
|
||||
if f.fromSource() then
|
||||
not exists(f.getInitializer()) and result = TZero()
|
||||
else if f instanceof ArrayLengthField then result != TNeg()
|
||||
else if f.hasName("MAX_VALUE") then result = TPos()
|
||||
else if f.hasName("MIN_VALUE") then result = TNeg()
|
||||
else any()
|
||||
if f.fromSource()
|
||||
then not exists(f.getInitializer()) and result = TZero()
|
||||
else
|
||||
if f instanceof ArrayLengthField
|
||||
then result != TNeg()
|
||||
else
|
||||
if f.hasName("MAX_VALUE")
|
||||
then result = TPos()
|
||||
else if f.hasName("MIN_VALUE") then result = TNeg() else any()
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `e`. */
|
||||
cached
|
||||
private Sign exprSign(Expr e) {
|
||||
result = certainExprSign(e) or
|
||||
result = certainExprSign(e)
|
||||
or
|
||||
not exists(certainExprSign(e)) and
|
||||
(
|
||||
unknownSign(e) or
|
||||
result = exprSign(e.(ParExpr).getExpr()) or
|
||||
unknownSign(e)
|
||||
or
|
||||
result = exprSign(e.(ParExpr).getExpr())
|
||||
or
|
||||
exists(SsaVariable v | v.getAUse() = e |
|
||||
result = ssaSign(v, any(SsaReadPositionBlock bb | bb.getBlock() = e.getBasicBlock())) or
|
||||
result = ssaSign(v, any(SsaReadPositionBlock bb | bb.getBlock() = e.getBasicBlock()))
|
||||
or
|
||||
not exists(e.getBasicBlock()) and result = ssaDefSign(v)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(FieldAccess fa | fa = e |
|
||||
not exists(SsaVariable v | v.getAUse() = fa) and
|
||||
result = fieldSign(fa.getField())
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(VarAccess va | va = e |
|
||||
not exists(SsaVariable v | v.getAUse() = va) and
|
||||
not va instanceof FieldAccess
|
||||
) or
|
||||
result = exprSign(e.(AssignExpr).getSource()) or
|
||||
result = exprSign(e.(PlusExpr).getExpr()) or
|
||||
result = exprSign(e.(PostIncExpr).getExpr()) or
|
||||
result = exprSign(e.(PostDecExpr).getExpr()) or
|
||||
result = exprSign(e.(PreIncExpr).getExpr()).inc() or
|
||||
result = exprSign(e.(PreDecExpr).getExpr()).dec() or
|
||||
result = exprSign(e.(MinusExpr).getExpr()).neg() or
|
||||
result = exprSign(e.(BitNotExpr).getExpr()).bitnot() or
|
||||
)
|
||||
or
|
||||
result = exprSign(e.(AssignExpr).getSource())
|
||||
or
|
||||
result = exprSign(e.(PlusExpr).getExpr())
|
||||
or
|
||||
result = exprSign(e.(PostIncExpr).getExpr())
|
||||
or
|
||||
result = exprSign(e.(PostDecExpr).getExpr())
|
||||
or
|
||||
result = exprSign(e.(PreIncExpr).getExpr()).inc()
|
||||
or
|
||||
result = exprSign(e.(PreDecExpr).getExpr()).dec()
|
||||
or
|
||||
result = exprSign(e.(MinusExpr).getExpr()).neg()
|
||||
or
|
||||
result = exprSign(e.(BitNotExpr).getExpr()).bitnot()
|
||||
or
|
||||
exists(DivExpr div |
|
||||
div = e and
|
||||
result = exprSign(div.getLeftOperand()) and
|
||||
result != TZero()
|
||||
|
|
||||
|
|
||||
div.getRightOperand().(FloatingPointLiteral).getValue().toFloat() = 0 or
|
||||
div.getRightOperand().(DoubleLiteral).getValue().toFloat() = 0
|
||||
) or
|
||||
exists(Sign s1, Sign s2 |
|
||||
binaryOpSigns(e, s1, s2)
|
||||
|
|
||||
(e instanceof AssignAddExpr or e instanceof AddExpr) and result = s1.add(s2) or
|
||||
(e instanceof AssignSubExpr or e instanceof SubExpr) and result = s1.add(s2.neg()) or
|
||||
(e instanceof AssignMulExpr or e instanceof MulExpr) and result = s1.mul(s2) or
|
||||
(e instanceof AssignDivExpr or e instanceof DivExpr) and result = s1.div(s2) or
|
||||
(e instanceof AssignRemExpr or e instanceof RemExpr) and result = s1.rem(s2) or
|
||||
(e instanceof AssignAndExpr or e instanceof AndBitwiseExpr) and result = s1.bitand(s2) or
|
||||
(e instanceof AssignOrExpr or e instanceof OrBitwiseExpr) and result = s1.bitor(s2) or
|
||||
(e instanceof AssignXorExpr or e instanceof XorBitwiseExpr) and result = s1.bitxor(s2) or
|
||||
(e instanceof AssignLShiftExpr or e instanceof LShiftExpr) and result = s1.lshift(s2) or
|
||||
(e instanceof AssignRShiftExpr or e instanceof RShiftExpr) and result = s1.rshift(s2) or
|
||||
(e instanceof AssignURShiftExpr or e instanceof URShiftExpr) and result = s1.urshift(s2)
|
||||
) or
|
||||
result = exprSign(e.(ConditionalExpr).getTrueExpr()) or
|
||||
result = exprSign(e.(ConditionalExpr).getFalseExpr()) or
|
||||
)
|
||||
or
|
||||
exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) |
|
||||
(e instanceof AssignAddExpr or e instanceof AddExpr) and
|
||||
result = s1.add(s2)
|
||||
or
|
||||
(e instanceof AssignSubExpr or e instanceof SubExpr) and
|
||||
result = s1.add(s2.neg())
|
||||
or
|
||||
(e instanceof AssignMulExpr or e instanceof MulExpr) and
|
||||
result = s1.mul(s2)
|
||||
or
|
||||
(e instanceof AssignDivExpr or e instanceof DivExpr) and
|
||||
result = s1.div(s2)
|
||||
or
|
||||
(e instanceof AssignRemExpr or e instanceof RemExpr) and
|
||||
result = s1.rem(s2)
|
||||
or
|
||||
(e instanceof AssignAndExpr or e instanceof AndBitwiseExpr) and
|
||||
result = s1.bitand(s2)
|
||||
or
|
||||
(e instanceof AssignOrExpr or e instanceof OrBitwiseExpr) and
|
||||
result = s1.bitor(s2)
|
||||
or
|
||||
(e instanceof AssignXorExpr or e instanceof XorBitwiseExpr) and
|
||||
result = s1.bitxor(s2)
|
||||
or
|
||||
(e instanceof AssignLShiftExpr or e instanceof LShiftExpr) and
|
||||
result = s1.lshift(s2)
|
||||
or
|
||||
(e instanceof AssignRShiftExpr or e instanceof RShiftExpr) and
|
||||
result = s1.rshift(s2)
|
||||
or
|
||||
(e instanceof AssignURShiftExpr or e instanceof URShiftExpr) and
|
||||
result = s1.urshift(s2)
|
||||
)
|
||||
or
|
||||
result = exprSign(e.(ConditionalExpr).getTrueExpr())
|
||||
or
|
||||
result = exprSign(e.(ConditionalExpr).getFalseExpr())
|
||||
or
|
||||
result = exprSign(e.(CastExpr).getExpr())
|
||||
)
|
||||
}
|
||||
|
||||
private Sign binaryOpLhsSign(Expr e) {
|
||||
result = exprSign(e.(BinaryExpr).getLeftOperand()) or
|
||||
result = exprSign(e.(AssignOp).getDest())
|
||||
}
|
||||
|
||||
private Sign binaryOpRhsSign(Expr e) {
|
||||
result = exprSign(e.(BinaryExpr).getRightOperand()) or
|
||||
result = exprSign(e.(AssignOp).getRhs())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) {
|
||||
lhs = binaryOpLhsSign(e) and
|
||||
@@ -444,4 +600,3 @@ predicate strictlyNegative(Expr e) {
|
||||
not exprSign(e) = TPos() and
|
||||
not exprSign(e) = TZero()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.Collections
|
||||
|
||||
private import SSA
|
||||
private import DefUse
|
||||
private import semmle.code.java.security.SecurityTests
|
||||
@@ -15,7 +14,6 @@ private import semmle.code.java.security.Validation
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
|
||||
module TaintTracking {
|
||||
|
||||
/**
|
||||
* A taint tracking configuration.
|
||||
*
|
||||
@@ -54,8 +52,7 @@ module TaintTracking {
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
@@ -65,8 +62,7 @@ module TaintTracking {
|
||||
/** Holds if the edge from `node1` to `node2` is a taint sanitizer. */
|
||||
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final
|
||||
override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
@@ -76,8 +72,7 @@ module TaintTracking {
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
localAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
@@ -129,8 +124,7 @@ module TaintTracking {
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
// Ignore paths through test code.
|
||||
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
|
||||
@@ -140,8 +134,7 @@ module TaintTracking {
|
||||
/** Holds if the edge from `node1` to `node2` is a taint sanitizer. */
|
||||
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final
|
||||
override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isSanitizerEdge(node1, node2)
|
||||
}
|
||||
|
||||
@@ -151,8 +144,7 @@ module TaintTracking {
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
localAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
@@ -170,9 +162,7 @@ module TaintTracking {
|
||||
* Holds if taint can flow from `src` to `sink` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localTaintStep*(src, sink)
|
||||
}
|
||||
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `src` to `sink`.
|
||||
@@ -188,8 +178,13 @@ module TaintTracking {
|
||||
* different objects.
|
||||
*/
|
||||
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr()) or
|
||||
exists(Argument arg | src.asExpr() = arg and arg.isVararg() and sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall())
|
||||
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
|
||||
or
|
||||
exists(Argument arg |
|
||||
src.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,30 +193,47 @@ module TaintTracking {
|
||||
* different objects.
|
||||
*/
|
||||
private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
|
||||
sink.(AddExpr).getAnOperand() = src and sink.getType() instanceof TypeString or
|
||||
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString or
|
||||
sink.(ArrayCreationExpr).getInit() = src or
|
||||
sink.(ArrayInit).getAnInit() = src or
|
||||
sink.(ArrayAccess).getArray() = src or
|
||||
sink.(LogicExpr).getAnOperand() = src or
|
||||
sink.(AddExpr).getAnOperand() = src and sink.getType() instanceof TypeString
|
||||
or
|
||||
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString
|
||||
or
|
||||
sink.(ArrayCreationExpr).getInit() = src
|
||||
or
|
||||
sink.(ArrayInit).getAnInit() = src
|
||||
or
|
||||
sink.(ArrayAccess).getArray() = src
|
||||
or
|
||||
sink.(LogicExpr).getAnOperand() = src
|
||||
or
|
||||
exists(Assignment assign | assign.getSource() = src |
|
||||
sink = assign.getDest().(ArrayAccess).getArray()
|
||||
) or
|
||||
constructorStep(src, sink) or
|
||||
qualifierToMethodStep(src, sink) or
|
||||
qualifierToArgumentStep(src, sink) or
|
||||
argToMethodStep(src, sink) or
|
||||
argToArgStep(src, sink) or
|
||||
argToQualifierStep(src, sink) or
|
||||
comparisonStep(src, sink) or
|
||||
stringBuilderStep(src, sink) or
|
||||
serializationStep(src, sink) or
|
||||
)
|
||||
or
|
||||
constructorStep(src, sink)
|
||||
or
|
||||
qualifierToMethodStep(src, sink)
|
||||
or
|
||||
qualifierToArgumentStep(src, sink)
|
||||
or
|
||||
argToMethodStep(src, sink)
|
||||
or
|
||||
argToArgStep(src, sink)
|
||||
or
|
||||
argToQualifierStep(src, sink)
|
||||
or
|
||||
comparisonStep(src, sink)
|
||||
or
|
||||
stringBuilderStep(src, sink)
|
||||
or
|
||||
serializationStep(src, sink)
|
||||
or
|
||||
qualifierToArgStep(src, sink)
|
||||
}
|
||||
|
||||
private class BulkData extends RefType {
|
||||
BulkData() {
|
||||
this.(Array).getElementType().(PrimitiveType).getName().regexpMatch("byte|char") or
|
||||
this.(Array).getElementType().(PrimitiveType).getName().regexpMatch("byte|char")
|
||||
or
|
||||
exists(RefType t | this.getASourceSupertype*() = t |
|
||||
t.hasQualifiedName("java.io", "InputStream") or
|
||||
t.hasQualifiedName("java.nio", "ByteBuffer") or
|
||||
@@ -250,48 +262,74 @@ module TaintTracking {
|
||||
exists(int argi | sink.getArgument(argi) = tracked |
|
||||
exists(string s | sink.getConstructedType().getQualifiedName() = s |
|
||||
// String constructor does nothing to data
|
||||
s = "java.lang.String" and argi = 0 or
|
||||
s = "java.lang.String" and argi = 0
|
||||
or
|
||||
// some readers preserve the content of streams
|
||||
s = "java.io.InputStreamReader" and argi = 0 or
|
||||
s = "java.io.BufferedReader" and argi = 0 or
|
||||
s = "java.io.CharArrayReader" and argi = 0 or
|
||||
s = "java.io.StringReader" and argi = 0 or
|
||||
s = "java.io.InputStreamReader" and argi = 0
|
||||
or
|
||||
s = "java.io.BufferedReader" and argi = 0
|
||||
or
|
||||
s = "java.io.CharArrayReader" and argi = 0
|
||||
or
|
||||
s = "java.io.StringReader" and argi = 0
|
||||
or
|
||||
// data preserved through streams
|
||||
s = "java.io.ObjectInputStream" and argi = 0 or
|
||||
s = "java.io.ByteArrayInputStream" and argi = 0 or
|
||||
s = "java.io.DataInputStream" and argi = 0 or
|
||||
s = "java.io.BufferedInputStream" and argi = 0 or
|
||||
s = "com.esotericsoftware.kryo.io.Input" and argi = 0 or
|
||||
s = "java.beans.XMLDecoder" and argi = 0 or
|
||||
s = "java.io.ObjectInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.ByteArrayInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.DataInputStream" and argi = 0
|
||||
or
|
||||
s = "java.io.BufferedInputStream" and argi = 0
|
||||
or
|
||||
s = "com.esotericsoftware.kryo.io.Input" and argi = 0
|
||||
or
|
||||
s = "java.beans.XMLDecoder" and argi = 0
|
||||
or
|
||||
// a tokenizer preserves the content of a string
|
||||
s = "java.util.StringTokenizer" and argi = 0 or
|
||||
s = "java.util.StringTokenizer" and argi = 0
|
||||
or
|
||||
// unzipping the stream preserves content
|
||||
s = "java.util.zip.ZipInputStream" and argi = 0 or
|
||||
s = "java.util.zip.GZIPInputStream" and argi = 0 or
|
||||
s = "java.util.zip.ZipInputStream" and argi = 0
|
||||
or
|
||||
s = "java.util.zip.GZIPInputStream" and argi = 0
|
||||
or
|
||||
// string builders and buffers
|
||||
s = "java.lang.StringBuilder" and argi = 0 or
|
||||
s = "java.lang.StringBuffer" and argi = 0 or
|
||||
s = "java.lang.StringBuilder" and argi = 0
|
||||
or
|
||||
s = "java.lang.StringBuffer" and argi = 0
|
||||
or
|
||||
// a cookie with tainted ingredients is tainted
|
||||
s = "javax.servlet.http.Cookie" and argi = 0 or
|
||||
s = "javax.servlet.http.Cookie" and argi = 1 or
|
||||
s = "javax.servlet.http.Cookie" and argi = 0
|
||||
or
|
||||
s = "javax.servlet.http.Cookie" and argi = 1
|
||||
or
|
||||
// various xml stream source constructors.
|
||||
s = "org.xml.sax.InputSource" and argi = 0 or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 0 and sink.getNumArgument() = 1 or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 1 and sink.getNumArgument() = 2 or
|
||||
s = "javax.xml.transform.stream.StreamSource" and argi = 0 or
|
||||
s = "org.xml.sax.InputSource" and argi = 0
|
||||
or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 0 and sink.getNumArgument() = 1
|
||||
or
|
||||
s = "javax.xml.transform.sax.SAXSource" and argi = 1 and sink.getNumArgument() = 2
|
||||
or
|
||||
s = "javax.xml.transform.stream.StreamSource" and argi = 0
|
||||
or
|
||||
//a URI constructed from a tainted string is tainted.
|
||||
s = "java.net.URI" and argi = 0 and sink.getNumArgument() = 1
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(RefType t | t.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(t, sink.getConstructedType())
|
||||
) and argi = 0 or
|
||||
) and
|
||||
argi = 0
|
||||
or
|
||||
// wrappers constructed by extension
|
||||
exists(Constructor c, Parameter p, SuperConstructorInvocationStmt sup |
|
||||
c = sink.getConstructor() and
|
||||
p = c.getParameter(argi) and
|
||||
sup.getEnclosingCallable() = c and
|
||||
constructorStep(p.getAnAccess(), sup)
|
||||
) or
|
||||
)
|
||||
or
|
||||
// a custom InputStream that wraps a tainted data source is tainted
|
||||
inputStreamWrapper(sink.getConstructor(), argi)
|
||||
)
|
||||
@@ -301,23 +339,25 @@ module TaintTracking {
|
||||
private predicate qualifierToArgumentStep(Expr tracked, RValue sink) {
|
||||
exists(MethodAccess ma, int arg |
|
||||
taintPreservingQualifierToArgument(ma.getMethod(), arg) and
|
||||
tracked = ma.getQualifier() and sink = ma.getArgument(arg)
|
||||
tracked = ma.getQualifier() and
|
||||
sink = ma.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
/** Methods that passes tainted data from qualifier to argument.*/
|
||||
/** Methods that passes tainted data from qualifier to argument. */
|
||||
private predicate taintPreservingQualifierToArgument(Method m, int arg) {
|
||||
m instanceof CollectionMethod and
|
||||
m.hasName("toArray") and arg = 1
|
||||
m.hasName("toArray") and
|
||||
arg = 1
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
m.hasName("writeTo") and arg = 0
|
||||
m.hasName("writeTo") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from the qualifier. */
|
||||
private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
|
||||
(taintPreservingQualifierToMethod(sink.getMethod()) or unsafeEscape(sink))
|
||||
and
|
||||
(taintPreservingQualifierToMethod(sink.getMethod()) or unsafeEscape(sink)) and
|
||||
tracked = sink.getQualifier()
|
||||
}
|
||||
|
||||
@@ -336,13 +376,17 @@ module TaintTracking {
|
||||
m.getName() = "toString" or
|
||||
m.getName() = "toUpperCase" or
|
||||
m.getName() = "trim"
|
||||
) or
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" | hasSubtype*(c, m.getDeclaringType())) and
|
||||
)
|
||||
or
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(c, m.getDeclaringType())
|
||||
) and
|
||||
(
|
||||
m.getName().matches("to%String") or
|
||||
m.getName() = "toByteArray" or
|
||||
m.getName().matches("%Value")
|
||||
) or
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().getQualifiedName().matches("%Reader") and
|
||||
m.getName().matches("read%")
|
||||
or
|
||||
@@ -415,10 +459,14 @@ module TaintTracking {
|
||||
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
|
||||
method instanceof StringReplaceMethod and arg = 1
|
||||
or
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" | hasSubtype*(c, method.getDeclaringType())) and
|
||||
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
|
||||
hasSubtype*(c, method.getDeclaringType())
|
||||
) and
|
||||
(
|
||||
method.getName().matches("parse%") and arg = 0 or
|
||||
method.getName().matches("valueOf%") and arg = 0 or
|
||||
method.getName().matches("parse%") and arg = 0
|
||||
or
|
||||
method.getName().matches("valueOf%") and arg = 0
|
||||
or
|
||||
method.getName().matches("to%String") and arg = 0
|
||||
)
|
||||
or
|
||||
@@ -427,8 +475,10 @@ module TaintTracking {
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
|
||||
) and
|
||||
(
|
||||
method.getName() = "append" and arg = 0 or
|
||||
method.getName() = "insert" and arg = 1 or
|
||||
method.getName() = "append" and arg = 0
|
||||
or
|
||||
method.getName() = "insert" and arg = 1
|
||||
or
|
||||
method.getName() = "replace" and arg = 2
|
||||
)
|
||||
or
|
||||
@@ -437,31 +487,44 @@ module TaintTracking {
|
||||
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder")
|
||||
) and
|
||||
(
|
||||
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1 or
|
||||
method.getName() = "decode" and arg = 0 and method.getNumberOfParameters() = 1 or
|
||||
method.getName() = "encodeToString" and arg = 0 or
|
||||
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1
|
||||
or
|
||||
method.getName() = "decode" and arg = 0 and method.getNumberOfParameters() = 1
|
||||
or
|
||||
method.getName() = "encodeToString" and arg = 0
|
||||
or
|
||||
method.getName() = "wrap" and arg = 0
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
(
|
||||
method.getName() = "buffer" and arg = 0 or
|
||||
method.getName() = "readLines" and arg = 0 or
|
||||
method.getName() = "readFully" and arg = 0 and method.getParameterType(1).hasName("int") or
|
||||
method.getName() = "toBufferedInputStream" and arg = 0 or
|
||||
method.getName() = "toBufferedReader" and arg = 0 or
|
||||
method.getName() = "toByteArray" and arg = 0 or
|
||||
method.getName() = "toCharArray" and arg = 0 or
|
||||
method.getName() = "toInputStream" and arg = 0 or
|
||||
method.getName() = "buffer" and arg = 0
|
||||
or
|
||||
method.getName() = "readLines" and arg = 0
|
||||
or
|
||||
method.getName() = "readFully" and arg = 0 and method.getParameterType(1).hasName("int")
|
||||
or
|
||||
method.getName() = "toBufferedInputStream" and arg = 0
|
||||
or
|
||||
method.getName() = "toBufferedReader" and arg = 0
|
||||
or
|
||||
method.getName() = "toByteArray" and arg = 0
|
||||
or
|
||||
method.getName() = "toCharArray" and arg = 0
|
||||
or
|
||||
method.getName() = "toInputStream" and arg = 0
|
||||
or
|
||||
method.getName() = "toString" and arg = 0
|
||||
)
|
||||
or
|
||||
// A URI created from a tainted string is still tainted.
|
||||
method.getDeclaringType().hasQualifiedName("java.net", "URI") and
|
||||
method.hasName("create") and arg = 0
|
||||
method.hasName("create") and
|
||||
arg = 0
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
|
||||
method.hasName("sourceToInputSource") and arg = 0
|
||||
method.hasName("sourceToInputSource") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,19 +545,32 @@ module TaintTracking {
|
||||
* `output`th argument if the `input`th argument is tainted.
|
||||
*/
|
||||
private predicate taintPreservingArgToArg(Method method, int input, int output) {
|
||||
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and (
|
||||
method.hasName("copy") and input = 0 and output = 1 or
|
||||
method.hasName("copyLarge") and input = 0 and output = 1 or
|
||||
method.hasName("read") and input = 0 and output = 1 or
|
||||
method.hasName("readFully") and input = 0 and output = 1 and not method.getParameterType(1).hasName("int") or
|
||||
method.hasName("write") and input = 0 and output = 1 or
|
||||
method.hasName("writeChunked") and input = 0 and output = 1 or
|
||||
method.hasName("writeLines") and input = 0 and output = 2 or
|
||||
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
|
||||
(
|
||||
method.hasName("copy") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("copyLarge") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("read") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("readFully") and
|
||||
input = 0 and
|
||||
output = 1 and
|
||||
not method.getParameterType(1).hasName("int")
|
||||
or
|
||||
method.hasName("write") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("writeChunked") and input = 0 and output = 1
|
||||
or
|
||||
method.hasName("writeLines") and input = 0 and output = 2
|
||||
or
|
||||
method.hasName("writeLines") and input = 1 and output = 2
|
||||
)
|
||||
or
|
||||
method.getDeclaringType().hasQualifiedName("java.lang", "System") and
|
||||
method.hasName("arraycopy") and input = 0 and output = 2
|
||||
method.hasName("arraycopy") and
|
||||
input = 0 and
|
||||
output = 2
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -516,7 +592,8 @@ module TaintTracking {
|
||||
*/
|
||||
private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
|
||||
method.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
|
||||
method.hasName("write") and arg = 0
|
||||
method.hasName("write") and
|
||||
arg = 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,7 +615,8 @@ module TaintTracking {
|
||||
*/
|
||||
private predicate taintPreservingQualifierToArg(Method method, int i) {
|
||||
method.getDeclaringType().hasQualifiedName("java.io", "InputStream") and
|
||||
method.hasName("read") and i = 0
|
||||
method.hasName("read") and
|
||||
i = 0
|
||||
}
|
||||
|
||||
/** A comparison or equality test with a constant. */
|
||||
@@ -552,11 +630,12 @@ module TaintTracking {
|
||||
exists(MethodAccess m | m.getMethod() instanceof EqualsMethod |
|
||||
m = sink and
|
||||
(
|
||||
m.getQualifier() = tracked and m.getArgument(0) = other or
|
||||
m.getQualifier() = tracked and m.getArgument(0) = other
|
||||
or
|
||||
m.getQualifier() = other and m.getArgument(0) = tracked
|
||||
)
|
||||
)
|
||||
|
|
||||
|
|
||||
other.isCompileTimeConstant() or other instanceof NullLiteral
|
||||
)
|
||||
}
|
||||
@@ -610,7 +689,6 @@ module TaintTracking {
|
||||
result.getMethod().hasName("writeObject")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,11 +706,12 @@ class StringBuilderVar extends LocalVariableDecl {
|
||||
* Gets a call that adds something to this string builder, from the argument at the given index.
|
||||
*/
|
||||
MethodAccess getAnInput(int arg) {
|
||||
result.getQualifier() = getAChainedReference()
|
||||
and
|
||||
result.getQualifier() = getAChainedReference() and
|
||||
(
|
||||
result.getMethod().getName() = "append" and arg = 0 or
|
||||
result.getMethod().getName() = "insert" and arg = 1 or
|
||||
result.getMethod().getName() = "append" and arg = 0
|
||||
or
|
||||
result.getMethod().getName() = "insert" and arg = 1
|
||||
or
|
||||
result.getMethod().getName() = "replace" and arg = 2
|
||||
)
|
||||
}
|
||||
@@ -649,7 +728,8 @@ class StringBuilderVar extends LocalVariableDecl {
|
||||
result = getAnAppend() and
|
||||
append = getAnAppend() and
|
||||
(
|
||||
result.getQualifier() = append or
|
||||
result.getQualifier() = append
|
||||
or
|
||||
not exists(MethodAccess chainAccess | chainAccess.getQualifier() = append) and
|
||||
exists(RValue sbva1, RValue sbva2 |
|
||||
adjacentUseUse(sbva1, sbva2) and
|
||||
@@ -678,9 +758,7 @@ class StringBuilderVar extends LocalVariableDecl {
|
||||
/**
|
||||
* Gets an expression that refers to this `StringBuilder`, possibly after some chained calls.
|
||||
*/
|
||||
Expr getAChainedReference() {
|
||||
result = getAChainedReference(_)
|
||||
}
|
||||
Expr getAChainedReference() { result = getAChainedReference(_) }
|
||||
}
|
||||
|
||||
private MethodAccess callReturningSameType(Expr ref) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* type has a subtype or if an inferred upper bound passed through at least one
|
||||
* explicit or implicit cast that lost type information.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
@@ -28,16 +29,22 @@ private class TypeFlowNode extends TTypeFlowNode {
|
||||
result = asExpr().toString() or
|
||||
result = asMethod().toString()
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
result = asField().getLocation() or
|
||||
result = asSsa().getLocation() or
|
||||
result = asExpr().getLocation() or
|
||||
result = asMethod().getLocation()
|
||||
}
|
||||
|
||||
Field asField() { this = TField(result) }
|
||||
|
||||
BaseSsaVariable asSsa() { this = TSsa(result) }
|
||||
|
||||
Expr asExpr() { this = TExpr(result) }
|
||||
|
||||
Method asMethod() { this = TMethod(result) }
|
||||
|
||||
RefType getType() {
|
||||
result = asField().getType() or
|
||||
result = asSsa().getSourceVariable().getType() or
|
||||
@@ -65,11 +72,18 @@ private predicate privateParamArg(Parameter p, Argument arg) {
|
||||
* necessarily functionally determined by `n2`.
|
||||
*/
|
||||
private predicate joinStep0(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
n2.asExpr().(ConditionalExpr).getTrueExpr() = n1.asExpr() or
|
||||
n2.asExpr().(ConditionalExpr).getFalseExpr() = n1.asExpr() or
|
||||
n2.asField().getAnAssignedValue() = n1.asExpr() or
|
||||
n2.asSsa().(BaseSsaPhiNode).getAPhiInput() = n1.asSsa() or
|
||||
exists(ReturnStmt ret | n2.asMethod() = ret.getEnclosingCallable() and ret.getResult() = n1.asExpr()) or
|
||||
n2.asExpr().(ConditionalExpr).getTrueExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(ConditionalExpr).getFalseExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asField().getAnAssignedValue() = n1.asExpr()
|
||||
or
|
||||
n2.asSsa().(BaseSsaPhiNode).getAPhiInput() = n1.asSsa()
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
n2.asMethod() = ret.getEnclosingCallable() and ret.getResult() = n1.asExpr()
|
||||
)
|
||||
or
|
||||
viableImpl_v1(n2.asExpr()) = n1.asMethod()
|
||||
or
|
||||
exists(Argument arg, Parameter p |
|
||||
@@ -84,12 +98,20 @@ private predicate joinStep0(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
* functionally determined by `n2`.
|
||||
*/
|
||||
private predicate step(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
n2.asExpr() = n1.asField().getAnAccess() or
|
||||
n2.asExpr() = n1.asSsa().getAUse() or
|
||||
n2.asExpr().(ParExpr).getExpr() = n1.asExpr() or
|
||||
n2.asExpr().(CastExpr).getExpr() = n1.asExpr() and not n2.asExpr().getType() instanceof PrimitiveType or
|
||||
n2.asExpr().(AssignExpr).getSource() = n1.asExpr() and not n2.asExpr().getType() instanceof PrimitiveType or
|
||||
n2.asSsa().(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr() or
|
||||
n2.asExpr() = n1.asField().getAnAccess()
|
||||
or
|
||||
n2.asExpr() = n1.asSsa().getAUse()
|
||||
or
|
||||
n2.asExpr().(ParExpr).getExpr() = n1.asExpr()
|
||||
or
|
||||
n2.asExpr().(CastExpr).getExpr() = n1.asExpr() and
|
||||
not n2.asExpr().getType() instanceof PrimitiveType
|
||||
or
|
||||
n2.asExpr().(AssignExpr).getSource() = n1.asExpr() and
|
||||
not n2.asExpr().getType() instanceof PrimitiveType
|
||||
or
|
||||
n2.asSsa().(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
n2.asSsa().(BaseSsaImplicitInit).captures(n1.asSsa())
|
||||
}
|
||||
|
||||
@@ -97,8 +119,10 @@ private predicate step(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
* Holds if `null` is the only value that flows to `n`.
|
||||
*/
|
||||
private predicate isNull(TypeFlowNode n) {
|
||||
n.asExpr() instanceof NullLiteral or
|
||||
exists(TypeFlowNode mid | isNull(mid) and step(mid, n)) or
|
||||
n.asExpr() instanceof NullLiteral
|
||||
or
|
||||
exists(TypeFlowNode mid | isNull(mid) and step(mid, n))
|
||||
or
|
||||
forex(TypeFlowNode mid | joinStep0(mid, n) | isNull(mid))
|
||||
}
|
||||
|
||||
@@ -111,7 +135,13 @@ private predicate joinStep(TypeFlowNode n1, TypeFlowNode n2) {
|
||||
}
|
||||
|
||||
private predicate joinStepRank1(int r, TypeFlowNode n1, TypeFlowNode n2) {
|
||||
n1 = rank[r](TypeFlowNode n | joinStep(n, n2) | n order by n.getLocation().getStartLine(), n.getLocation().getStartColumn())
|
||||
n1 = rank[r](TypeFlowNode n |
|
||||
joinStep(n, n2)
|
||||
|
|
||||
n
|
||||
order by
|
||||
n.getLocation().getStartLine(), n.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate joinStepRank2(int r2, int r1, TypeFlowNode n) {
|
||||
@@ -133,8 +163,9 @@ private predicate exactTypeRank(int r, TypeFlowNode n, RefType t) {
|
||||
}
|
||||
|
||||
private predicate exactTypeJoin(int r, TypeFlowNode n, RefType t) {
|
||||
exactTypeRank(1, n, t) and r = 1 or
|
||||
exactTypeJoin(r-1, n, t) and exactTypeRank(r, n, t)
|
||||
exactTypeRank(1, n, t) and r = 1
|
||||
or
|
||||
exactTypeJoin(r - 1, n, t) and exactTypeRank(r, n, t)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,8 +178,10 @@ private predicate exactType(TypeFlowNode n, RefType t) {
|
||||
e.getType() = t and
|
||||
not e instanceof FunctionalExpr and
|
||||
exists(RefType sub | sub.getASourceSupertype() = t.getSourceDeclaration())
|
||||
) or
|
||||
exists(TypeFlowNode mid | exactType(mid, t) and step(mid, n)) or
|
||||
)
|
||||
or
|
||||
exists(TypeFlowNode mid | exactType(mid, t) and step(mid, n))
|
||||
or
|
||||
// The following is an optimized version of
|
||||
// `forex(TypeFlowNode mid | joinStep(mid, n) | exactType(mid, t))`
|
||||
exactTypeJoin(lastRank(n), n, t)
|
||||
@@ -164,11 +197,19 @@ private predicate upcastCand(Expr e, RefType t, RefType t1, RefType t2) {
|
||||
t = boxIfNeeded(e.getType()) and
|
||||
t.getErasure() = t1 and
|
||||
(
|
||||
exists(Variable v | v.getAnAssignedValue() = e and t2 = v.getType().getErasure()) or
|
||||
exists(CastExpr c | c.getExpr() = e and t2 = c.getType().getErasure()) or
|
||||
exists(ReturnStmt ret | ret.getResult() = e and t2 = ret.getEnclosingCallable().getReturnType().getErasure()) or
|
||||
exists(Parameter p | privateParamArg(p, e) and t2 = p.getType().getErasure()) or
|
||||
exists(ConditionalExpr cond | cond.getTrueExpr() = e or cond.getFalseExpr() = e | t2 = cond.getType().getErasure())
|
||||
exists(Variable v | v.getAnAssignedValue() = e and t2 = v.getType().getErasure())
|
||||
or
|
||||
exists(CastExpr c | c.getExpr() = e and t2 = c.getType().getErasure())
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
ret.getResult() = e and t2 = ret.getEnclosingCallable().getReturnType().getErasure()
|
||||
)
|
||||
or
|
||||
exists(Parameter p | privateParamArg(p, e) and t2 = p.getType().getErasure())
|
||||
or
|
||||
exists(ConditionalExpr cond | cond.getTrueExpr() = e or cond.getFalseExpr() = e |
|
||||
t2 = cond.getType().getErasure()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -182,7 +223,8 @@ private predicate upcast(Expr e, RefType t) {
|
||||
|
||||
/** Gets the element type of an array or subtype of `Iterable`. */
|
||||
private Type elementType(RefType t) {
|
||||
result = t.(Array).getComponentType() or
|
||||
result = t.(Array).getComponentType()
|
||||
or
|
||||
exists(ParameterizedType it |
|
||||
it.getSourceDeclaration().hasQualifiedName("java.lang", "Iterable") and
|
||||
result = it.getATypeArgument() and
|
||||
@@ -211,7 +253,9 @@ private predicate upcastEnhancedForStmt(BaseSsaUpdate v, RefType t) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate downcastSuccessorAux(CastExpr cast, BaseSsaVariable v, RefType t, RefType t1, RefType t2) {
|
||||
private predicate downcastSuccessorAux(
|
||||
CastExpr cast, BaseSsaVariable v, RefType t, RefType t1, RefType t2
|
||||
) {
|
||||
cast.getExpr() = v.getAUse() and
|
||||
t = cast.getType() and
|
||||
t1 = t.getErasure() and
|
||||
@@ -260,12 +304,10 @@ private predicate typeFlowJoin(int r, TypeFlowNode n, RefType t) {
|
||||
(
|
||||
r = 1 and typeFlowJoinCand(n, t)
|
||||
or
|
||||
typeFlowJoin(r-1, n, t) and joinStepRank(r, _, n)
|
||||
typeFlowJoin(r - 1, n, t) and joinStepRank(r, _, n)
|
||||
) and
|
||||
forall(TypeFlowNode mid | joinStepRank(r, mid, n) |
|
||||
exists(RefType midtyp |
|
||||
exactType(mid, midtyp) or typeFlow(mid, midtyp)
|
||||
|
|
||||
exists(RefType midtyp | exactType(mid, midtyp) or typeFlow(mid, midtyp) |
|
||||
midtyp.getASupertype*() = t
|
||||
)
|
||||
)
|
||||
@@ -276,11 +318,16 @@ private predicate typeFlowJoin(int r, TypeFlowNode n, RefType t) {
|
||||
* likely to be better than the static type of `n`.
|
||||
*/
|
||||
private predicate typeFlow(TypeFlowNode n, RefType t) {
|
||||
upcast(n.asExpr(), t) or
|
||||
upcastEnhancedForStmt(n.asSsa(), t) or
|
||||
downcastSuccessor(n.asExpr(), t) or
|
||||
instanceOfGuarded(n.asExpr(), t) or
|
||||
exists(TypeFlowNode mid | typeFlow(mid, t) and step(mid, n)) or
|
||||
upcast(n.asExpr(), t)
|
||||
or
|
||||
upcastEnhancedForStmt(n.asSsa(), t)
|
||||
or
|
||||
downcastSuccessor(n.asExpr(), t)
|
||||
or
|
||||
instanceOfGuarded(n.asExpr(), t)
|
||||
or
|
||||
exists(TypeFlowNode mid | typeFlow(mid, t) and step(mid, n))
|
||||
or
|
||||
typeFlowJoin(lastRank(n), n, t)
|
||||
}
|
||||
|
||||
@@ -289,9 +336,7 @@ private predicate typeFlow(TypeFlowNode n, RefType t) {
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate irrelevantBound(TypeFlowNode n, RefType t) {
|
||||
exists(RefType bound |
|
||||
typeFlow(n, bound) or n.getType() = bound
|
||||
|
|
||||
exists(RefType bound | typeFlow(n, bound) or n.getType() = bound |
|
||||
t = bound.getErasure().(RefType).getASourceSupertype+()
|
||||
)
|
||||
}
|
||||
@@ -305,7 +350,8 @@ private predicate bestTypeFlow(TypeFlowNode n, RefType t) {
|
||||
not irrelevantBound(n, t.getErasure())
|
||||
}
|
||||
|
||||
cached private module TypeFlowBounds {
|
||||
cached
|
||||
private module TypeFlowBounds {
|
||||
/**
|
||||
* Holds if the runtime type of `f` is bounded by `t` and if this bound is
|
||||
* likely to be better than the static type of `f`. The flag `exact` indicates
|
||||
@@ -320,8 +366,9 @@ cached private module TypeFlowBounds {
|
||||
or
|
||||
not exactType(n, _) and bestTypeFlow(n, srctype) and exact = false
|
||||
)
|
||||
|
|
||||
t = srctype.(BoundedType).getAnUltimateUpperBoundType() or
|
||||
|
|
||||
t = srctype.(BoundedType).getAnUltimateUpperBoundType()
|
||||
or
|
||||
t = srctype and not srctype instanceof BoundedType
|
||||
)
|
||||
}
|
||||
@@ -340,8 +387,9 @@ cached private module TypeFlowBounds {
|
||||
or
|
||||
not exactType(n, _) and bestTypeFlow(n, srctype) and exact = false
|
||||
)
|
||||
|
|
||||
t = srctype.(BoundedType).getAnUltimateUpperBoundType() or
|
||||
|
|
||||
t = srctype.(BoundedType).getAnUltimateUpperBoundType()
|
||||
or
|
||||
t = srctype and not srctype instanceof BoundedType
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,33 +14,35 @@
|
||||
import java
|
||||
|
||||
private newtype TBaseSsaSourceVariable =
|
||||
TLocalVar(Callable c, LocalScopeVariable v) { c = v.getCallable() or c = v.getAnAccess().getEnclosingCallable() }
|
||||
TLocalVar(Callable c, LocalScopeVariable v) {
|
||||
c = v.getCallable() or c = v.getAnAccess().getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* A local variable in the context of a `Callable` in which it is accessed.
|
||||
*/
|
||||
class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
|
||||
/** Gets the variable corresponding to this `BaseSsaSourceVariable`. */
|
||||
LocalScopeVariable getVariable() {
|
||||
this = TLocalVar(_, result)
|
||||
}
|
||||
LocalScopeVariable getVariable() { this = TLocalVar(_, result) }
|
||||
|
||||
/**
|
||||
* Gets an access of this `BaseSsaSourceVariable`. This access is within `this.getEnclosingCallable()`.
|
||||
*/
|
||||
cached
|
||||
VarAccess getAnAccess() {
|
||||
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) and result = v.getAnAccess() and result.getEnclosingCallable() = c)
|
||||
exists(LocalScopeVariable v, Callable c |
|
||||
this = TLocalVar(c, v) and result = v.getAnAccess() and result.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the `Callable` in which this `BaseSsaSourceVariable` is defined. */
|
||||
Callable getEnclosingCallable() {
|
||||
this = TLocalVar(result, _)
|
||||
}
|
||||
Callable getEnclosingCallable() { this = TLocalVar(result, _) }
|
||||
|
||||
string toString() {
|
||||
exists(LocalScopeVariable v, Callable c | this = TLocalVar(c, v) |
|
||||
if c = v.getCallable() then result = v.getName() else result = c.getName() + "(..)." + v.getName()
|
||||
if c = v.getCallable()
|
||||
then result = v.getName()
|
||||
else result = c.getName() + "(..)." + v.getName()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,27 +51,28 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
|
||||
}
|
||||
|
||||
/** Gets the type of this variable. */
|
||||
Type getType() {
|
||||
result = this.getVariable().getType()
|
||||
}
|
||||
Type getType() { result = this.getVariable().getType() }
|
||||
}
|
||||
|
||||
private cached module SsaImpl {
|
||||
|
||||
cached
|
||||
private module SsaImpl {
|
||||
/** Gets the destination variable of an update of a tracked variable. */
|
||||
cached
|
||||
BaseSsaSourceVariable getDestVar(VariableUpdate upd) {
|
||||
result.getAnAccess() = upd.(Assignment).getDest() or
|
||||
result.getAnAccess() = upd.(Assignment).getDest()
|
||||
or
|
||||
exists(LocalVariableDecl v | v = upd.(LocalVariableDeclExpr).getVariable() |
|
||||
result = TLocalVar(v.getCallable(), v)
|
||||
) or
|
||||
)
|
||||
or
|
||||
result.getAnAccess() = upd.(UnaryAssignExpr).getExpr()
|
||||
}
|
||||
|
||||
/** Holds if `n` updates the local variable `v`. */
|
||||
cached
|
||||
predicate variableUpdate(BaseSsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and b.getNode(i) = n
|
||||
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
|
||||
b.getNode(i) = n
|
||||
}
|
||||
|
||||
/** Gets the definition point of a nested class in the parent scope. */
|
||||
@@ -95,8 +98,12 @@ private cached module SsaImpl {
|
||||
* a `VarAccess` inside a closure. `capturedvar` is the variable in its defining
|
||||
* scope, and `closurevar` is the variable in the closure.
|
||||
*/
|
||||
private ControlFlowNode captureNode(BaseSsaSourceVariable capturedvar, BaseSsaSourceVariable closurevar) {
|
||||
exists(LocalScopeVariable v, Callable inner, Callable outer, NestedClass innerclass, VarAccess va |
|
||||
private ControlFlowNode captureNode(
|
||||
BaseSsaSourceVariable capturedvar, BaseSsaSourceVariable closurevar
|
||||
) {
|
||||
exists(
|
||||
LocalScopeVariable v, Callable inner, Callable outer, NestedClass innerclass, VarAccess va
|
||||
|
|
||||
va.getVariable() = v and
|
||||
inner = va.getEnclosingCallable() and
|
||||
outer = v.getCallable() and
|
||||
@@ -115,7 +122,9 @@ private cached module SsaImpl {
|
||||
}
|
||||
|
||||
/** Holds if the value of `v` is captured in `b` at index `i`. */
|
||||
private predicate variableCapture(BaseSsaSourceVariable capturedvar, BaseSsaSourceVariable closurevar, BasicBlock b, int i) {
|
||||
private predicate variableCapture(
|
||||
BaseSsaSourceVariable capturedvar, BaseSsaSourceVariable closurevar, BasicBlock b, int i
|
||||
) {
|
||||
b.getNode(i) = captureNode(capturedvar, closurevar)
|
||||
}
|
||||
|
||||
@@ -130,10 +139,12 @@ private cached module SsaImpl {
|
||||
|
||||
private predicate liveAtEntry(BaseSsaSourceVariable v, BasicBlock b) {
|
||||
exists(int i | variableUseOrCapture(v, b, i) |
|
||||
not exists(int j | variableUpdate(v, _, b, j) | j < i))
|
||||
not exists(int j | variableUpdate(v, _, b, j) | j < i)
|
||||
)
|
||||
or
|
||||
liveAtExit(v, b) and not variableUpdate(v, _, b, _)
|
||||
}
|
||||
|
||||
private predicate liveAtExit(BaseSsaSourceVariable v, BasicBlock b) {
|
||||
liveAtEntry(v, b.getABBSuccessor())
|
||||
}
|
||||
@@ -150,9 +161,7 @@ private cached module SsaImpl {
|
||||
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
|
||||
cached
|
||||
predicate hasEntryDef(BaseSsaSourceVariable v, BasicBlock b) {
|
||||
exists(LocalScopeVariable l, Callable c |
|
||||
v = TLocalVar(c, l) and c.getBody() = b
|
||||
|
|
||||
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
|
||||
l instanceof Parameter or
|
||||
l.getCallable() != c
|
||||
)
|
||||
@@ -167,8 +176,8 @@ private cached module SsaImpl {
|
||||
* dominator tree. Thus, reaching definitions can be calculated in terms of
|
||||
* dominance.
|
||||
*/
|
||||
cached module SsaDefReaches {
|
||||
|
||||
cached
|
||||
module SsaDefReaches {
|
||||
/**
|
||||
* Holds if `rankix` is the rank the index `i` at which there is an SSA definition or use of
|
||||
* `v` in the basic block `b`.
|
||||
@@ -178,7 +187,9 @@ private cached module SsaImpl {
|
||||
* basic blocks.
|
||||
*/
|
||||
private predicate defUseRank(BaseSsaSourceVariable v, BasicBlock b, int rankix, int i) {
|
||||
i = rank[rankix](int j | any(TrackedSsaDef def).definesAt(v, b, j) or variableUseOrCapture(v, b, j))
|
||||
i = rank[rankix](int j |
|
||||
any(TrackedSsaDef def).definesAt(v, b, j) or variableUseOrCapture(v, b, j)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the maximum rank index for the given variable and basic block. */
|
||||
@@ -187,7 +198,9 @@ private cached module SsaImpl {
|
||||
}
|
||||
|
||||
/** Holds if a definition of an SSA variable occurs at the specified rank index in basic block `b`. */
|
||||
private predicate ssaDefRank(BaseSsaSourceVariable v, TrackedSsaDef def, BasicBlock b, int rankix) {
|
||||
private predicate ssaDefRank(
|
||||
BaseSsaSourceVariable v, TrackedSsaDef def, BasicBlock b, int rankix
|
||||
) {
|
||||
exists(int i |
|
||||
def.definesAt(v, b, i) and
|
||||
defUseRank(v, b, rankix, i)
|
||||
@@ -195,9 +208,14 @@ private cached module SsaImpl {
|
||||
}
|
||||
|
||||
/** Holds if the SSA definition reaches the rank index `rankix` in its own basic block `b`. */
|
||||
private predicate ssaDefReachesRank(BaseSsaSourceVariable v, TrackedSsaDef def, BasicBlock b, int rankix) {
|
||||
ssaDefRank(v, def, b, rankix) or
|
||||
ssaDefReachesRank(v, def, b, rankix-1) and rankix <= lastRank(v, b) and not ssaDefRank(v, _, b, rankix)
|
||||
private predicate ssaDefReachesRank(
|
||||
BaseSsaSourceVariable v, TrackedSsaDef def, BasicBlock b, int rankix
|
||||
) {
|
||||
ssaDefRank(v, def, b, rankix)
|
||||
or
|
||||
ssaDefReachesRank(v, def, b, rankix - 1) and
|
||||
rankix <= lastRank(v, b) and
|
||||
not ssaDefRank(v, _, b, rankix)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +226,8 @@ private cached module SsaImpl {
|
||||
predicate ssaDefReachesEndOfBlock(BaseSsaSourceVariable v, TrackedSsaDef def, BasicBlock b) {
|
||||
liveAtExit(v, b) and
|
||||
(
|
||||
ssaDefReachesRank(v, def, b, lastRank(v, b)) or
|
||||
ssaDefReachesRank(v, def, b, lastRank(v, b))
|
||||
or
|
||||
exists(BasicBlock idom |
|
||||
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
ssaDefReachesEndOfBlock(v, def, idom) and
|
||||
@@ -221,7 +240,9 @@ private cached module SsaImpl {
|
||||
* Holds if the SSA definition of `v` at `def` reaches `use` in the same basic block
|
||||
* without crossing another SSA definition of `v`.
|
||||
*/
|
||||
private predicate ssaDefReachesUseWithinBlock(BaseSsaSourceVariable v, TrackedSsaDef def, RValue use) {
|
||||
private predicate ssaDefReachesUseWithinBlock(
|
||||
BaseSsaSourceVariable v, TrackedSsaDef def, RValue use
|
||||
) {
|
||||
exists(BasicBlock b, int rankix, int i |
|
||||
ssaDefReachesRank(v, def, b, rankix) and
|
||||
defUseRank(v, b, rankix, i) and
|
||||
@@ -235,7 +256,8 @@ private cached module SsaImpl {
|
||||
*/
|
||||
cached
|
||||
predicate ssaDefReachesUse(BaseSsaSourceVariable v, TrackedSsaDef def, RValue use) {
|
||||
ssaDefReachesUseWithinBlock(v, def, use) or
|
||||
ssaDefReachesUseWithinBlock(v, def, use)
|
||||
or
|
||||
exists(BasicBlock b |
|
||||
variableUse(v, use, b, _) and
|
||||
ssaDefReachesEndOfBlock(v, def, b.getABBPredecessor()) and
|
||||
@@ -248,7 +270,9 @@ private cached module SsaImpl {
|
||||
* `closurevar` in the same basic block without crossing another SSA
|
||||
* definition of `v`.
|
||||
*/
|
||||
private predicate ssaDefReachesCaptureWithinBlock(BaseSsaSourceVariable v, TrackedSsaDef def, BaseSsaSourceVariable closurevar) {
|
||||
private predicate ssaDefReachesCaptureWithinBlock(
|
||||
BaseSsaSourceVariable v, TrackedSsaDef def, BaseSsaSourceVariable closurevar
|
||||
) {
|
||||
exists(BasicBlock b, int rankix, int i |
|
||||
ssaDefReachesRank(v, def, b, rankix) and
|
||||
defUseRank(v, b, rankix, i) and
|
||||
@@ -261,25 +285,27 @@ private cached module SsaImpl {
|
||||
* `closurevar` without crossing another SSA definition of `v`.
|
||||
*/
|
||||
cached
|
||||
predicate ssaDefReachesCapture(BaseSsaSourceVariable v, TrackedSsaDef def, BaseSsaSourceVariable closurevar) {
|
||||
ssaDefReachesCaptureWithinBlock(v, def, closurevar) or
|
||||
predicate ssaDefReachesCapture(
|
||||
BaseSsaSourceVariable v, TrackedSsaDef def, BaseSsaSourceVariable closurevar
|
||||
) {
|
||||
ssaDefReachesCaptureWithinBlock(v, def, closurevar)
|
||||
or
|
||||
exists(BasicBlock b |
|
||||
variableCapture(v, closurevar, b, _) and
|
||||
ssaDefReachesEndOfBlock(v, def, b.getABBPredecessor()) and
|
||||
not ssaDefReachesCaptureWithinBlock(v, _, closurevar)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private import SsaImpl
|
||||
private import SsaDefReaches
|
||||
|
||||
private newtype TBaseSsaVariable =
|
||||
TSsaPhiNode(BaseSsaSourceVariable v, BasicBlock b) { phiNode(v, b) } or
|
||||
TSsaUpdate(BaseSsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) { variableUpdate(v, n, b, i) } or
|
||||
TSsaUpdate(BaseSsaSourceVariable v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
variableUpdate(v, n, b, i)
|
||||
} or
|
||||
TSsaEntryDef(BaseSsaSourceVariable v, BasicBlock b) { hasEntryDef(v, b) }
|
||||
|
||||
/**
|
||||
@@ -291,8 +317,10 @@ private class TrackedSsaDef extends BaseSsaVariable {
|
||||
* Phi nodes are placed at index -1.
|
||||
*/
|
||||
predicate definesAt(BaseSsaSourceVariable v, BasicBlock b, int i) {
|
||||
this = TSsaPhiNode(v, b) and i = -1 or
|
||||
this = TSsaUpdate(v, _, b, i) or
|
||||
this = TSsaPhiNode(v, b) and i = -1
|
||||
or
|
||||
this = TSsaUpdate(v, _, b, i)
|
||||
or
|
||||
this = TSsaEntryDef(v, b) and i = 0
|
||||
}
|
||||
}
|
||||
@@ -323,19 +351,13 @@ class BaseSsaVariable extends TBaseSsaVariable {
|
||||
BasicBlock getBasicBlock() { result = getCFGNode().getBasicBlock() }
|
||||
|
||||
/** Gets an access of this SSA variable. */
|
||||
RValue getAUse() {
|
||||
ssaDefReachesUse(_, this, result)
|
||||
}
|
||||
RValue getAUse() { ssaDefReachesUse(_, this, result) }
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) {
|
||||
ssaDefReachesEndOfBlock(_, this, b)
|
||||
}
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(_, this, b) }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
private BaseSsaVariable getAPhiInput() {
|
||||
result = this.(BaseSsaPhiNode).getAPhiInput()
|
||||
}
|
||||
private BaseSsaVariable getAPhiInput() { result = this.(BaseSsaPhiNode).getAPhiInput() }
|
||||
|
||||
/** Gets a definition in the same callable that ultimately defines this variable and is not itself a phi node. */
|
||||
BaseSsaVariable getAnUltimateLocalDefinition() {
|
||||
@@ -361,14 +383,10 @@ class BaseSsaVariable extends TBaseSsaVariable {
|
||||
/** An SSA variable that is defined by a `VariableUpdate`. */
|
||||
class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate {
|
||||
BaseSsaUpdate() {
|
||||
exists(VariableUpdate upd |
|
||||
upd = this.getCFGNode() and getDestVar(upd) = getSourceVariable()
|
||||
)
|
||||
exists(VariableUpdate upd | upd = this.getCFGNode() and getDestVar(upd) = getSourceVariable())
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "SSA def(" + getSourceVariable() + ")"
|
||||
}
|
||||
override string toString() { result = "SSA def(" + getSourceVariable() + ")" }
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
VariableUpdate getDefiningExpr() {
|
||||
@@ -381,9 +399,7 @@ class BaseSsaUpdate extends BaseSsaVariable, TSsaUpdate {
|
||||
* includes initial values of parameters, fields, and closure variables.
|
||||
*/
|
||||
class BaseSsaImplicitInit extends BaseSsaVariable, TSsaEntryDef {
|
||||
override string toString() {
|
||||
result = "SSA init(" + getSourceVariable() + ")"
|
||||
}
|
||||
override string toString() { result = "SSA init(" + getSourceVariable() + ")" }
|
||||
|
||||
/** Holds if this is a closure variable that captures the value of `capturedvar`. */
|
||||
predicate captures(BaseSsaVariable capturedvar) {
|
||||
@@ -400,9 +416,7 @@ class BaseSsaImplicitInit extends BaseSsaVariable, TSsaEntryDef {
|
||||
|
||||
/** An SSA phi node. */
|
||||
class BaseSsaPhiNode extends BaseSsaVariable, TSsaPhiNode {
|
||||
override string toString() {
|
||||
result = "SSA phi(" + getSourceVariable() + ")"
|
||||
}
|
||||
override string toString() { result = "SSA phi(" + getSourceVariable() + ")" }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
BaseSsaVariable getAPhiInput() {
|
||||
@@ -413,4 +427,3 @@ class BaseSsaPhiNode extends BaseSsaVariable, TSsaPhiNode {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@ private module DispatchImpl {
|
||||
exists(RefType srctype | srctype = src.getType() |
|
||||
exists(TypeVariable v | v = srctype |
|
||||
t = v.getUpperBoundType+() and not t instanceof TypeVariable
|
||||
) or
|
||||
)
|
||||
or
|
||||
t = srctype and not srctype instanceof TypeVariable
|
||||
) and
|
||||
if src instanceof ClassInstanceExpr then exact = true else exact = false
|
||||
@@ -62,7 +63,7 @@ private module DispatchImpl {
|
||||
c = viableCallable(ctx) and
|
||||
contextArgHasType(ctx, i, t, exact) and
|
||||
ma.getMethod() = def
|
||||
|
|
||||
|
|
||||
exact = true and result = exactMethodImpl(def, t.getSourceDeclaration())
|
||||
or
|
||||
exact = false and
|
||||
@@ -84,12 +85,14 @@ private module DispatchImpl {
|
||||
}
|
||||
|
||||
private predicate unificationTargets(Type t1, Type t2) {
|
||||
exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g)) or
|
||||
exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g))
|
||||
or
|
||||
exists(Array a1, Array a2 |
|
||||
unificationTargets(a1, a2) and
|
||||
t1 = a1.getComponentType() and
|
||||
t2 = a2.getComponentType()
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(ParameterizedType pt1, ParameterizedType pt2, int pos |
|
||||
unificationTargets(pt1, pt2) and
|
||||
not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and
|
||||
@@ -99,7 +102,9 @@ private module DispatchImpl {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate typeArgsOfUnificationTargets(ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2) {
|
||||
private predicate typeArgsOfUnificationTargets(
|
||||
ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2
|
||||
) {
|
||||
unificationTargets(t1, t2) and
|
||||
arg1 = t1.getTypeArgument(pos) and
|
||||
arg2 = t2.getTypeArgument(pos)
|
||||
@@ -111,14 +116,21 @@ private module DispatchImpl {
|
||||
exists(RefType arg1, RefType arg2 |
|
||||
typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and
|
||||
failsUnification(arg1, arg2)
|
||||
) or
|
||||
failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType()) or
|
||||
)
|
||||
or
|
||||
failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType())
|
||||
or
|
||||
not (
|
||||
t1 instanceof Array and t2 instanceof Array or
|
||||
t1.(PrimitiveType) = t2.(PrimitiveType) or
|
||||
t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration() or
|
||||
t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration() or
|
||||
t1 instanceof BoundedType and t2 instanceof RefType or
|
||||
t1 instanceof Array and t2 instanceof Array
|
||||
or
|
||||
t1.(PrimitiveType) = t2.(PrimitiveType)
|
||||
or
|
||||
t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration()
|
||||
or
|
||||
t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration()
|
||||
or
|
||||
t1 instanceof BoundedType and t2 instanceof RefType
|
||||
or
|
||||
t1 instanceof RefType and t2 instanceof BoundedType
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3,209 +3,238 @@ private import DataFlowUtil
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Arguments that are wrapped in an implicit varargs array
|
||||
* creation are not included, but the implicitly created array is.
|
||||
* Instance arguments are also included.
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() {
|
||||
exists(Argument arg | this.asExpr() = arg | not arg.isVararg()) or
|
||||
this instanceof ImplicitVarargsArray or
|
||||
this = getInstanceArgument(_)
|
||||
}
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Arguments that are wrapped in an implicit varargs array
|
||||
* creation are not included, but the implicitly created array is.
|
||||
* Instance arguments are also included.
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() {
|
||||
exists(Argument arg | this.asExpr() = arg | not arg.isVararg())
|
||||
or
|
||||
this instanceof ImplicitVarargsArray
|
||||
or
|
||||
this = getInstanceArgument(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
predicate argumentOf(Call call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition())
|
||||
or
|
||||
call = this.(ImplicitVarargsArray).getCall() and
|
||||
pos = call.getCallee().getNumberOfParameters() - 1
|
||||
or
|
||||
pos = -1 and this = getInstanceArgument(call)
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends ExprNode {
|
||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getResult()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` through a static field.
|
||||
*/
|
||||
private predicate staticFieldStep(ExprNode node1, ExprNode node2) {
|
||||
exists(Field f, FieldRead fr |
|
||||
f.isStatic() and
|
||||
f.getAnAssignedValue() = node1.getExpr() and
|
||||
fr.getField() = f and
|
||||
fr = node2.getExpr() and
|
||||
hasNonlocalValue(fr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` through variable capture.
|
||||
*/
|
||||
private predicate variableCaptureStep(Node node1, ExprNode node2) {
|
||||
exists(SsaImplicitInit closure, SsaVariable captured |
|
||||
closure.captures(captured) and
|
||||
node2.getExpr() = closure.getAFirstUse()
|
||||
|
|
||||
node1.asExpr() = captured.getAUse()
|
||||
or
|
||||
not exists(captured.getAUse()) and
|
||||
exists(SsaVariable capturedDef | capturedDef = captured.getAnUltimateDefinition() |
|
||||
capturedDef.(SsaImplicitInit).isParameterDefinition(node1.asParameter()) or
|
||||
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = node1
|
||||
.asExpr() or
|
||||
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(AssignOp) = node1.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` through a static field or
|
||||
* variable capture.
|
||||
*/
|
||||
predicate jumpStep(Node node1, Node node2) {
|
||||
staticFieldStep(node1, node2) or
|
||||
variableCaptureStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fa` is an access to an instance field that occurs as the
|
||||
* destination of an assignment of the value `src`.
|
||||
*/
|
||||
predicate instanceFieldAssign(Expr src, FieldAccess fa) {
|
||||
exists(AssignExpr a |
|
||||
a.getSource() = src and
|
||||
a.getDest() = fa and
|
||||
fa.getField() instanceof InstanceField
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of `f`.
|
||||
*/
|
||||
private Type getFieldTypeBound(Field f) {
|
||||
fieldTypeFlow(f, result, _)
|
||||
or
|
||||
not fieldTypeFlow(f, _, _) and result = f.getType()
|
||||
}
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(InstanceField f) or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
|
||||
/**
|
||||
* A reference contained in an object. Examples include instance fields, the
|
||||
* contents of a collection object, or the contents of an array.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract RefType getDeclaringType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
|
||||
/**
|
||||
* Holds if this content may contain an object of the same type as the one
|
||||
* that contains this content, and if this fact should be used to compress
|
||||
* access paths.
|
||||
*
|
||||
* Examples include the tail pointer in a linked list or the left and right
|
||||
* pointers in a binary tree.
|
||||
*/
|
||||
predicate isSelfRef() { none() }
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
InstanceField f;
|
||||
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
|
||||
InstanceField getField() { result = f }
|
||||
|
||||
override string toString() { result = f.toString() }
|
||||
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
|
||||
override RefType getDeclaringType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = getFieldTypeBound(f) }
|
||||
|
||||
override predicate isSelfRef() { compatibleTypes(getDeclaringType(), getType()) }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
|
||||
override RefType getDeclaringType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
|
||||
override RefType getDeclaringType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
exists(FieldAccess fa |
|
||||
instanceFieldAssign(node1.asExpr(), fa) and
|
||||
node2.getPreUpdateNode() = getFieldQualifier(fa) and
|
||||
f.(FieldContent).getField() = fa.getField()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) {
|
||||
exists(FieldRead fr |
|
||||
node1 = getFieldQualifier(fr) and
|
||||
fr.getField() = f.(FieldContent).getField() and
|
||||
fr = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a representative (boxed) type for `t` for the purpose of pruning
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
* numeric conversions, and otherwise the erasure is used.
|
||||
*/
|
||||
RefType getErasedRepr(Type t) {
|
||||
exists(Type e | e = t.getErasure() |
|
||||
if e instanceof NumericOrCharType
|
||||
then result.(BoxedType).getPrimitiveType().getName() = "double"
|
||||
else
|
||||
if e instanceof BooleanType
|
||||
then result.(BoxedType).getPrimitiveType().getName() = "boolean"
|
||||
else result = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate canContainBool(Type t) {
|
||||
t instanceof BooleanType or
|
||||
any(BooleanType b).(RefType).getASourceSupertype+() = t
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(Type t1, Type t2) {
|
||||
exists(Type e1, Type e2 |
|
||||
e1 = getErasedRepr(t1) and
|
||||
e2 = getErasedRepr(t2)
|
||||
|
|
||||
/*
|
||||
* Because of `getErasedRepr`, `erasedHaveIntersection` is a sufficient
|
||||
* compatibility check, but `conContainBool` is kept as a dummy disjunct
|
||||
* to get the proper join-order.
|
||||
*/
|
||||
predicate argumentOf(Call call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition()) or
|
||||
call = this.(ImplicitVarargsArray).getCall() and pos = call.getCallee().getNumberOfParameters() - 1 or
|
||||
pos = -1 and this = getInstanceArgument(call)
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends ExprNode {
|
||||
ReturnNode() {
|
||||
exists(ReturnStmt ret | this.getExpr() = ret.getResult())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` through a static field.
|
||||
*/
|
||||
private predicate staticFieldStep(ExprNode node1, ExprNode node2) {
|
||||
exists(Field f, FieldRead fr |
|
||||
f.isStatic() and
|
||||
f.getAnAssignedValue() = node1.getExpr() and
|
||||
fr.getField() = f and
|
||||
fr = node2.getExpr() and
|
||||
hasNonlocalValue(fr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` through variable capture.
|
||||
*/
|
||||
private predicate variableCaptureStep(Node node1, ExprNode node2) {
|
||||
exists(SsaImplicitInit closure, SsaVariable captured |
|
||||
closure.captures(captured) and
|
||||
node2.getExpr() = closure.getAFirstUse()
|
||||
|
|
||||
node1.asExpr() = captured.getAUse() or
|
||||
not exists(captured.getAUse()) and
|
||||
exists(SsaVariable capturedDef | capturedDef = captured.getAnUltimateDefinition() |
|
||||
capturedDef.(SsaImplicitInit).isParameterDefinition(node1.asParameter()) or
|
||||
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
|
||||
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(AssignOp) = node1.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` through a static field or
|
||||
* variable capture.
|
||||
*/
|
||||
predicate jumpStep(Node node1, Node node2) {
|
||||
staticFieldStep(node1, node2) or
|
||||
variableCaptureStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fa` is an access to an instance field that occurs as the
|
||||
* destination of an assignment of the value `src`.
|
||||
*/
|
||||
predicate instanceFieldAssign(Expr src, FieldAccess fa) {
|
||||
exists(AssignExpr a |
|
||||
a.getSource() = src and
|
||||
a.getDest() = fa and
|
||||
fa.getField() instanceof InstanceField
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of `f`.
|
||||
*/
|
||||
private Type getFieldTypeBound(Field f) {
|
||||
fieldTypeFlow(f, result, _) or
|
||||
not fieldTypeFlow(f, _, _) and result = f.getType()
|
||||
}
|
||||
|
||||
private newtype TContent = TFieldContent(InstanceField f) or TCollectionContent() or TArrayContent()
|
||||
|
||||
/**
|
||||
* A reference contained in an object. Examples include instance fields, the
|
||||
* contents of a collection object, or the contents of an array.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract RefType getDeclaringType();
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
/**
|
||||
* Holds if this content may contain an object of the same type as the one
|
||||
* that contains this content, and if this fact should be used to compress
|
||||
* access paths.
|
||||
*
|
||||
* Examples include the tail pointer in a linked list or the left and right
|
||||
* pointers in a binary tree.
|
||||
*/
|
||||
predicate isSelfRef() { none() }
|
||||
}
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
InstanceField f;
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
InstanceField getField() { result = f }
|
||||
override string toString() { result = f.toString() }
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
override RefType getDeclaringType() { result = f.getDeclaringType() }
|
||||
override Type getType() { result = getFieldTypeBound(f) }
|
||||
override predicate isSelfRef() { compatibleTypes(getDeclaringType(), getType()) }
|
||||
}
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
override RefType getDeclaringType() { none() }
|
||||
override Type getType() { none() }
|
||||
}
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
override RefType getDeclaringType() { none() }
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
exists(FieldAccess fa |
|
||||
instanceFieldAssign(node1.asExpr(), fa) and
|
||||
node2.getPreUpdateNode() = getFieldQualifier(fa) and
|
||||
f.(FieldContent).getField() = fa.getField()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) {
|
||||
exists(FieldRead fr |
|
||||
node1 = getFieldQualifier(fr) and
|
||||
fr.getField() = f.(FieldContent).getField() and
|
||||
fr = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a representative (boxed) type for `t` for the purpose of pruning
|
||||
* possible flow. A single type is used for all numeric types to account for
|
||||
* numeric conversions, and otherwise the erasure is used.
|
||||
*/
|
||||
RefType getErasedRepr(Type t) {
|
||||
exists(Type e | e = t.getErasure() |
|
||||
if e instanceof NumericOrCharType then
|
||||
result.(BoxedType).getPrimitiveType().getName() = "double"
|
||||
else if e instanceof BooleanType then
|
||||
result.(BoxedType).getPrimitiveType().getName() = "boolean"
|
||||
else
|
||||
result = e
|
||||
)
|
||||
}
|
||||
|
||||
private predicate canContainBool(Type t) {
|
||||
t instanceof BooleanType or
|
||||
any(BooleanType b).(RefType).getASourceSupertype+() = t
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(Type t1, Type t2) {
|
||||
exists(Type e1, Type e2 |
|
||||
e1 = getErasedRepr(t1) and
|
||||
e2 = getErasedRepr(t2)
|
||||
|
|
||||
/*
|
||||
* Because of `getErasedRepr`, `erasedHaveIntersection` is a sufficient
|
||||
* compatibility check, but `conContainBool` is kept as a dummy disjunct
|
||||
* to get the proper join-order.
|
||||
*/
|
||||
erasedHaveIntersection(e1, e2) or
|
||||
canContainBool(e1) and canContainBool(e2)
|
||||
)
|
||||
}
|
||||
erasedHaveIntersection(e1, e2)
|
||||
or
|
||||
canContainBool(e1) and canContainBool(e2)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,354 +1,405 @@
|
||||
/**
|
||||
* Basic definitions for use in the data flow library.
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
import semmle.code.java.dataflow.InstanceAccess
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) or
|
||||
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
|
||||
TImplicitVarargsArray(Call c) { c.getCallee().isVarargs() and not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray()) } or
|
||||
TInstanceParameterNode(Callable c) { exists(c.getBody()) and not c.isStatic() } or
|
||||
TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or
|
||||
TMallocNode(ClassInstanceExpr cie) or
|
||||
TExplicitArgPostCall(Expr e) { explicitInstanceArgument(_, e) or e instanceof Argument } or
|
||||
TImplicitArgPostCall(InstanceAccessExt ia) { implicitInstanceArgument(_, ia) } or
|
||||
TExplicitStoreTarget(Expr e) { exists(FieldAccess fa | instanceFieldAssign(_, fa) and e = fa.getQualifier()) } or
|
||||
TImplicitStoreTarget(InstanceAccessExt ia) { exists(FieldAccess fa | instanceFieldAssign(_, fa) and ia.isImplicitFieldQualifier(fa)) }
|
||||
|
||||
/**
|
||||
* An element, viewed as a node in a data flow graph. Either an expression,
|
||||
* a parameter, or an implicit varargs array creation.
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.asExpr().getType() or
|
||||
result = this.asParameter().getType() or
|
||||
exists(Parameter p | result = p.getType() and p.isVarargs() and p = this.(ImplicitVarargsArray).getCall().getCallee().getAParameter()) or
|
||||
result = this.(InstanceParameterNode).getCallable().getDeclaringType() or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getType() or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getType() or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.asExpr().getEnclosingCallable() or
|
||||
result = this.asParameter().getCallable() or
|
||||
result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
|
||||
result = this.(InstanceParameterNode).getCallable() or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallable()
|
||||
}
|
||||
|
||||
private Type getImprovedTypeBound() {
|
||||
exprTypeFlow(this.asExpr(), result, _) or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getImprovedTypeBound()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Type getTypeBound() {
|
||||
result = getImprovedTypeBound() or
|
||||
result = getType() and not exists(getImprovedTypeBound())
|
||||
}
|
||||
cached
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) or
|
||||
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
|
||||
TImplicitVarargsArray(Call c) {
|
||||
c.getCallee().isVarargs() and
|
||||
not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray())
|
||||
} or
|
||||
TInstanceParameterNode(Callable c) { exists(c.getBody()) and not c.isStatic() } or
|
||||
TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or
|
||||
TMallocNode(ClassInstanceExpr cie) or
|
||||
TExplicitArgPostCall(Expr e) { explicitInstanceArgument(_, e) or e instanceof Argument } or
|
||||
TImplicitArgPostCall(InstanceAccessExt ia) { implicitInstanceArgument(_, ia) } or
|
||||
TExplicitStoreTarget(Expr e) {
|
||||
exists(FieldAccess fa | instanceFieldAssign(_, fa) and e = fa.getQualifier())
|
||||
} or
|
||||
TImplicitStoreTarget(InstanceAccessExt ia) {
|
||||
exists(FieldAccess fa | instanceFieldAssign(_, fa) and ia.isImplicitFieldQualifier(fa))
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
override string toString() { result = expr.toString() }
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
/**
|
||||
* An element, viewed as a node in a data flow graph. Either an expression,
|
||||
* a parameter, or an implicit varargs array creation.
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the node corresponding to `e`. */
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** An explicit or implicit parameter. */
|
||||
abstract class ParameterNode extends Node {
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate isParameterOf(Callable c, int pos);
|
||||
}
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/**
|
||||
* A parameter, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExplicitParameterNode extends ParameterNode, TExplicitParameterNode {
|
||||
Parameter param;
|
||||
ExplicitParameterNode() { this = TExplicitParameterNode(param) }
|
||||
override string toString() { result = param.toString() }
|
||||
override Location getLocation() { result = param.getLocation() }
|
||||
/** Gets the parameter corresponding to this node. */
|
||||
Parameter getParameter() { result = param }
|
||||
override predicate isParameterOf(Callable c, int pos) {
|
||||
c.getParameter(pos) = param
|
||||
}
|
||||
}
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
|
||||
|
||||
/** Gets the node corresponding to `p`. */
|
||||
ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
|
||||
/**
|
||||
* An implicit varargs array creation expression.
|
||||
*
|
||||
* A call `f(x1, x2)` to a method `f(A... xs)` desugars to `f(new A[]{x1, x2})`,
|
||||
* and this node corresponds to such an implicit array creation.
|
||||
*/
|
||||
class ImplicitVarargsArray extends Node, TImplicitVarargsArray {
|
||||
Call call;
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call) }
|
||||
override string toString() { result = "new ..[] { .. }" }
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
/** Gets the call containing this varargs array creation argument. */
|
||||
Call getCall() { result = call }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance parameter for an instance method or constructor.
|
||||
*/
|
||||
class InstanceParameterNode extends ParameterNode, TInstanceParameterNode {
|
||||
Callable callable;
|
||||
InstanceParameterNode() { this = TInstanceParameterNode(callable) }
|
||||
override string toString() { result = "parameter this" }
|
||||
override Location getLocation() { result = callable.getLocation() }
|
||||
/** Gets the callable containing this `this` parameter. */
|
||||
Callable getCallable() { result = callable }
|
||||
override predicate isParameterOf(Callable c, int pos) {
|
||||
callable = c and pos = -1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An implicit read of `this` or `A.this`.
|
||||
*/
|
||||
class ImplicitInstanceAccess extends Node, TImplicitInstanceAccess {
|
||||
InstanceAccessExt ia;
|
||||
ImplicitInstanceAccess() { this = TImplicitInstanceAccess(ia) }
|
||||
override string toString() { result = ia.toString() }
|
||||
override Location getLocation() { result = ia.getLocation() }
|
||||
InstanceAccessExt getInstanceAccess() { result = ia }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that corresponds to the value of a `ClassInstanceExpr` before the
|
||||
* constructor has run.
|
||||
*/
|
||||
private class MallocNode extends Node, TMallocNode {
|
||||
ClassInstanceExpr cie;
|
||||
MallocNode() { this = TMallocNode(cie) }
|
||||
override string toString() { result = cie.toString() + " [pre constructor]" }
|
||||
override Location getLocation() { result = cie.getLocation() }
|
||||
ClassInstanceExpr getClassInstanceExpr() { result = cie }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
private class NewExpr extends PostUpdateNode, TExprNode {
|
||||
NewExpr() { exists(ClassInstanceExpr cie | this = TExprNode(cie)) }
|
||||
override Node getPreUpdateNode() { this = TExprNode(result.(MallocNode).getClassInstanceExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PostUpdateNode` that is not a `ClassInstanceExpr`.
|
||||
*/
|
||||
private abstract class ImplicitPostUpdateNode extends PostUpdateNode {
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
|
||||
}
|
||||
|
||||
private class ExplicitArgPostCall extends ImplicitPostUpdateNode, TExplicitArgPostCall {
|
||||
override Node getPreUpdateNode() { this = TExplicitArgPostCall(result.asExpr()) }
|
||||
}
|
||||
|
||||
private class ImplicitArgPostCall extends ImplicitPostUpdateNode, TImplicitArgPostCall {
|
||||
override Node getPreUpdateNode() { this = TImplicitArgPostCall(result.(ImplicitInstanceAccess).getInstanceAccess()) }
|
||||
}
|
||||
|
||||
private class ExplicitStoreTarget extends ImplicitPostUpdateNode, TExplicitStoreTarget {
|
||||
override Node getPreUpdateNode() { this = TExplicitStoreTarget(result.asExpr()) }
|
||||
}
|
||||
|
||||
private class ImplicitStoreTarget extends ImplicitPostUpdateNode, TImplicitStoreTarget {
|
||||
override Node getPreUpdateNode() { this = TImplicitStoreTarget(result.(ImplicitInstanceAccess).getInstanceAccess()) }
|
||||
}
|
||||
|
||||
/** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */
|
||||
private predicate thisAccess(Node n, ControlFlowNode cfgnode) {
|
||||
n.(InstanceParameterNode).getCallable().getBody() = cfgnode or
|
||||
exists(InstanceAccess ia | ia = n.asExpr() and ia = cfgnode and ia.isOwnInstanceAccess()) or
|
||||
n.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getCfgNode() = cfgnode
|
||||
}
|
||||
|
||||
/** Calculation of the relative order in which `this` references are read. */
|
||||
private module ThisFlow {
|
||||
private predicate thisAccess(Node n, BasicBlock b, int i) {
|
||||
thisAccess(n, b.getNode(i))
|
||||
}
|
||||
|
||||
private predicate thisRank(Node n, BasicBlock b, int rankix) {
|
||||
exists(int i |
|
||||
i = rank[rankix](int j | thisAccess(_, b, j)) and
|
||||
thisAccess(n, b, i)
|
||||
)
|
||||
}
|
||||
|
||||
private int lastRank(BasicBlock b) {
|
||||
result = max(int rankix | thisRank(_, b, rankix))
|
||||
}
|
||||
|
||||
private predicate blockPrecedesThisAccess(BasicBlock b) {
|
||||
thisAccess(_, b.getABBSuccessor*(), _)
|
||||
}
|
||||
|
||||
private predicate thisAccessBlockReaches(BasicBlock b1, BasicBlock b2) {
|
||||
thisAccess(_, b1, _) and b2 = b1.getABBSuccessor() or
|
||||
exists(BasicBlock mid |
|
||||
thisAccessBlockReaches(b1, mid) and
|
||||
b2 = mid.getABBSuccessor() and
|
||||
not thisAccess(_, mid, _) and
|
||||
blockPrecedesThisAccess(b2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate thisAccessBlockStep(BasicBlock b1, BasicBlock b2) {
|
||||
thisAccessBlockReaches(b1, b2) and
|
||||
thisAccess(_, b2, _)
|
||||
}
|
||||
|
||||
/** Holds if `n1` and `n2` are control-flow adjacent references to `this`. */
|
||||
predicate adjacentThisRefs(Node n1, Node n2) {
|
||||
exists(int rankix, BasicBlock b |
|
||||
thisRank(n1, b, rankix) and
|
||||
thisRank(n2, b, rankix+1)
|
||||
) or
|
||||
exists(BasicBlock b1, BasicBlock b2 |
|
||||
thisRank(n1, b1, lastRank(b1)) and
|
||||
thisAccessBlockStep(b1, b2) and
|
||||
thisRank(n2, b2, 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localFlow(Node node1, Node node2) {
|
||||
localFlowStep*(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `FieldRead` is not completely determined by explicit SSA
|
||||
* updates.
|
||||
*/
|
||||
predicate hasNonlocalValue(FieldRead fr) {
|
||||
not exists(SsaVariable v | v.getAUse() = fr) or
|
||||
exists(SsaVariable v, SsaVariable def |
|
||||
v.getAUse() = fr and def = v.getAnUltimateDefinition()
|
||||
|
|
||||
def instanceof SsaImplicitInit or
|
||||
def instanceof SsaImplicitUpdate
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node node1, Node node2) {
|
||||
// Variable flow steps through adjacent def-use and use-use pairs.
|
||||
exists(SsaExplicitUpdate upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
|
||||
upd.getDefiningExpr().(AssignOp) = node1.asExpr()
|
||||
|
|
||||
node2.asExpr() = upd.getAFirstUse()
|
||||
) or
|
||||
exists(SsaImplicitInit init |
|
||||
init.isParameterDefinition(node1.asParameter()) and
|
||||
node2.asExpr() = init.getAFirstUse()
|
||||
) or
|
||||
adjacentUseUse(node1.asExpr(), node2.asExpr()) and
|
||||
not exists(FieldRead fr | hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr())
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.asExpr().getType()
|
||||
or
|
||||
ThisFlow::adjacentThisRefs(node1, node2) or
|
||||
adjacentUseUse(node1.(PostUpdateNode).getPreUpdateNode().asExpr(), node2.asExpr()) or
|
||||
ThisFlow::adjacentThisRefs(node1.(PostUpdateNode).getPreUpdateNode(), node2) or
|
||||
node2.asExpr().(ParExpr).getExpr() = node1.asExpr() or
|
||||
node2.asExpr().(CastExpr).getExpr() = node1.asExpr() or
|
||||
node2.asExpr().(ConditionalExpr).getTrueExpr() = node1.asExpr() or
|
||||
node2.asExpr().(ConditionalExpr).getFalseExpr() = node1.asExpr() or
|
||||
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
|
||||
result = this.asParameter().getType()
|
||||
or
|
||||
exists(Parameter p |
|
||||
result = p.getType() and
|
||||
p.isVarargs() and
|
||||
p = this.(ImplicitVarargsArray).getCall().getCallee().getAParameter()
|
||||
)
|
||||
or
|
||||
result = this.(InstanceParameterNode).getCallable().getDeclaringType()
|
||||
or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getType()
|
||||
or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getType()
|
||||
or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
|
||||
}
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
Callable getEnclosingCallable() {
|
||||
result = this.asExpr().getEnclosingCallable() or
|
||||
result = this.asParameter().getCallable() or
|
||||
result = this.(ImplicitVarargsArray).getCall().getEnclosingCallable() or
|
||||
result = this.(InstanceParameterNode).getCallable() or
|
||||
result = this.(ImplicitInstanceAccess).getInstanceAccess().getEnclosingCallable() or
|
||||
result = this.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getEnclosingCallable()
|
||||
}
|
||||
|
||||
private Type getImprovedTypeBound() {
|
||||
exprTypeFlow(this.asExpr(), result, _) or
|
||||
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getImprovedTypeBound()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node that occurs as the qualifier of `fa`.
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Node getFieldQualifier(FieldAccess fa) {
|
||||
fa.getField() instanceof InstanceField and
|
||||
(
|
||||
result.asExpr() = fa.getQualifier() or
|
||||
result.(ImplicitInstanceAccess).getInstanceAccess().isImplicitFieldQualifier(fa)
|
||||
Type getTypeBound() {
|
||||
result = getImprovedTypeBound()
|
||||
or
|
||||
result = getType() and not exists(getImprovedTypeBound())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
/** Gets the node corresponding to `e`. */
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
|
||||
/** An explicit or implicit parameter. */
|
||||
abstract class ParameterNode extends Node {
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate isParameterOf(Callable c, int pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExplicitParameterNode extends ParameterNode, TExplicitParameterNode {
|
||||
Parameter param;
|
||||
|
||||
ExplicitParameterNode() { this = TExplicitParameterNode(param) }
|
||||
|
||||
override string toString() { result = param.toString() }
|
||||
|
||||
override Location getLocation() { result = param.getLocation() }
|
||||
|
||||
/** Gets the parameter corresponding to this node. */
|
||||
Parameter getParameter() { result = param }
|
||||
|
||||
override predicate isParameterOf(Callable c, int pos) { c.getParameter(pos) = param }
|
||||
}
|
||||
|
||||
/** Gets the node corresponding to `p`. */
|
||||
ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
|
||||
/**
|
||||
* An implicit varargs array creation expression.
|
||||
*
|
||||
* A call `f(x1, x2)` to a method `f(A... xs)` desugars to `f(new A[]{x1, x2})`,
|
||||
* and this node corresponds to such an implicit array creation.
|
||||
*/
|
||||
class ImplicitVarargsArray extends Node, TImplicitVarargsArray {
|
||||
Call call;
|
||||
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call) }
|
||||
|
||||
override string toString() { result = "new ..[] { .. }" }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
|
||||
/** Gets the call containing this varargs array creation argument. */
|
||||
Call getCall() { result = call }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance parameter for an instance method or constructor.
|
||||
*/
|
||||
class InstanceParameterNode extends ParameterNode, TInstanceParameterNode {
|
||||
Callable callable;
|
||||
|
||||
InstanceParameterNode() { this = TInstanceParameterNode(callable) }
|
||||
|
||||
override string toString() { result = "parameter this" }
|
||||
|
||||
override Location getLocation() { result = callable.getLocation() }
|
||||
|
||||
/** Gets the callable containing this `this` parameter. */
|
||||
Callable getCallable() { result = callable }
|
||||
|
||||
override predicate isParameterOf(Callable c, int pos) { callable = c and pos = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An implicit read of `this` or `A.this`.
|
||||
*/
|
||||
class ImplicitInstanceAccess extends Node, TImplicitInstanceAccess {
|
||||
InstanceAccessExt ia;
|
||||
|
||||
ImplicitInstanceAccess() { this = TImplicitInstanceAccess(ia) }
|
||||
|
||||
override string toString() { result = ia.toString() }
|
||||
|
||||
override Location getLocation() { result = ia.getLocation() }
|
||||
|
||||
InstanceAccessExt getInstanceAccess() { result = ia }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that corresponds to the value of a `ClassInstanceExpr` before the
|
||||
* constructor has run.
|
||||
*/
|
||||
private class MallocNode extends Node, TMallocNode {
|
||||
ClassInstanceExpr cie;
|
||||
|
||||
MallocNode() { this = TMallocNode(cie) }
|
||||
|
||||
override string toString() { result = cie.toString() + " [pre constructor]" }
|
||||
|
||||
override Location getLocation() { result = cie.getLocation() }
|
||||
|
||||
ClassInstanceExpr getClassInstanceExpr() { result = cie }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
private class NewExpr extends PostUpdateNode, TExprNode {
|
||||
NewExpr() { exists(ClassInstanceExpr cie | this = TExprNode(cie)) }
|
||||
|
||||
override Node getPreUpdateNode() { this = TExprNode(result.(MallocNode).getClassInstanceExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PostUpdateNode` that is not a `ClassInstanceExpr`.
|
||||
*/
|
||||
abstract private class ImplicitPostUpdateNode extends PostUpdateNode {
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
|
||||
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
|
||||
}
|
||||
|
||||
private class ExplicitArgPostCall extends ImplicitPostUpdateNode, TExplicitArgPostCall {
|
||||
override Node getPreUpdateNode() { this = TExplicitArgPostCall(result.asExpr()) }
|
||||
}
|
||||
|
||||
private class ImplicitArgPostCall extends ImplicitPostUpdateNode, TImplicitArgPostCall {
|
||||
override Node getPreUpdateNode() {
|
||||
this = TImplicitArgPostCall(result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
}
|
||||
}
|
||||
|
||||
private class ExplicitStoreTarget extends ImplicitPostUpdateNode, TExplicitStoreTarget {
|
||||
override Node getPreUpdateNode() { this = TExplicitStoreTarget(result.asExpr()) }
|
||||
}
|
||||
|
||||
private class ImplicitStoreTarget extends ImplicitPostUpdateNode, TImplicitStoreTarget {
|
||||
override Node getPreUpdateNode() {
|
||||
this = TImplicitStoreTarget(result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */
|
||||
private predicate thisAccess(Node n, ControlFlowNode cfgnode) {
|
||||
n.(InstanceParameterNode).getCallable().getBody() = cfgnode
|
||||
or
|
||||
exists(InstanceAccess ia | ia = n.asExpr() and ia = cfgnode and ia.isOwnInstanceAccess())
|
||||
or
|
||||
n.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getCfgNode() = cfgnode
|
||||
}
|
||||
|
||||
/** Calculation of the relative order in which `this` references are read. */
|
||||
private module ThisFlow {
|
||||
private predicate thisAccess(Node n, BasicBlock b, int i) { thisAccess(n, b.getNode(i)) }
|
||||
|
||||
private predicate thisRank(Node n, BasicBlock b, int rankix) {
|
||||
exists(int i |
|
||||
i = rank[rankix](int j | thisAccess(_, b, j)) and
|
||||
thisAccess(n, b, i)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate explicitInstanceArgument(Call call, Expr instarg) {
|
||||
call instanceof MethodAccess and instarg = call.getQualifier() and not call.getCallee().isStatic()
|
||||
private int lastRank(BasicBlock b) { result = max(int rankix | thisRank(_, b, rankix)) }
|
||||
|
||||
private predicate blockPrecedesThisAccess(BasicBlock b) { thisAccess(_, b.getABBSuccessor*(), _) }
|
||||
|
||||
private predicate thisAccessBlockReaches(BasicBlock b1, BasicBlock b2) {
|
||||
thisAccess(_, b1, _) and b2 = b1.getABBSuccessor()
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
thisAccessBlockReaches(b1, mid) and
|
||||
b2 = mid.getABBSuccessor() and
|
||||
not thisAccess(_, mid, _) and
|
||||
blockPrecedesThisAccess(b2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate implicitInstanceArgument(Call call, InstanceAccessExt ia) {
|
||||
ia.isImplicitMethodQualifier(call) or
|
||||
ia.isImplicitThisConstructorArgument(call)
|
||||
private predicate thisAccessBlockStep(BasicBlock b1, BasicBlock b2) {
|
||||
thisAccessBlockReaches(b1, b2) and
|
||||
thisAccess(_, b2, _)
|
||||
}
|
||||
|
||||
/** Gets the instance argument of a non-static call. */
|
||||
Node getInstanceArgument(Call call) {
|
||||
result.(MallocNode).getClassInstanceExpr() = call or
|
||||
explicitInstanceArgument(call, result.asExpr()) or
|
||||
implicitInstanceArgument(call, result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
/** Holds if `n1` and `n2` are control-flow adjacent references to `this`. */
|
||||
predicate adjacentThisRefs(Node n1, Node n2) {
|
||||
exists(int rankix, BasicBlock b |
|
||||
thisRank(n1, b, rankix) and
|
||||
thisRank(n2, b, rankix + 1)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock b1, BasicBlock b2 |
|
||||
thisRank(n1, b1, lastRank(b1)) and
|
||||
thisAccessBlockStep(b1, b2) and
|
||||
thisRank(n2, b2, 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localFlow(Node node1, Node node2) { localFlowStep*(node1, node2) }
|
||||
|
||||
/**
|
||||
* Holds if the `FieldRead` is not completely determined by explicit SSA
|
||||
* updates.
|
||||
*/
|
||||
predicate hasNonlocalValue(FieldRead fr) {
|
||||
not exists(SsaVariable v | v.getAUse() = fr)
|
||||
or
|
||||
exists(SsaVariable v, SsaVariable def | v.getAUse() = fr and def = v.getAnUltimateDefinition() |
|
||||
def instanceof SsaImplicitInit or
|
||||
def instanceof SsaImplicitUpdate
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in one local step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node node1, Node node2) {
|
||||
// Variable flow steps through adjacent def-use and use-use pairs.
|
||||
exists(SsaExplicitUpdate upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
|
||||
upd.getDefiningExpr().(AssignOp) = node1.asExpr()
|
||||
|
|
||||
node2.asExpr() = upd.getAFirstUse()
|
||||
)
|
||||
or
|
||||
exists(SsaImplicitInit init |
|
||||
init.isParameterDefinition(node1.asParameter()) and
|
||||
node2.asExpr() = init.getAFirstUse()
|
||||
)
|
||||
or
|
||||
adjacentUseUse(node1.asExpr(), node2.asExpr()) and
|
||||
not exists(FieldRead fr |
|
||||
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
|
||||
)
|
||||
or
|
||||
ThisFlow::adjacentThisRefs(node1, node2)
|
||||
or
|
||||
adjacentUseUse(node1.(PostUpdateNode).getPreUpdateNode().asExpr(), node2.asExpr())
|
||||
or
|
||||
ThisFlow::adjacentThisRefs(node1.(PostUpdateNode).getPreUpdateNode(), node2)
|
||||
or
|
||||
node2.asExpr().(ParExpr).getExpr() = node1.asExpr()
|
||||
or
|
||||
node2.asExpr().(CastExpr).getExpr() = node1.asExpr()
|
||||
or
|
||||
node2.asExpr().(ConditionalExpr).getTrueExpr() = node1.asExpr()
|
||||
or
|
||||
node2.asExpr().(ConditionalExpr).getFalseExpr() = node1.asExpr()
|
||||
or
|
||||
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node that occurs as the qualifier of `fa`.
|
||||
*/
|
||||
Node getFieldQualifier(FieldAccess fa) {
|
||||
fa.getField() instanceof InstanceField and
|
||||
(
|
||||
result.asExpr() = fa.getQualifier() or
|
||||
result.(ImplicitInstanceAccess).getInstanceAccess().isImplicitFieldQualifier(fa)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate explicitInstanceArgument(Call call, Expr instarg) {
|
||||
call instanceof MethodAccess and instarg = call.getQualifier() and not call.getCallee().isStatic()
|
||||
}
|
||||
|
||||
private predicate implicitInstanceArgument(Call call, InstanceAccessExt ia) {
|
||||
ia.isImplicitMethodQualifier(call) or
|
||||
ia.isImplicitThisConstructorArgument(call)
|
||||
}
|
||||
|
||||
/** Gets the instance argument of a non-static call. */
|
||||
Node getInstanceArgument(Call call) {
|
||||
result.(MallocNode).getClassInstanceExpr() = call or
|
||||
explicitInstanceArgument(call, result.asExpr()) or
|
||||
implicitInstanceArgument(call, result.(ImplicitInstanceAccess).getInstanceAccess())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user