Java: Autoformat most of semmle.code.java.dataflow.

This commit is contained in:
Anders Schack-Mulligen
2018-10-11 15:59:01 +02:00
parent f341aa79a3
commit 5502db4c74
18 changed files with 1932 additions and 1315 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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
)
}

View File

@@ -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)
)
)

View File

@@ -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))
)
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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) {

View File

@@ -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
)
}

View File

@@ -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 {
)
}
}

View File

@@ -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
)
)

View File

@@ -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)
)
}

View File

@@ -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())
}