mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Merge branch 'main' into java-kotlin-sensitive-logging-substring-barriers
This commit is contained in:
@@ -1808,6 +1808,52 @@ class VariableAssign extends VariableUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TVariableWrite =
|
||||
TParamInit(Parameter p) or
|
||||
TVarWriteExpr(VariableUpdate u)
|
||||
|
||||
/**
|
||||
* A write to a variable. This is either a local variable declaration,
|
||||
* including parameter declarations, or an update to a variable.
|
||||
*/
|
||||
class VariableWrite extends TVariableWrite {
|
||||
/** Gets the expression representing this write, if any. */
|
||||
Expr asExpr() { this = TVarWriteExpr(result) }
|
||||
|
||||
/**
|
||||
* Gets the expression with the value being written, if any.
|
||||
*
|
||||
* This can be the same expression as returned by `asExpr()`, which is the
|
||||
* case for, for example, `++x` and `x += e`. For simple assignments like
|
||||
* `x = e`, `asExpr()` gets the whole assignment expression while
|
||||
* `getValue()` gets the right-hand side `e`. Post-crement operations like
|
||||
* `x++` do not have an expression with the value being written.
|
||||
*/
|
||||
Expr getValue() {
|
||||
this.asExpr().(VariableAssign).getSource() = result or
|
||||
this.asExpr().(AssignOp) = result or
|
||||
this.asExpr().(PreIncExpr) = result or
|
||||
this.asExpr().(PreDecExpr) = result
|
||||
}
|
||||
|
||||
/** Holds if this write is an initialization of parameter `p`. */
|
||||
predicate isParameterInit(Parameter p) { this = TParamInit(p) }
|
||||
|
||||
/** Gets a textual representation of this write. */
|
||||
string toString() {
|
||||
exists(Parameter p | this = TParamInit(p) and result = p.toString())
|
||||
or
|
||||
result = this.asExpr().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this write. */
|
||||
Location getLocation() {
|
||||
exists(Parameter p | this = TParamInit(p) and result = p.getLocation())
|
||||
or
|
||||
result = this.asExpr().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/** A type literal. For example, `String.class`. */
|
||||
class TypeLiteral extends Expr, @typeliteral {
|
||||
/** Gets the access to the type whose class is accessed. */
|
||||
|
||||
@@ -6,11 +6,12 @@ module;
|
||||
|
||||
import java
|
||||
private import codeql.controlflow.ControlFlowReachability
|
||||
private import semmle.code.java.dataflow.SSA as SSA
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.controlflow.Guards as Guards
|
||||
|
||||
private module ControlFlowInput implements InputSig<Location, ControlFlowNode, BasicBlock> {
|
||||
private import java as J
|
||||
import Ssa
|
||||
|
||||
AstNode getEnclosingAstNode(ControlFlowNode node) { node.getAstNode() = result }
|
||||
|
||||
@@ -27,23 +28,6 @@ private module ControlFlowInput implements InputSig<Location, ControlFlowNode, B
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class SourceVariable = SSA::SsaSourceVariable;
|
||||
|
||||
class SsaDefinition = SSA::SsaVariable;
|
||||
|
||||
class SsaExplicitWrite extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
|
||||
Expr getValue() {
|
||||
super.getDefiningExpr().(VariableAssign).getSource() = result or
|
||||
super.getDefiningExpr().(AssignOp) = result
|
||||
}
|
||||
}
|
||||
|
||||
class SsaPhiDefinition = SSA::SsaPhiNode;
|
||||
|
||||
class SsaUncertainWrite extends SsaDefinition instanceof SSA::SsaUncertainImplicitUpdate {
|
||||
SsaDefinition getPriorDefinition() { result = super.getPriorDef() }
|
||||
}
|
||||
|
||||
class GuardValue = Guards::GuardValue;
|
||||
|
||||
predicate ssaControlsBranchEdge(SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v) {
|
||||
|
||||
@@ -141,7 +141,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre
|
||||
|
||||
private module GuardsInput implements SharedGuards::InputSig<Location, ControlFlowNode, BasicBlock> {
|
||||
private import java as J
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.NullGuards as NullGuards
|
||||
|
||||
class NormalExitNode = ControlFlow::NormalExitNode;
|
||||
@@ -211,10 +211,10 @@ private module GuardsInput implements SharedGuards::InputSig<Location, ControlFl
|
||||
f.getInitializer() = NullGuards::baseNotNullExpr()
|
||||
)
|
||||
or
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl, BaseSsaUpdate v |
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl, Base::SsaExplicitWrite v |
|
||||
decl = cc.getVariable() and
|
||||
decl = v.getDefiningExpr() and
|
||||
this = v.getAUse()
|
||||
this = v.getARead()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -407,30 +407,8 @@ private module LogicInputCommon {
|
||||
}
|
||||
|
||||
private module LogicInput_v1 implements GuardsImpl::LogicInputSig {
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
|
||||
final private class FinalBaseSsaVariable = BaseSsaVariable;
|
||||
|
||||
class SsaDefinition extends FinalBaseSsaVariable {
|
||||
GuardsInput::Expr getARead() { result = this.getAUse() }
|
||||
}
|
||||
|
||||
class SsaExplicitWrite extends SsaDefinition instanceof BaseSsaUpdate {
|
||||
GuardsInput::Expr getValue() {
|
||||
super.getDefiningExpr().(VariableAssign).getSource() = result or
|
||||
super.getDefiningExpr().(AssignOp) = result
|
||||
}
|
||||
}
|
||||
|
||||
class SsaPhiDefinition extends SsaDefinition instanceof BaseSsaPhiNode {
|
||||
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) {
|
||||
super.hasInputFromBlock(inp, bb)
|
||||
}
|
||||
}
|
||||
|
||||
class SsaParameterInit extends SsaDefinition instanceof BaseSsaImplicitInit {
|
||||
Parameter getParameter() { super.isParameterDefinition(result) }
|
||||
}
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
import Base::Ssa
|
||||
|
||||
predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;
|
||||
|
||||
@@ -438,30 +416,8 @@ private module LogicInput_v1 implements GuardsImpl::LogicInputSig {
|
||||
}
|
||||
|
||||
private module LogicInput_v2 implements GuardsImpl::LogicInputSig {
|
||||
private import semmle.code.java.dataflow.SSA as SSA
|
||||
|
||||
final private class FinalSsaVariable = SSA::SsaVariable;
|
||||
|
||||
class SsaDefinition extends FinalSsaVariable {
|
||||
GuardsInput::Expr getARead() { result = this.getAUse() }
|
||||
}
|
||||
|
||||
class SsaExplicitWrite extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
|
||||
GuardsInput::Expr getValue() {
|
||||
super.getDefiningExpr().(VariableAssign).getSource() = result or
|
||||
super.getDefiningExpr().(AssignOp) = result
|
||||
}
|
||||
}
|
||||
|
||||
class SsaPhiDefinition extends SsaDefinition instanceof SSA::SsaPhiNode {
|
||||
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) {
|
||||
super.hasInputFromBlock(inp, bb)
|
||||
}
|
||||
}
|
||||
|
||||
class SsaParameterInit extends SsaDefinition instanceof SSA::SsaImplicitInit {
|
||||
Parameter getParameter() { super.isParameterDefinition(result) }
|
||||
}
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
import Ssa
|
||||
|
||||
predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;
|
||||
|
||||
|
||||
@@ -34,8 +34,12 @@ predicate useUsePair(VarRead use1, VarRead use2) { adjacentUseUse+(use1, use2) }
|
||||
* Other paths may also exist, so the SSA variables in `def` and `use` can be different.
|
||||
*/
|
||||
predicate defUsePair(VariableUpdate def, VarRead use) {
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = use and v.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr() = def
|
||||
exists(SsaDefinition v, SsaExplicitWrite write |
|
||||
v.getARead() = use and write.getDefiningExpr() = def
|
||||
|
|
||||
v.getAnUltimateDefinition() = write
|
||||
or
|
||||
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = write
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,7 +50,9 @@ predicate defUsePair(VariableUpdate def, VarRead use) {
|
||||
* Other paths may also exist, so the SSA variables can be different.
|
||||
*/
|
||||
predicate parameterDefUsePair(Parameter p, VarRead use) {
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = use and v.getAnUltimateDefinition().(SsaImplicitInit).isParameterDefinition(p)
|
||||
exists(SsaDefinition v, SsaParameterInit init | v.getARead() = use and init.getParameter() = p |
|
||||
v.getAnUltimateDefinition() = init
|
||||
or
|
||||
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = init
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ Expr enumConstEquality(Expr e, boolean polarity, EnumConstant c) {
|
||||
}
|
||||
|
||||
/** Gets an instanceof expression of `v` with type `type` */
|
||||
InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
|
||||
InstanceOfExpr instanceofExpr(SsaDefinition v, RefType type) {
|
||||
result.getCheckedType() = type and
|
||||
result.getExpr() = v.getAUse()
|
||||
result.getExpr() = v.getARead()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,8 +37,8 @@ InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
|
||||
*
|
||||
* Note this includes Kotlin's `==` and `!=` operators, which are value-equality tests.
|
||||
*/
|
||||
EqualityTest varEqualityTestExpr(SsaVariable v1, SsaVariable v2, boolean isEqualExpr) {
|
||||
result.hasOperands(v1.getAUse(), v2.getAUse()) and
|
||||
EqualityTest varEqualityTestExpr(SsaDefinition v1, SsaDefinition v2, boolean isEqualExpr) {
|
||||
result.hasOperands(v1.getARead(), v2.getARead()) and
|
||||
isEqualExpr = result.polarity()
|
||||
}
|
||||
|
||||
@@ -91,37 +91,44 @@ Expr clearlyNotNullExpr(Expr reason) {
|
||||
(reason = r1 or reason = r2)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, boolean branch, VarRead rval, Guard guard |
|
||||
exists(SsaDefinition v, boolean branch, VarRead rval, Guard guard |
|
||||
guard = directNullGuard(v, branch, false) and
|
||||
guard.controls(rval.getBasicBlock(), branch) and
|
||||
reason = guard and
|
||||
rval = v.getAUse() and
|
||||
rval = v.getARead() and
|
||||
result = rval and
|
||||
not result = baseNotNullExpr()
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
exists(SsaDefinition v |
|
||||
clearlyNotNull(v, reason) and
|
||||
result = v.getAUse() and
|
||||
result = v.getARead() and
|
||||
not result = baseNotNullExpr()
|
||||
)
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.isFinal() and
|
||||
f.getInitializer() = clearlyNotNullExpr(reason) and
|
||||
not result = baseNotNullExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
||||
predicate clearlyNotNull(SsaVariable v, Expr reason) {
|
||||
predicate clearlyNotNull(SsaDefinition v, Expr reason) {
|
||||
exists(Expr src |
|
||||
src = v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() and
|
||||
src = v.(SsaExplicitWrite).getValue() and
|
||||
src = clearlyNotNullExpr(reason)
|
||||
)
|
||||
or
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl |
|
||||
decl = cc.getVariable() and
|
||||
decl = v.(SsaExplicitUpdate).getDefiningExpr() and
|
||||
decl = v.(SsaExplicitWrite).getDefiningExpr() and
|
||||
reason = decl
|
||||
)
|
||||
or
|
||||
exists(SsaVariable captured |
|
||||
v.(SsaImplicitInit).captures(captured) and
|
||||
exists(SsaDefinition captured |
|
||||
v.(SsaCapturedDefinition).captures(captured) and
|
||||
clearlyNotNull(captured, reason)
|
||||
)
|
||||
or
|
||||
@@ -136,7 +143,7 @@ predicate clearlyNotNull(SsaVariable v, Expr reason) {
|
||||
Expr clearlyNotNullExpr() { result = clearlyNotNullExpr(_) }
|
||||
|
||||
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
||||
predicate clearlyNotNull(SsaVariable v) { clearlyNotNull(v, _) }
|
||||
predicate clearlyNotNull(SsaDefinition v) { clearlyNotNull(v, _) }
|
||||
|
||||
/**
|
||||
* Holds if the evaluation of a call to `m` resulting in the value `branch`
|
||||
@@ -207,7 +214,7 @@ deprecated Expr basicOrCustomNullGuard(Expr e, boolean branch, boolean isnull) {
|
||||
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
|
||||
* is true, and non-null if `isnull` is false.
|
||||
*/
|
||||
Expr directNullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
Expr directNullGuard(SsaDefinition v, boolean branch, boolean isnull) {
|
||||
result = basicNullGuard(sameValue(v, _), branch, isnull)
|
||||
}
|
||||
|
||||
@@ -219,7 +226,7 @@ Expr directNullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
|
||||
* is true, and non-null if `isnull` is false.
|
||||
*/
|
||||
deprecated Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
deprecated Guard nullGuard(SsaDefinition v, boolean branch, boolean isnull) {
|
||||
result = directNullGuard(v, branch, isnull)
|
||||
}
|
||||
|
||||
@@ -228,7 +235,9 @@ deprecated Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
* from `bb1` to `bb2` implies that `v` is guaranteed to be null if `isnull` is
|
||||
* true, and non-null if `isnull` is false.
|
||||
*/
|
||||
predicate nullGuardControlsBranchEdge(SsaVariable v, boolean isnull, BasicBlock bb1, BasicBlock bb2) {
|
||||
predicate nullGuardControlsBranchEdge(
|
||||
SsaDefinition v, boolean isnull, BasicBlock bb1, BasicBlock bb2
|
||||
) {
|
||||
exists(GuardValue gv |
|
||||
Guards_v3::ssaControlsBranchEdge(v, bb1, bb2, gv) and
|
||||
gv.isNullness(isnull)
|
||||
@@ -240,7 +249,7 @@ predicate nullGuardControlsBranchEdge(SsaVariable v, boolean isnull, BasicBlock
|
||||
* `bb` `v` is guaranteed to be null if `isnull` is true, and non-null if
|
||||
* `isnull` is false.
|
||||
*/
|
||||
predicate nullGuardControls(SsaVariable v, boolean isnull, BasicBlock bb) {
|
||||
predicate nullGuardControls(SsaDefinition v, boolean isnull, BasicBlock bb) {
|
||||
exists(GuardValue gv |
|
||||
Guards_v3::ssaControls(v, bb, gv) and
|
||||
gv.isNullness(isnull)
|
||||
@@ -263,6 +272,6 @@ predicate guardSuggestsExprMaybeNull(Expr guard, Expr e) {
|
||||
/**
|
||||
* Holds if `guard` is a guard expression that suggests that `v` might be null.
|
||||
*/
|
||||
predicate guardSuggestsVarMaybeNull(Expr guard, SsaVariable v) {
|
||||
predicate guardSuggestsVarMaybeNull(Expr guard, SsaDefinition v) {
|
||||
guardSuggestsExprMaybeNull(guard, sameValue(v, _))
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ predicate dereference(Expr e) {
|
||||
*
|
||||
* The `VarAccess` is included for nicer error reporting.
|
||||
*/
|
||||
private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
private ControlFlowNode varDereference(SsaDefinition v, VarAccess va) {
|
||||
dereference(result.asExpr()) and
|
||||
result.asExpr() = sameValue(v, va)
|
||||
}
|
||||
@@ -121,7 +121,7 @@ private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
/**
|
||||
* The first dereference of a variable in a given `BasicBlock`.
|
||||
*/
|
||||
private predicate firstVarDereferenceInBlock(BasicBlock bb, SsaVariable v, VarAccess va) {
|
||||
private predicate firstVarDereferenceInBlock(BasicBlock bb, SsaDefinition v, VarAccess va) {
|
||||
exists(ControlFlowNode n |
|
||||
varDereference(v, va) = n and
|
||||
n.getBasicBlock() = bb and
|
||||
@@ -135,14 +135,14 @@ private predicate firstVarDereferenceInBlock(BasicBlock bb, SsaVariable v, VarAc
|
||||
}
|
||||
|
||||
/** A variable suspected of being `null`. */
|
||||
private predicate varMaybeNull(SsaVariable v, ControlFlowNode node, string msg, Expr reason) {
|
||||
private predicate varMaybeNull(SsaDefinition v, ControlFlowNode node, string msg, Expr reason) {
|
||||
// A variable compared to null might be null.
|
||||
exists(Expr e |
|
||||
reason = e and
|
||||
msg = "as suggested by $@ null guard" and
|
||||
guardSuggestsVarMaybeNull(e, v) and
|
||||
node = v.getCfgNode() and
|
||||
not v instanceof SsaPhiNode and
|
||||
node = v.getControlFlowNode() and
|
||||
not v instanceof SsaPhiDefinition 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().getEnclosingStmt*()) and
|
||||
@@ -151,13 +151,13 @@ private predicate varMaybeNull(SsaVariable v, ControlFlowNode node, string msg,
|
||||
not exists(MethodCall 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()
|
||||
not v.(SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = nullExpr()
|
||||
)
|
||||
or
|
||||
// A parameter might be null if there is a null argument somewhere.
|
||||
exists(Parameter p, Expr arg |
|
||||
v.(SsaImplicitInit).isParameterDefinition(p) and
|
||||
node = v.getCfgNode() and
|
||||
v.(SsaParameterInit).getParameter() = p and
|
||||
node = v.getControlFlowNode() and
|
||||
p.getAnArgument() = arg and
|
||||
reason = arg and
|
||||
msg = "because of $@ null argument" and
|
||||
@@ -167,7 +167,7 @@ private predicate varMaybeNull(SsaVariable v, ControlFlowNode node, string msg,
|
||||
or
|
||||
// If the source of a variable is null then the variable may be null.
|
||||
exists(VariableAssign def |
|
||||
v.(SsaExplicitUpdate).getDefiningExpr() = def and
|
||||
v.(SsaExplicitWrite).getDefiningExpr() = def and
|
||||
def.getSource() = nullExpr(node.asExpr()) and
|
||||
reason = def and
|
||||
msg = "because of $@ assignment"
|
||||
@@ -179,26 +179,26 @@ private Expr nonEmptyExpr() {
|
||||
// An array creation with a known positive size is trivially non-empty.
|
||||
result.(ArrayCreationExpr).getFirstDimensionSize() > 0
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
exists(SsaDefinition v |
|
||||
// A use of an array variable is non-empty if...
|
||||
result = v.getAUse() and
|
||||
result = v.getARead() and
|
||||
v.getSourceVariable().getType() instanceof Array
|
||||
|
|
||||
// ...its definition is non-empty...
|
||||
v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = nonEmptyExpr()
|
||||
v.(SsaExplicitWrite).getValue() = 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
|
||||
cond.getCondition() = nonZeroGuard(length, branch) and
|
||||
length.getField().hasName("length") and
|
||||
length.getQualifier() = v.getAUse()
|
||||
length.getQualifier() = v.getARead()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
exists(SsaDefinition v |
|
||||
// A use of a Collection variable is non-empty if...
|
||||
result = v.getAUse() and
|
||||
result = v.getARead() and
|
||||
v.getSourceVariable().getType() instanceof CollectionType and
|
||||
exists(ConditionBlock cond, boolean branch, Expr c |
|
||||
// ...it is guarded by a condition...
|
||||
@@ -216,13 +216,13 @@ private Expr nonEmptyExpr() {
|
||||
// ...and the condition proves that it is non-empty, either by using the `isEmpty` method...
|
||||
c.(MethodCall).getMethod().hasName("isEmpty") and
|
||||
branch = false and
|
||||
c.(MethodCall).getQualifier() = v.getAUse()
|
||||
c.(MethodCall).getQualifier() = v.getARead()
|
||||
or
|
||||
// ...or a check on its `size`.
|
||||
exists(MethodCall size |
|
||||
c = nonZeroGuard(size, branch) and
|
||||
size.getMethod().hasName("size") and
|
||||
size.getQualifier() = v.getAUse()
|
||||
size.getQualifier() = v.getARead()
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -249,9 +249,9 @@ private predicate impossibleEdge(BasicBlock bb1, BasicBlock bb2) {
|
||||
}
|
||||
|
||||
private module NullnessConfig implements ControlFlowReachability::ConfigSig {
|
||||
predicate source(ControlFlowNode node, SsaVariable def) { varMaybeNull(def, node, _, _) }
|
||||
predicate source(ControlFlowNode node, SsaDefinition def) { varMaybeNull(def, node, _, _) }
|
||||
|
||||
predicate sink(ControlFlowNode node, SsaVariable def) { varDereference(def, _) = node }
|
||||
predicate sink(ControlFlowNode node, SsaDefinition def) { varDereference(def, _) = node }
|
||||
|
||||
predicate barrierValue(GuardValue gv) { gv.isNullness(false) }
|
||||
|
||||
@@ -266,7 +266,7 @@ private module NullnessFlow = ControlFlowReachability::Flow<NullnessConfig>;
|
||||
* Holds if the dereference of `v` at `va` might be `null`.
|
||||
*/
|
||||
predicate nullDeref(SsaSourceVariable v, VarAccess va, string msg, Expr reason) {
|
||||
exists(SsaVariable origin, SsaVariable ssa, ControlFlowNode src, ControlFlowNode sink |
|
||||
exists(SsaDefinition origin, SsaDefinition ssa, ControlFlowNode src, ControlFlowNode sink |
|
||||
varMaybeNull(origin, src, msg, reason) and
|
||||
NullnessFlow::flow(src, origin, sink, ssa) and
|
||||
ssa.getSourceVariable() = v and
|
||||
@@ -278,9 +278,9 @@ predicate nullDeref(SsaSourceVariable v, VarAccess va, string msg, Expr reason)
|
||||
* A dereference of a variable that is always `null`.
|
||||
*/
|
||||
predicate alwaysNullDeref(SsaSourceVariable v, VarAccess va) {
|
||||
exists(BasicBlock bb, SsaVariable ssa |
|
||||
forall(SsaVariable def | def = ssa.getAnUltimateDefinition() |
|
||||
def.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = alwaysNullExpr()
|
||||
exists(BasicBlock bb, SsaDefinition ssa |
|
||||
forall(SsaDefinition def | def = ssa.getAnUltimateDefinition() |
|
||||
def.(SsaExplicitWrite).getValue() = alwaysNullExpr()
|
||||
)
|
||||
or
|
||||
nullGuardControls(ssa, true, bb) and
|
||||
|
||||
@@ -242,17 +242,17 @@ module Sem implements Semantic<Location> {
|
||||
|
||||
Type getSsaType(SsaVariable var) { result = var.getSourceVariable().getType() }
|
||||
|
||||
final private class FinalSsaVariable = SSA::SsaVariable;
|
||||
final private class FinalSsaVariable = SSA::SsaDefinition;
|
||||
|
||||
class SsaVariable extends FinalSsaVariable {
|
||||
Expr getAUse() { result = super.getAUse() }
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class SsaPhiNode extends SsaVariable instanceof SSA::SsaPhiNode {
|
||||
class SsaPhiNode extends SsaVariable instanceof SSA::SsaPhiDefinition {
|
||||
predicate hasInputFromBlock(SsaVariable inp, BasicBlock bb) { super.hasInputFromBlock(inp, bb) }
|
||||
}
|
||||
|
||||
class SsaExplicitUpdate extends SsaVariable instanceof SSA::SsaExplicitUpdate {
|
||||
class SsaExplicitUpdate extends SsaVariable instanceof SSA::SsaExplicitWrite {
|
||||
Expr getDefiningExpr() { result = super.getDefiningExpr() }
|
||||
}
|
||||
|
||||
|
||||
@@ -30,17 +30,17 @@ predicate eqFlowCond = U::eqFlowCond/5;
|
||||
* only other input to `phi` is a `null` value.
|
||||
*
|
||||
* Note that the declared type of `phi` is `SsaVariable` instead of
|
||||
* `SsaPhiNode` in order for the reflexive case of `nonNullSsaFwdStep*(..)` to
|
||||
* have non-`SsaPhiNode` results.
|
||||
* `SsaPhiDefinition` in order for the reflexive case of `nonNullSsaFwdStep*(..)` to
|
||||
* have non-`SsaPhiDefinition` results.
|
||||
*/
|
||||
private predicate nonNullSsaFwdStep(SsaVariable v, SsaVariable phi) {
|
||||
exists(SsaExplicitUpdate vnull, SsaPhiNode phi0 | phi0 = phi |
|
||||
2 = strictcount(phi0.getAPhiInput()) and
|
||||
vnull = phi0.getAPhiInput() and
|
||||
v = phi0.getAPhiInput() and
|
||||
private predicate nonNullSsaFwdStep(SsaDefinition v, SsaDefinition phi) {
|
||||
exists(SsaExplicitWrite vnull, SsaPhiDefinition phi0 | phi0 = phi |
|
||||
2 = strictcount(phi0.getAnInput()) and
|
||||
vnull = phi0.getAnInput() and
|
||||
v = phi0.getAnInput() and
|
||||
not backEdge(phi0, v, _) and
|
||||
vnull != v and
|
||||
vnull.getDefiningExpr().(VariableAssign).getSource() instanceof NullLiteral
|
||||
vnull.getValue() instanceof NullLiteral
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,13 +56,13 @@ private predicate nonNullDefStep(Expr e1, Expr e2) {
|
||||
* explicit `ArrayCreationExpr` definition and that the definition does not go
|
||||
* through a back edge.
|
||||
*/
|
||||
ArrayCreationExpr getArrayDef(SsaVariable v) {
|
||||
ArrayCreationExpr getArrayDef(SsaDefinition v) {
|
||||
exists(Expr src |
|
||||
v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = src and
|
||||
v.(SsaExplicitWrite).getValue() = src and
|
||||
nonNullDefStep*(result, src)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable mid |
|
||||
exists(SsaDefinition mid |
|
||||
result = getArrayDef(mid) and
|
||||
nonNullSsaFwdStep(mid, v)
|
||||
)
|
||||
@@ -74,9 +74,9 @@ ArrayCreationExpr getArrayDef(SsaVariable v) {
|
||||
* `arrlen` without going through a back edge.
|
||||
*/
|
||||
private predicate arrayLengthDef(FieldRead arrlen, ArrayCreationExpr def) {
|
||||
exists(SsaVariable arr |
|
||||
exists(SsaDefinition arr |
|
||||
arrlen.getField() instanceof ArrayLengthField and
|
||||
arrlen.getQualifier() = arr.getAUse() and
|
||||
arrlen.getQualifier() = arr.getARead() and
|
||||
def = getArrayDef(arr)
|
||||
)
|
||||
}
|
||||
@@ -86,9 +86,9 @@ pragma[nomagic]
|
||||
private predicate constantIntegerExpr(Expr e, int val) {
|
||||
e.(CompileTimeConstantExpr).getIntValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantIntegerExpr(src, val)
|
||||
)
|
||||
or
|
||||
@@ -112,9 +112,9 @@ pragma[nomagic]
|
||||
private predicate constantBooleanExpr(Expr e, boolean val) {
|
||||
e.(CompileTimeConstantExpr).getBooleanValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantBooleanExpr(src, val)
|
||||
)
|
||||
or
|
||||
@@ -125,9 +125,9 @@ pragma[nomagic]
|
||||
private predicate constantStringExpr(Expr e, string val) {
|
||||
e.(CompileTimeConstantExpr).getStringValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantStringExpr(src, val)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
* `ControlFlowNode` at which it is defined. Each SSA variable is defined
|
||||
* either by a phi node, an implicit initial value (for parameters and fields),
|
||||
* an explicit update, or an implicit update (for fields).
|
||||
* An implicit update occurs either at a `Call` that might modify a field, at
|
||||
* another update that can update the qualifier of a field, or at a `FieldRead`
|
||||
* of the field in case the field is not amenable to a non-trivial SSA
|
||||
* representation.
|
||||
* An implicit update occurs either at a `Call` that might modify a field, or
|
||||
* at another update that can update the qualifier of a field.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import internal.SsaImpl
|
||||
import internal.SsaImpl::Ssa as Ssa
|
||||
import Ssa
|
||||
|
||||
/**
|
||||
* A fully qualified variable in the context of a `Callable` in which it is
|
||||
@@ -105,7 +105,7 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
SsaSourceVariable getQualifier() { this = TQualifiedField(_, result, _) }
|
||||
|
||||
/** Gets an SSA variable that has this variable as its underlying source variable. */
|
||||
SsaVariable getAnSsaVariable() { result.getSourceVariable() = this }
|
||||
SsaDefinition getAnSsaVariable() { result.getSourceVariable() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,22 +138,76 @@ class SsaSourceField extends SsaSourceVariable {
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition in a closure that captures a variable. */
|
||||
class SsaCapturedDefinition extends SsaImplicitEntryDefinition {
|
||||
SsaCapturedDefinition() { captures(this, _) }
|
||||
|
||||
override string toString() { result = "SSA capture def(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/** Holds if this definition captures the value of `capturedvar`. */
|
||||
predicate captures(SsaDefinition capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* Gets a definition that ultimately defines the captured variable and is not itself a phi node.
|
||||
*/
|
||||
SsaDefinition getAnUltimateCapturedDefinition() {
|
||||
exists(SsaDefinition capturedvar |
|
||||
captures(this, capturedvar) and result = capturedvar.getAnUltimateDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSA definition representing the potential definition of a variable
|
||||
* via a call.
|
||||
*/
|
||||
class SsaImplicitCallDefinition extends SsaImplicitWrite {
|
||||
SsaImplicitCallDefinition() { isNonLocal(this) and not hasQualifierUpdate(this) }
|
||||
|
||||
override string toString() { result = "SSA call def(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/**
|
||||
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
FieldWrite getANonLocalUpdate() { result = getANonLocalUpdate(this) }
|
||||
}
|
||||
|
||||
/** An SSA definition due to an update of the qualifier. */
|
||||
class SsaImplicitQualifierDefinition extends SsaImplicitWrite {
|
||||
SsaImplicitQualifierDefinition() { hasQualifierUpdate(this) }
|
||||
|
||||
override string toString() { result = "SSA qualifier def(" + this.getSourceVariable() + ")" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access of the SSA source variable underlying this SSA variable
|
||||
* that can be reached from this SSA variable without passing through any
|
||||
* other uses, but potentially through phi nodes and uncertain implicit
|
||||
* updates.
|
||||
*/
|
||||
VarRead ssaGetAFirstUse(SsaDefinition def) { firstUse(def, result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `SsaDefinition` instead.
|
||||
*
|
||||
* An SSA variable.
|
||||
*/
|
||||
class SsaVariable extends Definition {
|
||||
/** Gets the SSA source variable underlying this SSA variable. */
|
||||
SsaSourceVariable getSourceVariable() { result = super.getSourceVariable() }
|
||||
|
||||
/** Gets the `ControlFlowNode` at which this SSA variable is defined. */
|
||||
/**
|
||||
* DEPRECATED: Use `getControlFlowNode()` instead.
|
||||
*
|
||||
* Gets the `ControlFlowNode` at which this SSA variable is defined.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(BasicBlock bb, int i, int j |
|
||||
exists(BasicBlock bb, int i |
|
||||
this.definesAt(_, bb, i) and
|
||||
// untracked definitions are inserted just before reads
|
||||
(if this instanceof UntrackedDef then j = i + 1 else j = i) and
|
||||
// phi nodes are inserted at position `-1`
|
||||
result = bb.getNode(0.maximum(j))
|
||||
result = bb.getNode(0.maximum(i))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -166,10 +220,16 @@ class SsaVariable extends Definition {
|
||||
/** Gets the `BasicBlock` in which this SSA variable is defined. */
|
||||
BasicBlock getBasicBlock() { result = super.getBasicBlock() }
|
||||
|
||||
/** Gets an access of this SSA variable. */
|
||||
/**
|
||||
* DEPRECATED: Use `getARead()` instead.
|
||||
*
|
||||
* Gets an access of this SSA variable.
|
||||
*/
|
||||
VarRead getAUse() { result = getAUse(this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ssaGetAFirstUse(SsaDefinition)` instead.
|
||||
*
|
||||
* Gets an access of the SSA source variable underlying this SSA variable
|
||||
* that can be reached from this SSA variable without passing through any
|
||||
* other uses, but potentially through phi nodes and uncertain implicit
|
||||
@@ -178,34 +238,49 @@ class SsaVariable extends Definition {
|
||||
* Subsequent uses can be found by following the steps defined by
|
||||
* `adjacentUseUse`.
|
||||
*/
|
||||
VarRead getAFirstUse() { firstUse(this, result) }
|
||||
deprecated VarRead getAFirstUse() { firstUse(this, result) }
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(b, this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED.
|
||||
*
|
||||
* Gets an SSA variable whose value can flow to this one in one step. This
|
||||
* includes inputs to phi nodes, the prior definition of uncertain updates,
|
||||
* and the captured ssa variable for a closure variable.
|
||||
*/
|
||||
SsaVariable getAPhiInputOrPriorDef() {
|
||||
deprecated SsaVariable getAPhiInputOrPriorDef() {
|
||||
result = this.(SsaPhiNode).getAPhiInput() or
|
||||
result = this.(SsaUncertainImplicitUpdate).getPriorDef() or
|
||||
this.(SsaImplicitInit).captures(result)
|
||||
}
|
||||
|
||||
/** Gets a definition that ultimately defines this variable and is not itself a phi node. */
|
||||
SsaVariable getAnUltimateDefinition() {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaCapturedDefinition::getAnUltimateCapturedDefinition()`
|
||||
* and/or `SsaDefinition::getAnUltimateDefinition()` instead.
|
||||
*
|
||||
* Gets a definition that ultimately defines this variable and is not itself a phi node.
|
||||
*/
|
||||
deprecated SsaVariable getAnUltimateDefinition() {
|
||||
result = this.getAPhiInputOrPriorDef*() and not result instanceof SsaPhiNode
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA variable that either explicitly or implicitly updates the variable. */
|
||||
/**
|
||||
* DEPRECATED: use `SsaWriteDefinition` instead.
|
||||
*
|
||||
* An SSA variable that either explicitly or implicitly updates the variable.
|
||||
*/
|
||||
class SsaUpdate extends SsaVariable instanceof WriteDefinition {
|
||||
SsaUpdate() { not this instanceof SsaImplicitInit }
|
||||
}
|
||||
|
||||
/** An SSA variable that is defined by a `VariableUpdate`. */
|
||||
/**
|
||||
* DEPRECATED: Use `SsaExplicitWrite` instead.
|
||||
*
|
||||
* An SSA variable that is defined by a `VariableUpdate`.
|
||||
*/
|
||||
class SsaExplicitUpdate extends SsaUpdate {
|
||||
private VariableUpdate upd;
|
||||
|
||||
@@ -218,12 +293,14 @@ class SsaExplicitUpdate extends SsaUpdate {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaImplicitWrite` instead.
|
||||
*
|
||||
* An SSA variable that represents any sort of implicit update. This can be a
|
||||
* `Call` that might reach a non-local update of the field, an explicit or
|
||||
* implicit update of the qualifier of the field, or the implicit update that
|
||||
* occurs just prior to a `FieldRead` of an untracked field.
|
||||
*/
|
||||
class SsaImplicitUpdate extends SsaUpdate {
|
||||
deprecated class SsaImplicitUpdate extends SsaUpdate {
|
||||
SsaImplicitUpdate() { not this instanceof SsaExplicitUpdate }
|
||||
|
||||
override string toString() {
|
||||
@@ -246,69 +323,89 @@ class SsaImplicitUpdate extends SsaUpdate {
|
||||
}
|
||||
|
||||
private string getKind() {
|
||||
this instanceof UntrackedDef and result = "untracked"
|
||||
or
|
||||
this.hasExplicitQualifierUpdate() and
|
||||
result = "explicit qualifier"
|
||||
result = "explicit qualifier" // -> SSA qualifier def
|
||||
or
|
||||
if this.hasImplicitQualifierUpdate()
|
||||
then
|
||||
if isNonLocal(this)
|
||||
then result = "nonlocal + nonlocal qualifier"
|
||||
else result = "nonlocal qualifier"
|
||||
then result = "nonlocal + nonlocal qualifier" // -> SSA qualifier def
|
||||
else result = "nonlocal qualifier" // -> SSA qualifier def
|
||||
else (
|
||||
isNonLocal(this) and result = "nonlocal"
|
||||
isNonLocal(this) and result = "nonlocal" // -> SSA call def
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaImplicitCallDefinition.getANonLocalUpdate()` instead.
|
||||
*
|
||||
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
FieldWrite getANonLocalUpdate() {
|
||||
exists(SsaSourceField f, Callable setter |
|
||||
relevantFieldUpdate(setter, f.getField(), result) and
|
||||
defUpdatesNamedField(this, f, setter)
|
||||
)
|
||||
}
|
||||
deprecated FieldWrite getANonLocalUpdate() { result = getANonLocalUpdate(this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaImplicitQualifierDefinition` instead.
|
||||
*
|
||||
* Holds if this ssa variable might change the value to something unknown.
|
||||
*
|
||||
* Examples include updates that might change the value of the qualifier, or
|
||||
* reads from untracked variables, for example those where the field or one
|
||||
* of its qualifiers is volatile.
|
||||
*/
|
||||
predicate assignsUnknownValue() {
|
||||
this instanceof UntrackedDef
|
||||
or
|
||||
deprecated predicate assignsUnknownValue() {
|
||||
this.hasExplicitQualifierUpdate()
|
||||
or
|
||||
this.hasImplicitQualifierUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate isNonLocalImpl(SsaImplicitUpdate su) { exists(su.getANonLocalUpdate()) }
|
||||
|
||||
private predicate isNonLocal(SsaImplicitUpdate su) = forceLocal(isNonLocalImpl/1)(su)
|
||||
private predicate hasQualifierUpdate(SsaImplicitWrite def) {
|
||||
exists(SsaWriteDefinition qdef, BasicBlock bb, int i |
|
||||
qdef.definesAt(def.getSourceVariable().getQualifier(), bb, i) and
|
||||
def.definesAt(_, bb, i) and
|
||||
not qdef instanceof SsaImplicitEntryDefinition
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
private FieldWrite getANonLocalUpdate(SsaImplicitWrite calldef) {
|
||||
exists(SsaSourceField f, Callable setter |
|
||||
relevantFieldUpdate(setter, f.getField(), result) and
|
||||
defUpdatesNamedField(calldef, f, setter)
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate isNonLocalImpl(SsaImplicitWrite calldef) { exists(getANonLocalUpdate(calldef)) }
|
||||
|
||||
private predicate isNonLocal(SsaImplicitWrite calldef) = forceLocal(isNonLocalImpl/1)(calldef)
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaUncertainWrite` instead.
|
||||
*
|
||||
* An SSA variable that represents an uncertain implicit update of the value.
|
||||
* This is a `Call` that might reach a non-local update of the field or one of
|
||||
* its qualifiers.
|
||||
*/
|
||||
class SsaUncertainImplicitUpdate extends SsaImplicitUpdate {
|
||||
deprecated class SsaUncertainImplicitUpdate extends SsaImplicitUpdate {
|
||||
SsaUncertainImplicitUpdate() { ssaUncertainImplicitUpdate(this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getPriorDefinition()` instead.
|
||||
*
|
||||
* 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) }
|
||||
deprecated SsaVariable getPriorDef() { ssaDefReachesUncertainDef(result, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit`, `SsaImplicitEntryDefinition`, or `SsaCapturedDefinition` instead.
|
||||
*
|
||||
* An SSA variable that is defined by its initial value in the callable. This
|
||||
* includes initial values of parameters, fields, and closure variables.
|
||||
*/
|
||||
@@ -321,6 +418,8 @@ class SsaImplicitInit extends SsaVariable instanceof WriteDefinition {
|
||||
predicate captures(SsaVariable capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit::getParameter()` instead.
|
||||
*
|
||||
* Holds if the SSA variable is a parameter defined by its initial value in the callable.
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
@@ -329,12 +428,20 @@ class SsaImplicitInit extends SsaVariable instanceof WriteDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA phi node. */
|
||||
class SsaPhiNode extends SsaVariable instanceof PhiNode {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaPhiDefinition` instead.
|
||||
*
|
||||
* An SSA phi node.
|
||||
*/
|
||||
deprecated class SsaPhiNode extends SsaVariable instanceof PhiNode {
|
||||
override string toString() { result = "SSA phi(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
SsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
/**
|
||||
* DEPRECATED: Use `getAnInput()` instead.
|
||||
*
|
||||
* Gets an input to the phi node defining the SSA variable.
|
||||
*/
|
||||
deprecated SsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
SsaVariable getAnInput() { this.hasInputFromBlock(result, _) }
|
||||
@@ -357,10 +464,10 @@ private class RefTypeCastingExpr extends CastingExpr {
|
||||
*
|
||||
* 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
|
||||
Expr sameValue(SsaDefinition v, VarAccess va) {
|
||||
result = v.getARead() and result = va
|
||||
or
|
||||
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitUpdate).getDefiningExpr()
|
||||
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitWrite).getDefiningExpr()
|
||||
or
|
||||
result.(AssignExpr).getSource() = sameValue(v, va)
|
||||
or
|
||||
|
||||
@@ -12,7 +12,7 @@ module;
|
||||
|
||||
import java as J
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import codeql.typeflow.TypeFlow
|
||||
private import codeql.typeflow.UniversalFlow as UniversalFlow
|
||||
@@ -27,7 +27,7 @@ private RefType boxIfNeeded(J::Type t) {
|
||||
module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
private newtype TFlowNode =
|
||||
TField(Field f) { not f.getType() instanceof PrimitiveType } or
|
||||
TSsa(BaseSsaVariable ssa) { not ssa.getSourceVariable().getType() instanceof PrimitiveType } or
|
||||
TSsa(Base::SsaDefinition ssa) { not ssa.getSourceVariable().getType() instanceof PrimitiveType } or
|
||||
TExpr(Expr e) or
|
||||
TMethod(Method m) { not m.getReturnType() instanceof PrimitiveType }
|
||||
|
||||
@@ -55,7 +55,7 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
Field asField() { this = TField(result) }
|
||||
|
||||
/** Gets the SSA variable corresponding to this node, if any. */
|
||||
BaseSsaVariable asSsa() { this = TSsa(result) }
|
||||
Base::SsaDefinition asSsa() { this = TSsa(result) }
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { this = TExpr(result) }
|
||||
@@ -107,7 +107,7 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
not e.(FieldAccess).getField() = f
|
||||
)
|
||||
or
|
||||
n2.asSsa().(BaseSsaPhiNode).getAnUltimateLocalDefinition() = n1.asSsa()
|
||||
n2.asSsa().(Base::SsaPhiDefinition).getAnUltimateDefinition() = n1.asSsa()
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
n2.asMethod() = ret.getEnclosingCallable() and ret.getResult() = n1.asExpr()
|
||||
@@ -118,14 +118,14 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
exists(Argument arg, Parameter p |
|
||||
privateParamArg(p, arg) and
|
||||
n1.asExpr() = arg and
|
||||
n2.asSsa().(BaseSsaImplicitInit).isParameterDefinition(p) and
|
||||
n2.asSsa().(Base::SsaParameterInit).getParameter() = p and
|
||||
// skip trivial recursion
|
||||
not arg = n2.asSsa().getAUse()
|
||||
not arg = n2.asSsa().getARead()
|
||||
)
|
||||
or
|
||||
n2.asExpr() = n1.asField().getAnAccess()
|
||||
or
|
||||
n2.asExpr() = n1.asSsa().getAUse()
|
||||
n2.asExpr() = n1.asSsa().getARead()
|
||||
or
|
||||
n2.asExpr().(CastingExpr).getExpr() = n1.asExpr() and
|
||||
not n2.asExpr().getType() instanceof PrimitiveType
|
||||
@@ -133,9 +133,9 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
n2.asExpr().(AssignExpr).getSource() = n1.asExpr() and
|
||||
not n2.asExpr().getType() instanceof PrimitiveType
|
||||
or
|
||||
n2.asSsa().(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
n2.asSsa().(Base::SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
n2.asSsa().(BaseSsaImplicitInit).captures(n1.asSsa())
|
||||
n2.asSsa().(Base::SsaCapturedDefinition).captures(n1.asSsa())
|
||||
or
|
||||
n2.asExpr().(NotNullExpr).getExpr() = n1.asExpr()
|
||||
}
|
||||
@@ -147,7 +147,7 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
n.asExpr() instanceof NullLiteral
|
||||
or
|
||||
exists(LocalVariableDeclExpr decl |
|
||||
n.asSsa().(BaseSsaUpdate).getDefiningExpr() = decl and
|
||||
n.asSsa().(Base::SsaExplicitWrite).getDefiningExpr() = decl and
|
||||
not decl.hasImplicitInit() and
|
||||
not exists(decl.getInitOrPatternSource())
|
||||
)
|
||||
@@ -216,7 +216,9 @@ private module Input implements TypeFlowInput<Location> {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate upcastEnhancedForStmtAux(BaseSsaUpdate v, RefType t, RefType t1, RefType t2) {
|
||||
private predicate upcastEnhancedForStmtAux(
|
||||
Base::SsaExplicitWrite v, RefType t, RefType t1, RefType t2
|
||||
) {
|
||||
exists(EnhancedForStmt for |
|
||||
for.getVariable() = v.getDefiningExpr() and
|
||||
v.getSourceVariable().getType().getErasure() = t2 and
|
||||
@@ -230,7 +232,7 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* the type of the elements being iterated over, and this type is more precise
|
||||
* than the type of `v`.
|
||||
*/
|
||||
private predicate upcastEnhancedForStmt(BaseSsaUpdate v, RefType t) {
|
||||
private predicate upcastEnhancedForStmt(Base::SsaExplicitWrite v, RefType t) {
|
||||
exists(RefType t1, RefType t2 |
|
||||
upcastEnhancedForStmtAux(v, t, t1, t2) and
|
||||
t1.getASourceSupertype+() = t2
|
||||
@@ -238,9 +240,9 @@ private module Input implements TypeFlowInput<Location> {
|
||||
}
|
||||
|
||||
private predicate downcastSuccessorAux(
|
||||
CastingExpr cast, BaseSsaVariable v, RefType t, RefType t1, RefType t2
|
||||
CastingExpr cast, Base::SsaDefinition v, RefType t, RefType t1, RefType t2
|
||||
) {
|
||||
cast.getExpr() = v.getAUse() and
|
||||
cast.getExpr() = v.getARead() and
|
||||
t = cast.getType() and
|
||||
t1 = t.getErasure() and
|
||||
t2 = v.getSourceVariable().getType().getErasure()
|
||||
@@ -250,10 +252,10 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `va` is an access to a value that has previously been downcast to `t`.
|
||||
*/
|
||||
private predicate downcastSuccessor(VarAccess va, RefType t) {
|
||||
exists(CastingExpr cast, BaseSsaVariable v, RefType t1, RefType t2 |
|
||||
exists(CastingExpr cast, Base::SsaDefinition v, RefType t1, RefType t2 |
|
||||
downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and
|
||||
t1.getASourceSupertype+() = t2 and
|
||||
va = v.getAUse() and
|
||||
va = v.getARead() and
|
||||
dominates(cast.getControlFlowNode(), va.getControlFlowNode()) and
|
||||
dominates(cast.getControlFlowNode().getANormalSuccessor(), va.getControlFlowNode())
|
||||
)
|
||||
@@ -263,9 +265,9 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `va` is an access to a value that is guarded by `instanceof t` or `case e t`.
|
||||
*/
|
||||
private predicate typeTestGuarded(VarAccess va, RefType t) {
|
||||
exists(Guard typeTest, BaseSsaVariable v |
|
||||
typeTest.appliesTypeTest(v.getAUse(), t, _) and
|
||||
va = v.getAUse() and
|
||||
exists(Guard typeTest, Base::SsaDefinition v |
|
||||
typeTest.appliesTypeTest(v.getARead(), t, _) and
|
||||
va = v.getARead() and
|
||||
guardControls_v1(typeTest, va.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
@@ -274,12 +276,12 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `aa` is an access to a value that is guarded by `instanceof t` or `case e t`.
|
||||
*/
|
||||
private predicate arrayTypeTestGuarded(ArrayAccess aa, RefType t) {
|
||||
exists(Guard typeTest, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
|
||||
exists(Guard typeTest, Base::SsaDefinition v1, Base::SsaDefinition v2, ArrayAccess aa1 |
|
||||
typeTest.appliesTypeTest(aa1, t, _) and
|
||||
aa1.getArray() = v1.getAUse() and
|
||||
aa1.getIndexExpr() = v2.getAUse() and
|
||||
aa.getArray() = v1.getAUse() and
|
||||
aa.getIndexExpr() = v2.getAUse() and
|
||||
aa1.getArray() = v1.getARead() and
|
||||
aa1.getIndexExpr() = v2.getARead() and
|
||||
aa.getArray() = v1.getARead() and
|
||||
aa.getIndexExpr() = v2.getARead() and
|
||||
guardControls_v1(typeTest, aa.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
@@ -321,14 +323,14 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `ioe` checks `v`, its true-successor is `bb`, and `bb` has multiple
|
||||
* predecessors.
|
||||
*/
|
||||
private predicate instanceofDisjunct(InstanceOfExpr ioe, BasicBlock bb, BaseSsaVariable v) {
|
||||
ioe.getExpr() = v.getAUse() and
|
||||
private predicate instanceofDisjunct(InstanceOfExpr ioe, BasicBlock bb, Base::SsaDefinition v) {
|
||||
ioe.getExpr() = v.getARead() and
|
||||
strictcount(bb.getAPredecessor()) > 1 and
|
||||
exists(ConditionBlock cb | cb.getCondition() = ioe and cb.getTestSuccessor(true) = bb)
|
||||
}
|
||||
|
||||
/** Holds if `bb` is disjunctively guarded by multiple `instanceof` tests on `v`. */
|
||||
private predicate instanceofDisjunction(BasicBlock bb, BaseSsaVariable v) {
|
||||
private predicate instanceofDisjunction(BasicBlock bb, Base::SsaDefinition v) {
|
||||
strictcount(InstanceOfExpr ioe | instanceofDisjunct(ioe, bb, v)) =
|
||||
strictcount(bb.getAPredecessor())
|
||||
}
|
||||
@@ -338,10 +340,10 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* `instanceof t_i` where `t` is one of those `t_i`.
|
||||
*/
|
||||
predicate instanceofDisjunctionGuarded(TypeFlowNode n, RefType t) {
|
||||
exists(BasicBlock bb, InstanceOfExpr ioe, BaseSsaVariable v, VarAccess va |
|
||||
exists(BasicBlock bb, InstanceOfExpr ioe, Base::SsaDefinition v, VarAccess va |
|
||||
instanceofDisjunction(bb, v) and
|
||||
bb.dominates(va.getBasicBlock()) and
|
||||
va = v.getAUse() and
|
||||
va = v.getARead() and
|
||||
instanceofDisjunct(ioe, bb, v) and
|
||||
t = ioe.getSyntacticCheckedType() and
|
||||
n.asExpr() = va
|
||||
|
||||
@@ -25,7 +25,8 @@ private module BaseSsaStage {
|
||||
predicate backref() {
|
||||
(exists(TLocalVar(_, _)) implies any()) and
|
||||
(exists(any(BaseSsaSourceVariable v).getAnAccess()) implies any()) and
|
||||
(exists(getAUse(_)) implies any())
|
||||
(exists(any(SsaDefinition def).getARead()) implies any()) and
|
||||
(captures(_, _) implies any())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ private module BaseSsaImpl {
|
||||
|
||||
private import BaseSsaImpl
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
private module SsaImplInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
class SourceVariable = BaseSsaSourceVariable;
|
||||
|
||||
/**
|
||||
@@ -169,7 +170,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
certain = true
|
||||
or
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0 and
|
||||
i = -1 and
|
||||
certain = true
|
||||
}
|
||||
|
||||
@@ -189,67 +190,46 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
}
|
||||
}
|
||||
|
||||
private module Impl = SsaImplCommon::Make<Location, Cfg, SsaInput>;
|
||||
private module Impl = SsaImplCommon::Make<Location, Cfg, SsaImplInput>;
|
||||
|
||||
private module SsaInput implements Impl::SsaInputSig {
|
||||
private import java as J
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class Parameter = J::Parameter;
|
||||
|
||||
class VariableWrite = J::VariableWrite;
|
||||
|
||||
predicate explicitWrite(VariableWrite w, BasicBlock bb, int i, BaseSsaSourceVariable v) {
|
||||
variableUpdate(v, w.asExpr().getControlFlowNode(), bb, i)
|
||||
or
|
||||
exists(Parameter p, Callable c |
|
||||
c = p.getCallable() and
|
||||
v = TLocalVar(c, p) and
|
||||
w.isParameterInit(p) and
|
||||
c.getBody().getBasicBlock() = bb and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Ssa = Impl::MakeSsa<SsaInput>;
|
||||
|
||||
import Ssa
|
||||
private import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
VarRead getAUse(Impl::Definition def) {
|
||||
BaseSsaStage::ref() and
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
result.getControlFlowNode() = bb.getNode(i) and
|
||||
result = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Impl::Definition def) {
|
||||
Impl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate firstUse(Impl::Definition def, VarRead use) {
|
||||
exists(BasicBlock bb, int i |
|
||||
Impl::firstUse(def, bb, i, _) and
|
||||
use.getControlFlowNode() = bb.getNode(i)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaUpdate(Impl::Definition def, VariableUpdate upd) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
variableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = v
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaImplicitInit(Impl::WriteDefinition def) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `init` is a closure variable that captures the value of `capturedvar`. */
|
||||
cached
|
||||
predicate captures(BaseSsaImplicitInit init, BaseSsaVariable capturedvar) {
|
||||
predicate captures(SsaImplicitEntryDefinition init, SsaDefinition capturedvar) {
|
||||
exists(BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(_, capturedvar, bb, i) and
|
||||
Ssa::ssaDefReachesUncertainRead(_, capturedvar, bb, i) and
|
||||
variableCapture(capturedvar.getSourceVariable(), init.getSourceVariable(), bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate phiHasInputFromBlock(Impl::PhiNode phi, Impl::Definition inp, BasicBlock bb) {
|
||||
Impl::phiHasInputFromBlock(phi, inp, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
module SsaPublic {
|
||||
/**
|
||||
@@ -285,36 +265,73 @@ private module Cached {
|
||||
|
||||
import SsaPublic
|
||||
|
||||
/**
|
||||
* An SSA variable.
|
||||
*/
|
||||
class BaseSsaVariable extends Impl::Definition {
|
||||
/** Gets the `ControlFlowNode` at which this SSA variable is defined. */
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(BasicBlock bb, int i | this.definesAt(_, bb, i) and result = bb.getNode(0.maximum(i)))
|
||||
}
|
||||
/** An SSA definition in a closure that captures a variable. */
|
||||
class SsaCapturedDefinition extends SsaImplicitEntryDefinition {
|
||||
SsaCapturedDefinition() { captures(this, _) }
|
||||
|
||||
/** Gets an access of this SSA variable. */
|
||||
VarRead getAUse() { result = getAUse(this) }
|
||||
override string toString() { result = "SSA capture def(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/** Holds if this definition captures the value of `capturedvar`. */
|
||||
predicate captures(SsaDefinition capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* Gets an access of the SSA source variable underlying this SSA variable
|
||||
* that can be reached from this SSA variable without passing through any
|
||||
* other uses, but potentially through phi nodes.
|
||||
*
|
||||
* Subsequent uses can be found by following the steps defined by
|
||||
* `baseSsaAdjacentUseUse`.
|
||||
* Gets a definition that ultimately defines the captured variable and is not itself a phi node.
|
||||
*/
|
||||
VarRead getAFirstUse() { firstUse(this, result) }
|
||||
SsaDefinition getAnUltimateCapturedDefinition() {
|
||||
exists(SsaDefinition capturedvar |
|
||||
captures(this, capturedvar) and result = capturedvar.getAnUltimateDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private predicate ssaUpdate(Impl::Definition def, VariableUpdate upd) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
variableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = v
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate ssaImplicitInit(Impl::WriteDefinition def) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaDefinition` instead.
|
||||
*
|
||||
* An SSA variable.
|
||||
*/
|
||||
deprecated class BaseSsaVariable extends Impl::Definition {
|
||||
/**
|
||||
* DEPRECATED: Use `getControlFlowNode()` instead.
|
||||
*
|
||||
* Gets the `ControlFlowNode` at which this SSA variable is defined.
|
||||
*/
|
||||
deprecated ControlFlowNode getCfgNode() { result = this.(SsaDefinition).getControlFlowNode() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getARead()` instead.
|
||||
*
|
||||
* Gets an access of this SSA variable.
|
||||
*/
|
||||
deprecated VarRead getAUse() { result = this.(SsaDefinition).getARead() }
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(b, this) }
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { this.(SsaDefinition).isLiveAtEndOfBlock(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).getAnInput() }
|
||||
|
||||
/** Gets a definition in the same callable that ultimately defines this variable and is not itself a phi node. */
|
||||
BaseSsaVariable getAnUltimateLocalDefinition() {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaDefinition::getAnUltimateDefinition()` instead.
|
||||
*
|
||||
* Gets a definition in the same callable that ultimately defines this variable and is not itself a phi node.
|
||||
*/
|
||||
deprecated BaseSsaVariable getAnUltimateLocalDefinition() {
|
||||
result = this.getAPhiInput*() and not result instanceof BaseSsaPhiNode
|
||||
}
|
||||
|
||||
@@ -324,18 +341,27 @@ class BaseSsaVariable extends Impl::Definition {
|
||||
* variable.
|
||||
*/
|
||||
private BaseSsaVariable getAPhiInputOrCapturedVar() {
|
||||
result = this.(BaseSsaPhiNode).getAPhiInput() or
|
||||
result = this.(BaseSsaPhiNode).getAnInput() or
|
||||
this.(BaseSsaImplicitInit).captures(result)
|
||||
}
|
||||
|
||||
/** Gets a definition that ultimately defines this variable and is not itself a phi node. */
|
||||
BaseSsaVariable getAnUltimateDefinition() {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaCapturedDefinition::getAnUltimateCapturedDefinition()`
|
||||
* and/or `SsaDefinition::getAnUltimateDefinition()` instead.
|
||||
*
|
||||
* Gets a definition that ultimately defines this variable and is not itself a phi node.
|
||||
*/
|
||||
deprecated BaseSsaVariable getAnUltimateDefinition() {
|
||||
result = this.getAPhiInputOrCapturedVar*() and not result instanceof BaseSsaPhiNode
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA variable that is defined by a `VariableUpdate`. */
|
||||
class BaseSsaUpdate extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaExplicitWrite` instead.
|
||||
*
|
||||
* An SSA variable that is defined by a `VariableUpdate`.
|
||||
*/
|
||||
deprecated class BaseSsaUpdate extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
BaseSsaUpdate() { ssaUpdate(this, _) }
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
@@ -343,34 +369,46 @@ class BaseSsaUpdate extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit` or `SsaCapturedDefinition` instead.
|
||||
*
|
||||
* An SSA variable that is defined by its initial value in the callable. This
|
||||
* includes initial values of parameters, fields, and closure variables.
|
||||
*/
|
||||
class BaseSsaImplicitInit extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
deprecated class BaseSsaImplicitInit extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
BaseSsaImplicitInit() { ssaImplicitInit(this) }
|
||||
|
||||
/** Holds if this is a closure variable that captures the value of `capturedvar`. */
|
||||
predicate captures(BaseSsaVariable capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit::getParameter()` instead.
|
||||
*
|
||||
* Holds if the SSA variable is a parameter defined by its initial value in the callable.
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
deprecated predicate isParameterDefinition(Parameter p) {
|
||||
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
|
||||
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA phi node. */
|
||||
class BaseSsaPhiNode extends BaseSsaVariable instanceof Impl::PhiNode {
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
BaseSsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
/**
|
||||
* DEPRECATED: Use `SsaPhiDefinition` instead.
|
||||
*
|
||||
* An SSA phi node.
|
||||
*/
|
||||
deprecated class BaseSsaPhiNode extends BaseSsaVariable instanceof Impl::PhiNode {
|
||||
/**
|
||||
* DEPRECATED: Use `getAnInput()` instead.
|
||||
*
|
||||
* Gets an input to the phi node defining the SSA variable.
|
||||
*/
|
||||
deprecated BaseSsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
BaseSsaVariable getAnInput() { this.hasInputFromBlock(result, _) }
|
||||
|
||||
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
|
||||
predicate hasInputFromBlock(BaseSsaVariable inp, BasicBlock bb) {
|
||||
phiHasInputFromBlock(this, inp, bb)
|
||||
this.(SsaPhiDefinition).hasInputFromBlock(inp, bb)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,12 +460,12 @@ predicate arrayStoreStep(Node node1, Node node2) {
|
||||
}
|
||||
|
||||
private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType) {
|
||||
exists(EnhancedForStmt for, Expr e, SsaExplicitUpdate v |
|
||||
exists(EnhancedForStmt for, Expr e, SsaExplicitWrite v |
|
||||
for.getExpr() = e and
|
||||
node1.asExpr() = e and
|
||||
containerType = e.getType() and
|
||||
v.getDefiningExpr() = for.getVariable() and
|
||||
v.getAFirstUse() = node2.asExpr()
|
||||
ssaGetAFirstUse(v) = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ private predicate deadcode(Expr e) {
|
||||
module SsaFlow {
|
||||
module Impl = SsaImpl::DataFlowIntegration;
|
||||
|
||||
private predicate ssaDefAssigns(SsaExplicitUpdate def, Expr value) {
|
||||
private predicate ssaDefAssigns(SsaExplicitWrite def, Expr value) {
|
||||
exists(VariableUpdate upd | upd = def.getDefiningExpr() |
|
||||
value = upd.(VariableAssign).getSource() or
|
||||
value = upd.(AssignOp) or
|
||||
@@ -46,7 +46,7 @@ module SsaFlow {
|
||||
or
|
||||
exists(Parameter p |
|
||||
n = TExplicitParameterNode(p) and
|
||||
result.(Impl::WriteDefSourceNode).getDefinition().(SsaImplicitInit).isParameterDefinition(p)
|
||||
result.(Impl::WriteDefSourceNode).getDefinition().(SsaParameterInit).getParameter() = p
|
||||
)
|
||||
or
|
||||
ssaDefAssigns(result.(Impl::WriteDefSourceNode).getDefinition(), n.asExpr())
|
||||
|
||||
@@ -62,10 +62,10 @@ private predicate fieldStep(Node node1, Node node2) {
|
||||
private predicate closureFlowStep(Expr e1, Expr e2) {
|
||||
simpleAstFlowStep(e1, e2)
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = e2 and
|
||||
v.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() =
|
||||
e1
|
||||
exists(SsaDefinition v, SsaExplicitWrite def | v.getARead() = e2 and def.getValue() = e1 |
|
||||
v.getAnUltimateDefinition() = def
|
||||
or
|
||||
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def
|
||||
)
|
||||
}
|
||||
|
||||
@@ -395,13 +395,13 @@ class CastNode extends ExprNode {
|
||||
CastNode() {
|
||||
this.getExpr() instanceof CastingExpr
|
||||
or
|
||||
exists(SsaExplicitUpdate upd |
|
||||
exists(SsaExplicitWrite upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() =
|
||||
[
|
||||
any(SwitchStmt ss).getExpr(), any(SwitchExpr se).getExpr(),
|
||||
any(InstanceOfExpr ioe).getExpr()
|
||||
] and
|
||||
this.asExpr() = upd.getAFirstUse()
|
||||
this.asExpr() = ssaGetAFirstUse(upd)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -531,9 +531,9 @@ class NodeRegion instanceof BasicBlock {
|
||||
private predicate constantBooleanExpr(Expr e, boolean val) {
|
||||
e.(CompileTimeConstantExpr).getBooleanValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantBooleanExpr(src, val)
|
||||
)
|
||||
}
|
||||
@@ -551,15 +551,15 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode {
|
||||
*/
|
||||
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) {
|
||||
exists(
|
||||
ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaImplicitInit param,
|
||||
ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaParameterInit param,
|
||||
Guard guard
|
||||
|
|
||||
// get constant bool argument and parameter for this call
|
||||
viableParamArg(call, pragma[only_bind_into](paramNode), arg) and
|
||||
// get the ssa variable definition for this parameter
|
||||
param.isParameterDefinition(paramNode.getParameter()) and
|
||||
param.getParameter() = paramNode.getParameter() and
|
||||
// which is used in a guard
|
||||
param.getAUse() = guard and
|
||||
param.getARead() = guard and
|
||||
// which controls `n` with the opposite value of `arg`
|
||||
guard
|
||||
.controls(nr,
|
||||
|
||||
@@ -99,11 +99,12 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
|
||||
* updates.
|
||||
*/
|
||||
predicate hasNonlocalValue(FieldRead fr) {
|
||||
not exists(SsaVariable v | v.getAUse() = fr)
|
||||
not exists(SsaDefinition v | v.getARead() = fr)
|
||||
or
|
||||
exists(SsaVariable v, SsaVariable def | v.getAUse() = fr and def = v.getAnUltimateDefinition() |
|
||||
def instanceof SsaImplicitInit or
|
||||
def instanceof SsaImplicitUpdate
|
||||
exists(SsaDefinition v, SsaDefinition def |
|
||||
v.getARead() = fr and
|
||||
def = v.getAnUltimateDefinition() and
|
||||
def instanceof SsaImplicitWrite
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -82,13 +82,6 @@ private module TrackedVariablesImpl {
|
||||
|
||||
private import TrackedVariablesImpl
|
||||
|
||||
private predicate untrackedFieldWrite(BasicBlock bb, int i, SsaSourceVariable v) {
|
||||
v =
|
||||
any(SsaSourceField nf |
|
||||
bb.getNode(i + 1) = nf.getAnAccess().(FieldRead).getControlFlowNode() and not trackField(nf)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the definition point of a nested class in the parent scope. */
|
||||
private ControlFlowNode parentDef(NestedClass nc) {
|
||||
nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or
|
||||
@@ -171,7 +164,7 @@ private predicate uncertainVariableUpdateImpl(TrackedVar v, ControlFlowNode n, B
|
||||
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) =
|
||||
forceLocal(uncertainVariableUpdateImpl/4)(v, n, b, i)
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
private module SsaImplInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
class SourceVariable = SsaSourceVariable;
|
||||
|
||||
/**
|
||||
@@ -184,11 +177,8 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
certainVariableUpdate(v, _, bb, i) and
|
||||
certain = true
|
||||
or
|
||||
untrackedFieldWrite(bb, i, v) and
|
||||
certain = true
|
||||
or
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0 and
|
||||
i = -1 and
|
||||
certain = true
|
||||
or
|
||||
uncertainVariableUpdate(v, _, bb, i) and
|
||||
@@ -204,7 +194,10 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
hasDominanceInformation(bb) and
|
||||
(
|
||||
exists(VarRead use |
|
||||
v.getAnAccess() = use and bb.getNode(i) = use.getControlFlowNode() and certain = true
|
||||
v instanceof TrackedVar and
|
||||
v.getAnAccess() = use and
|
||||
bb.getNode(i) = use.getControlFlowNode() and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
variableCapture(v, _, bb, i) and
|
||||
@@ -213,7 +206,35 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
}
|
||||
}
|
||||
|
||||
import SsaImplCommon::Make<Location, Cfg, SsaInput> as Impl
|
||||
import SsaImplCommon::Make<Location, Cfg, SsaImplInput> as Impl
|
||||
|
||||
private module SsaInput implements Impl::SsaInputSig {
|
||||
private import java as J
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class Parameter = J::Parameter;
|
||||
|
||||
class VariableWrite = J::VariableWrite;
|
||||
|
||||
predicate explicitWrite(VariableWrite w, BasicBlock bb, int i, SsaSourceVariable v) {
|
||||
exists(VariableUpdate upd |
|
||||
upd = w.asExpr() and
|
||||
certainVariableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = v
|
||||
)
|
||||
or
|
||||
exists(Parameter p, Callable c |
|
||||
c = p.getCallable() and
|
||||
v = TLocalVar(c, p) and
|
||||
w.isParameterInit(p) and
|
||||
c.getBody().getBasicBlock() = bb and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Ssa = Impl::MakeSsa<SsaInput>;
|
||||
|
||||
final class Definition = Impl::Definition;
|
||||
|
||||
@@ -223,14 +244,51 @@ final class UncertainWriteDefinition = Impl::UncertainWriteDefinition;
|
||||
|
||||
final class PhiNode = Impl::PhiNode;
|
||||
|
||||
class UntrackedDef extends Definition {
|
||||
private VarRead read;
|
||||
predicate ssaExplicitUpdate(SsaUpdate def, VariableUpdate upd) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
certainVariableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = def.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
UntrackedDef() { ssaUntrackedDef(this, read) }
|
||||
deprecated predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
uncertainVariableUpdate(v, _, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
string toString() { result = read.toString() }
|
||||
predicate ssaImplicitInit(WriteDefinition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
|
||||
Location getLocation() { result = read.getLocation() }
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches `redef` without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
deprecated predicate ssaDefReachesUncertainDef(TrackedSsaDef def, SsaUncertainImplicitUpdate redef) {
|
||||
Impl::uncertainWriteDefinitionInput(redef, def)
|
||||
}
|
||||
|
||||
VarRead getAUse(Definition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
result.getControlFlowNode() = bb.getNode(i) and
|
||||
result = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def) {
|
||||
Impl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
deprecated predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
Impl::phiHasInputFromBlock(phi, inp, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -247,24 +305,6 @@ private module Cached {
|
||||
result.getAnAccess() = upd.(UnaryAssignExpr).getExpr()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaExplicitUpdate(SsaUpdate def, VariableUpdate upd) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
certainVariableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = def.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaUntrackedDef(Definition def, VarRead read) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
untrackedFieldWrite(bb, i, v) and
|
||||
read.getControlFlowNode() = bb.getNode(i + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* The SSA construction for a field `f` relies on implicit update nodes at
|
||||
* every call site that conceivably could reach an update of the field.
|
||||
@@ -484,46 +524,20 @@ private module Cached {
|
||||
|
||||
overlay[global]
|
||||
cached
|
||||
predicate defUpdatesNamedField(SsaImplicitUpdate def, TrackedField f, Callable setter) {
|
||||
f = def.getSourceVariable() and
|
||||
updatesNamedField0(def.getCfgNode().asCall(), f, setter)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
uncertainVariableUpdate(v, _, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaImplicitInit(WriteDefinition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0
|
||||
)
|
||||
predicate defUpdatesNamedField(SsaImplicitWrite calldef, TrackedField f, Callable setter) {
|
||||
f = calldef.getSourceVariable() and
|
||||
updatesNamedField0(calldef.getControlFlowNode().asCall(), f, setter)
|
||||
}
|
||||
|
||||
/** Holds if `init` is a closure variable that captures the value of `capturedvar`. */
|
||||
cached
|
||||
predicate captures(SsaImplicitInit init, SsaVariable capturedvar) {
|
||||
predicate captures(SsaImplicitEntryDefinition init, SsaDefinition capturedvar) {
|
||||
exists(BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(_, capturedvar, bb, i) and
|
||||
Ssa::ssaDefReachesUncertainRead(_, capturedvar, bb, i) and
|
||||
variableCapture(capturedvar.getSourceVariable(), init.getSourceVariable(), bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches `redef` without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
cached
|
||||
predicate ssaDefReachesUncertainDef(TrackedSsaDef def, SsaUncertainImplicitUpdate redef) {
|
||||
Impl::uncertainWriteDefinitionInput(redef, def)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value defined at `def` can reach `use` without passing through
|
||||
* any other uses, but possibly through phi nodes and uncertain implicit updates.
|
||||
@@ -536,25 +550,6 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
VarRead getAUse(Definition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
result.getControlFlowNode() = bb.getNode(i) and
|
||||
result = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def) {
|
||||
Impl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
Impl::phiHasInputFromBlock(phi, inp, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
module DataFlowIntegration {
|
||||
import DataFlowIntegrationImpl
|
||||
@@ -664,14 +659,12 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
|
||||
}
|
||||
}
|
||||
|
||||
Expr getARead(Definition def) { result = getAUse(def) }
|
||||
Expr getARead(Definition def) { result = def.(SsaDefinition).getARead() }
|
||||
|
||||
predicate ssaDefHasSource(WriteDefinition def) {
|
||||
def instanceof SsaExplicitUpdate or def.(SsaImplicitInit).isParameterDefinition(_)
|
||||
}
|
||||
predicate ssaDefHasSource(WriteDefinition def) { def instanceof SsaExplicitWrite }
|
||||
|
||||
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
|
||||
def instanceof SsaUncertainImplicitUpdate
|
||||
def instanceof SsaUncertainWrite
|
||||
}
|
||||
|
||||
class GuardValue = Guards::GuardValue;
|
||||
|
||||
@@ -8,7 +8,10 @@ private import java as J
|
||||
private import semmle.code.java.dataflow.SSA as Ssa
|
||||
private import semmle.code.java.dataflow.RangeUtils as RU
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
/** Gets a use of this variable. */
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
|
||||
@@ -11,9 +11,11 @@ module Private {
|
||||
|
||||
class BasicBlock = BB::BasicBlock;
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class SsaPhiNode = Ssa::SsaPhiNode;
|
||||
class SsaPhiNode = Ssa::SsaPhiDefinition;
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ module Private {
|
||||
|
||||
class Guard = G::Guards_v2::Guard;
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable = Ssa::SsaDefinition;
|
||||
|
||||
class SsaPhiNode = Ssa::SsaPhiNode;
|
||||
class SsaPhiNode = Ssa::SsaPhiDefinition;
|
||||
|
||||
class VarAccess = J::VarAccess;
|
||||
|
||||
@@ -240,8 +240,8 @@ private module Impl {
|
||||
}
|
||||
|
||||
/** Returns the underlying variable update of the explicit SSA variable `v`. */
|
||||
VariableUpdate getExplicitSsaAssignment(SsaVariable v) {
|
||||
result = v.(SsaExplicitUpdate).getDefiningExpr()
|
||||
VariableUpdate getExplicitSsaAssignment(SsaDefinition v) {
|
||||
result = v.(SsaExplicitWrite).getDefiningExpr()
|
||||
}
|
||||
|
||||
/** Returns the assignment of the variable update `def`. */
|
||||
@@ -267,13 +267,12 @@ private module Impl {
|
||||
}
|
||||
|
||||
/** Gets the variable underlying the implicit SSA variable `v`. */
|
||||
Variable getImplicitSsaDeclaration(SsaVariable v) {
|
||||
result = v.(SsaImplicitUpdate).getSourceVariable().getVariable() or
|
||||
result = v.(SsaImplicitInit).getSourceVariable().getVariable()
|
||||
Variable getImplicitSsaDeclaration(SsaDefinition v) {
|
||||
result = v.(SsaImplicitWrite).getSourceVariable().getVariable()
|
||||
}
|
||||
|
||||
/** Holds if the variable underlying the implicit SSA variable `v` is not a field. */
|
||||
predicate nonFieldImplicitSsaDefinition(SsaImplicitInit v) { v.isParameterDefinition(_) }
|
||||
predicate nonFieldImplicitSsaDefinition(SsaParameterInit v) { any() }
|
||||
|
||||
/** Returned an expression that is assigned to `f`. */
|
||||
Expr getAssignedValueToField(Field f) {
|
||||
@@ -324,7 +323,7 @@ private module Impl {
|
||||
result = e.(CastingExpr).getExpr()
|
||||
}
|
||||
|
||||
Expr getARead(SsaVariable v) { result = v.getAUse() }
|
||||
Expr getARead(SsaDefinition v) { result = v.getARead() }
|
||||
|
||||
Field getField(FieldAccess fa) { result = fa.getField() }
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ private import semmle.code.java.dataflow.SSA as Ssa
|
||||
private import semmle.code.java.controlflow.BasicBlocks as BB
|
||||
private import SsaReadPositionCommon
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable = Ssa::SsaDefinition;
|
||||
|
||||
class SsaPhiNode = Ssa::SsaPhiNode;
|
||||
class SsaPhiNode = Ssa::SsaPhiDefinition;
|
||||
|
||||
class BasicBlock = BB::BasicBlock;
|
||||
|
||||
/** Gets a basic block in which SSA variable `v` is read. */
|
||||
BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getAUse().getBasicBlock() }
|
||||
BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getARead().getBasicBlock() }
|
||||
|
||||
private predicate id(BB::ExprParent x, BB::ExprParent y) { x = y }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.internal.DataFlowUtil as DataFlow
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.InstanceAccess
|
||||
@@ -162,14 +162,28 @@ private module TypeTrackingSteps {
|
||||
storeContents = loadContents
|
||||
}
|
||||
|
||||
predicate simpleLocalSmallStep(Node n1, Node n2) {
|
||||
exists(BaseSsaVariable v, BaseSsaVariable def |
|
||||
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter())
|
||||
/**
|
||||
* Holds if `n` is a read of an SSA variable that is ultimately defined by `def`.
|
||||
*
|
||||
* This includes reads of captured variables even though they are not technically
|
||||
* local steps, but treating them as local is useful for type tracking purposes.
|
||||
*/
|
||||
private predicate readsSsa(Node n, Base::SsaDefinition def) {
|
||||
exists(Base::SsaDefinition v |
|
||||
v.getAnUltimateDefinition() = def or
|
||||
v.(Base::SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def
|
||||
|
|
||||
v.getAnUltimateDefinition() = def and
|
||||
v.getAUse() = n2.asExpr()
|
||||
v.getARead() = n.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate simpleLocalSmallStep(Node n1, Node n2) {
|
||||
exists(Base::SsaDefinition def |
|
||||
def.(Base::SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
def.(Base::SsaParameterInit).getParameter() = n1.asParameter()
|
||||
|
|
||||
readsSsa(n2, def)
|
||||
)
|
||||
or
|
||||
exists(Callable c | n1.(DataFlow::InstanceParameterNode).getCallable() = c |
|
||||
@@ -220,11 +234,10 @@ private module TypeTrackingSteps {
|
||||
n2.asExpr() = get
|
||||
)
|
||||
or
|
||||
exists(EnhancedForStmt for, BaseSsaVariable ssa, BaseSsaVariable def |
|
||||
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
|
||||
exists(EnhancedForStmt for, Base::SsaDefinition def |
|
||||
for.getVariable() = def.(Base::SsaExplicitWrite).getDefiningExpr() and
|
||||
for.getExpr() = v.getAnAccess() and
|
||||
ssa.getAnUltimateDefinition() = def and
|
||||
ssa.getAUse() = n2.asExpr()
|
||||
readsSsa(n2, def)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -259,16 +272,15 @@ private module TypeTrackingSteps {
|
||||
}
|
||||
|
||||
predicate loadStep(Node n1, LocalSourceNode n2, Content f) {
|
||||
exists(BaseSsaVariable v, BaseSsaVariable def |
|
||||
exists(Base::SsaDefinition def |
|
||||
exists(EnhancedForStmt for |
|
||||
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
|
||||
for.getVariable() = def.(Base::SsaExplicitWrite).getDefiningExpr() and
|
||||
for.getExpr() = n1.asExpr() and
|
||||
n1.getType() instanceof Array and
|
||||
f = ContentArray()
|
||||
)
|
||||
|
|
||||
v.getAnUltimateDefinition() = def and
|
||||
v.getAUse() = n2.asExpr()
|
||||
readsSsa(n2, def)
|
||||
)
|
||||
or
|
||||
n2.asExpr().(ArrayAccess).getArray() = n1.asExpr()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
@@ -71,21 +71,24 @@ private predicate callFlowStep(Node n1, Node n2) {
|
||||
* flow, calls, returns, fields, array reads or writes, or container taint steps.
|
||||
*/
|
||||
private predicate step(Node n1, Node n2) {
|
||||
exists(BaseSsaVariable v, BaseSsaVariable def |
|
||||
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
exists(Base::SsaDefinition v, Base::SsaDefinition def |
|
||||
def.(Base::SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter())
|
||||
def.(Base::SsaParameterInit).getParameter() = n1.asParameter()
|
||||
or
|
||||
exists(EnhancedForStmt for |
|
||||
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
|
||||
for.getVariable() = def.(Base::SsaExplicitWrite).getDefiningExpr() and
|
||||
for.getExpr() = n1.asExpr()
|
||||
)
|
||||
|
|
||||
v.getAnUltimateDefinition() = def and
|
||||
v.getAUse() = n2.asExpr()
|
||||
(
|
||||
v.(Base::SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def or
|
||||
v.getAnUltimateDefinition() = def
|
||||
) and
|
||||
v.getARead() = n2.asExpr()
|
||||
)
|
||||
or
|
||||
baseSsaAdjacentUseUse(n1.asExpr(), n2.asExpr())
|
||||
Base::baseSsaAdjacentUseUse(n1.asExpr(), n2.asExpr())
|
||||
or
|
||||
exists(Callable c | n1.(InstanceParameterNode).getCallable() = c |
|
||||
exists(InstanceAccess ia |
|
||||
|
||||
@@ -7,7 +7,7 @@ import java
|
||||
import semmle.code.java.dataflow.TypeFlow
|
||||
private import DispatchFlow as DispatchFlow
|
||||
private import ObjFlow as ObjFlow
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dispatch.internal.Unification
|
||||
|
||||
@@ -194,10 +194,10 @@ private module Dispatch {
|
||||
*/
|
||||
private predicate impossibleDispatchTarget(MethodCall source, Method tgt) {
|
||||
tgt = viableImpl_v1_cand(source) and
|
||||
exists(Guard typeTest, BaseSsaVariable v, Expr q, RefType t |
|
||||
exists(Guard typeTest, Base::SsaDefinition v, Expr q, RefType t |
|
||||
source.getQualifier() = q and
|
||||
v.getAUse() = q and
|
||||
typeTest.appliesTypeTest(v.getAUse(), t, false) and
|
||||
v.getARead() = q and
|
||||
typeTest.appliesTypeTest(v.getARead(), t, false) and
|
||||
guardControls_v1(typeTest, q.getBasicBlock(), false) and
|
||||
tgt.getDeclaringType().getSourceDeclaration().getASourceSupertype*() = t.getErasure()
|
||||
)
|
||||
|
||||
@@ -41,11 +41,13 @@ private class InputStreamWrapperCapturedJumpStep extends AdditionalTaintStep {
|
||||
*/
|
||||
private class InputStreamWrapperCapturedLocalStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(InputStreamRead m, NestedClass wrapper, SsaVariable captured, SsaImplicitInit capturer |
|
||||
exists(
|
||||
InputStreamRead m, NestedClass wrapper, SsaDefinition captured, SsaCapturedDefinition capturer
|
||||
|
|
||||
wrapper.getASourceSupertype+() instanceof TypeInputStream and
|
||||
m.getDeclaringType() = wrapper and
|
||||
capturer.captures(captured) and
|
||||
TaintTracking::localTaint(DataFlow::exprNode(capturer.getAFirstUse()),
|
||||
TaintTracking::localTaint(DataFlow::exprNode(ssaGetAFirstUse(capturer)),
|
||||
any(DataFlow::PostUpdateNode pun |
|
||||
pun.getPreUpdateNode().asExpr() = m.getParameter(0).getAnAccess()
|
||||
)) and
|
||||
@@ -55,9 +57,9 @@ private class InputStreamWrapperCapturedLocalStep extends AdditionalTaintStep {
|
||||
.getASourceSupertype*()
|
||||
.getSourceDeclaration() = wrapper
|
||||
|
|
||||
n1.asExpr() = captured.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource()
|
||||
n1.asExpr() = captured.(SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource()
|
||||
or
|
||||
captured.(SsaImplicitInit).isParameterDefinition(n1.asParameter())
|
||||
captured.(SsaParameterInit).getParameter() = n1.asParameter()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,10 +262,10 @@ private predicate reaches(Expr src, Argument arg) {
|
||||
any(StartComponentMethodCall ma).getIntentArg() = arg and
|
||||
src = arg
|
||||
or
|
||||
exists(Expr mid, BaseSsa::BaseSsaVariable ssa, BaseSsa::BaseSsaUpdate upd |
|
||||
exists(Expr mid, BaseSsa::SsaDefinition ssa, BaseSsa::SsaExplicitWrite upd |
|
||||
reaches(mid, arg) and
|
||||
mid = ssa.getAUse() and
|
||||
upd = ssa.getAnUltimateLocalDefinition() and
|
||||
mid = ssa.getARead() and
|
||||
upd = ssa.getAnUltimateDefinition() and
|
||||
src = upd.getDefiningExpr().(VariableAssign).getSource()
|
||||
)
|
||||
or
|
||||
|
||||
@@ -38,9 +38,13 @@ private predicate isShell(Expr ex) {
|
||||
cmd.regexpMatch(".*(sh|javac?|python[23]?|osascript|cmd)(\\.exe)?$")
|
||||
)
|
||||
or
|
||||
exists(SsaVariable ssa |
|
||||
ex = ssa.getAUse() and
|
||||
isShell(ssa.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr())
|
||||
exists(SsaDefinition ssa, SsaExplicitWrite def |
|
||||
ex = ssa.getARead() and
|
||||
isShell(def.getDefiningExpr())
|
||||
|
|
||||
ssa.getAnUltimateDefinition() = def
|
||||
or
|
||||
ssa.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def
|
||||
)
|
||||
or
|
||||
isShell(ex.(Assignment).getRhs())
|
||||
@@ -61,17 +65,17 @@ private class ListOfStringType extends CollectionType {
|
||||
/**
|
||||
* A variable that could be used as a list of arguments to a command.
|
||||
*/
|
||||
private class CommandArgumentList extends SsaExplicitUpdate {
|
||||
private class CommandArgumentList extends SsaExplicitWrite {
|
||||
CommandArgumentList() {
|
||||
this.getSourceVariable().getType() instanceof ListOfStringType and
|
||||
forex(CollectionMutation ma | ma.getQualifier() = this.getAUse() |
|
||||
forex(CollectionMutation ma | ma.getQualifier() = this.getARead() |
|
||||
ma.getMethod().getName().matches("add%")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a use of the variable for which the list could be empty. */
|
||||
private VarRead getAUseBeforeFirstAdd() {
|
||||
result = this.getAFirstUse()
|
||||
result = ssaGetAFirstUse(this)
|
||||
or
|
||||
exists(VarRead mid |
|
||||
mid = this.getAUseBeforeFirstAdd() and
|
||||
@@ -87,7 +91,7 @@ private class CommandArgumentList extends SsaExplicitUpdate {
|
||||
* Gets an addition to this list, i.e. a call to an `add` or `addAll` method.
|
||||
*/
|
||||
MethodCall getAnAdd() {
|
||||
result.getQualifier() = this.getAUse() and
|
||||
result.getQualifier() = this.getARead() and
|
||||
result.getMethod().getName().matches("add%")
|
||||
}
|
||||
|
||||
@@ -121,10 +125,10 @@ private predicate arrayVarWrite(ArrayAccess acc) { exists(Assignment a | a.getDe
|
||||
/**
|
||||
* A variable that could be an array of arguments to a command.
|
||||
*/
|
||||
private class CommandArgumentArray extends SsaExplicitUpdate {
|
||||
private class CommandArgumentArray extends SsaExplicitWrite {
|
||||
CommandArgumentArray() {
|
||||
this.getSourceVariable().getType() instanceof ArrayOfStringType and
|
||||
forall(ArrayAccess a | a.getArray() = this.getAUse() and arrayVarWrite(a) |
|
||||
forall(ArrayAccess a | a.getArray() = this.getARead() and arrayVarWrite(a) |
|
||||
a.getIndexExpr() instanceof CompileTimeConstantExpr
|
||||
)
|
||||
}
|
||||
@@ -133,7 +137,7 @@ private class CommandArgumentArray extends SsaExplicitUpdate {
|
||||
Expr getAWrite(int index, VarRead use) {
|
||||
exists(Assignment a, ArrayAccess acc |
|
||||
acc.getArray() = use and
|
||||
use = this.getAUse() and
|
||||
use = this.getARead() and
|
||||
index = acc.getIndexExpr().(CompileTimeConstantExpr).getIntValue() and
|
||||
acc = a.getDest() and
|
||||
result = a.getRhs()
|
||||
@@ -150,7 +154,7 @@ private class CommandArgumentArray extends SsaExplicitUpdate {
|
||||
private class CommandArgArrayImmutableFirst extends CommandArgumentArray {
|
||||
CommandArgArrayImmutableFirst() {
|
||||
(exists(this.getAWrite(0)) or exists(firstElementOf(this.getDefiningExpr()))) and
|
||||
forall(VarRead use | exists(this.getAWrite(0, use)) | use = this.getAFirstUse())
|
||||
forall(VarRead use | exists(this.getAWrite(0, use)) | use = ssaGetAFirstUse(this))
|
||||
}
|
||||
|
||||
/** Gets the first element of this array. */
|
||||
@@ -173,7 +177,9 @@ private Expr firstElementOf(Expr arr) {
|
||||
or
|
||||
result = firstElementOf(arr.(LocalVariableDeclExpr).getInit())
|
||||
or
|
||||
exists(CommandArgArrayImmutableFirst caa | arr = caa.getAUse() | result = caa.getFirstElement())
|
||||
exists(CommandArgArrayImmutableFirst caa | arr = caa.getARead() |
|
||||
result = caa.getFirstElement()
|
||||
)
|
||||
or
|
||||
exists(MethodCall ma, Method m |
|
||||
arr = ma and
|
||||
|
||||
@@ -9,7 +9,7 @@ import java
|
||||
private import codeql.typeflow.UniversalFlow as UniversalFlow
|
||||
private import semmle.code.java.Collections
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
@@ -115,7 +115,7 @@ private predicate nodeWithAddition(FlowNode n, Variable v) {
|
||||
n.asField() = v
|
||||
or
|
||||
n.asSsa().getSourceVariable().getVariable() = v and
|
||||
(n.asSsa() instanceof BaseSsaUpdate or n.asSsa().(BaseSsaImplicitInit).isParameterDefinition(_))
|
||||
n.asSsa() instanceof Base::SsaExplicitWrite
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,14 +46,14 @@ class RightShiftOp extends Expr {
|
||||
}
|
||||
|
||||
private predicate boundedRead(VarRead read) {
|
||||
exists(SsaVariable v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
|
||||
read = v.getAUse() and
|
||||
exists(SsaDefinition v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
|
||||
read = v.getARead() and
|
||||
cb.controls(read.getBasicBlock(), testIsTrue) and
|
||||
cb.getCondition() = comp
|
||||
|
|
||||
comp.getLesserOperand() = v.getAUse() and testIsTrue = true
|
||||
comp.getLesserOperand() = v.getARead() and testIsTrue = true
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
|
||||
comp.getGreaterOperand() = v.getARead() and testIsTrue = false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,9 @@ private predicate validationCall(MethodCall ma, VarAccess va) {
|
||||
}
|
||||
|
||||
private predicate validatedAccess(VarAccess va) {
|
||||
exists(SsaVariable v, MethodCall guardcall |
|
||||
va = v.getAUse() and
|
||||
validationCall(guardcall, v.getAUse())
|
||||
exists(SsaDefinition v, MethodCall guardcall |
|
||||
va = v.getARead() and
|
||||
validationCall(guardcall, v.getARead())
|
||||
|
|
||||
guardcall.(Guard).controls(va.getBasicBlock(), _)
|
||||
or
|
||||
|
||||
Reference in New Issue
Block a user