Merge branch 'main' into java-kotlin-sensitive-logging-substring-barriers

This commit is contained in:
Paul Hodgkinson
2025-11-20 12:24:22 +00:00
committed by GitHub
481 changed files with 23326 additions and 10191 deletions

View File

@@ -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. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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