diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 282d90eaeee0..4b03375c69ee 100644 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -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. */ diff --git a/java/ql/lib/semmle/code/java/controlflow/ControlFlowReachability.qll b/java/ql/lib/semmle/code/java/controlflow/ControlFlowReachability.qll index 9fe6b9b0b1df..2b8353a6b078 100644 --- a/java/ql/lib/semmle/code/java/controlflow/ControlFlowReachability.qll +++ b/java/ql/lib/semmle/code/java/controlflow/ControlFlowReachability.qll @@ -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 { private import java as J + import Ssa AstNode getEnclosingAstNode(ControlFlowNode node) { node.getAstNode() = result } @@ -27,23 +28,6 @@ private module ControlFlowInput implements InputSig { 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 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; * 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 diff --git a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll index f65e15d1c61b..ac43590e4dc8 100644 --- a/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll +++ b/java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll @@ -242,17 +242,17 @@ module Sem implements Semantic { 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() } } diff --git a/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll b/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll index efd7bcd80889..f1fb6c0789a5 100644 --- a/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll +++ b/java/ql/lib/semmle/code/java/dataflow/RangeUtils.qll @@ -33,14 +33,14 @@ predicate eqFlowCond = U::eqFlowCond/5; * `SsaPhiNode` in order for the reflexive case of `nonNullSsaFwdStep*(..)` to * have non-`SsaPhiNode` 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) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/SSA.qll b/java/ql/lib/semmle/code/java/dataflow/SSA.qll index 40da9a4e94d1..ead9c6ed5cab 100644 --- a/java/ql/lib/semmle/code/java/dataflow/SSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/SSA.qll @@ -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 { +deprecated 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 | + deprecated ControlFlowNode getCfgNode() { + 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. */ - VarRead getAUse() { result = getAUse(this) } + /** + * DEPRECATED: Use `getARead()` instead. + * + * Gets an access of this SSA variable. + */ + deprecated 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,35 +238,50 @@ 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. */ -class SsaUpdate extends SsaVariable instanceof WriteDefinition { +/** + * DEPRECATED: use `SsaWriteDefinition` instead. + * + * An SSA variable that either explicitly or implicitly updates the variable. + */ +deprecated class SsaUpdate extends SsaVariable instanceof WriteDefinition { SsaUpdate() { not this instanceof SsaImplicitInit } } -/** An SSA variable that is defined by a `VariableUpdate`. */ -class SsaExplicitUpdate extends SsaUpdate { +/** + * DEPRECATED: Use `SsaExplicitWrite` instead. + * + * An SSA variable that is defined by a `VariableUpdate`. + */ +deprecated class SsaExplicitUpdate extends SsaUpdate { private VariableUpdate upd; SsaExplicitUpdate() { ssaExplicitUpdate(this, 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,26 +323,26 @@ 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() { + deprecated FieldWrite getANonLocalUpdate() { exists(SsaSourceField f, Callable setter | relevantFieldUpdate(setter, f.getField(), result) and defUpdatesNamedField(this, f, setter) @@ -273,46 +350,71 @@ class SsaImplicitUpdate extends SsaUpdate { } /** + * 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() } } +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(SsaImplicitUpdate su) { exists(su.getANonLocalUpdate()) } +private predicate isNonLocalImpl(SsaImplicitWrite calldef) { exists(getANonLocalUpdate(calldef)) } -private predicate isNonLocal(SsaImplicitUpdate su) = forceLocal(isNonLocalImpl/1)(su) +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. */ -class SsaImplicitInit extends SsaVariable instanceof WriteDefinition { +deprecated class SsaImplicitInit extends SsaVariable instanceof WriteDefinition { SsaImplicitInit() { ssaImplicitInit(this) } override string toString() { result = "SSA init(" + this.getSourceVariable() + ")" } @@ -321,20 +423,30 @@ 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) { + deprecated predicate isParameterDefinition(Parameter p) { this.getSourceVariable() = TLocalVar(p.getCallable(), p) and p.getCallable().getBody().getControlFlowNode() = this.getCfgNode() } } -/** 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 +469,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 diff --git a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll index 8ce9b1b91202..21cfc54fdf8a 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll @@ -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 { 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 { 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 { 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 { 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 { 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 { 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 { ) } - 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 { * 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 { } 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 { * 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 { * 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 { * 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 { * 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 { * `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 diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll index 5a818d18b850..e373340d7d7c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll @@ -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 { +private module SsaImplInput implements SsaImplCommon::InputSig { class SourceVariable = BaseSsaSourceVariable; /** @@ -169,7 +170,7 @@ private module SsaInput implements SsaImplCommon::InputSig 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 } } -private module Impl = SsaImplCommon::Make; +private module Impl = SsaImplCommon::Make; -private import Cached +private module SsaInput implements Impl::SsaInputSig { + private import java as J -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() - ) - } + class Expr = J::Expr; - cached - predicate ssaDefReachesEndOfBlock(BasicBlock bb, Impl::Definition def) { - Impl::ssaDefReachesEndOfBlock(bb, def, _) - } + class Parameter = J::Parameter; - cached - predicate firstUse(Impl::Definition def, VarRead use) { - exists(BasicBlock bb, int i | - Impl::firstUse(def, bb, i, _) and - use.getControlFlowNode() = bb.getNode(i) - ) - } + class VariableWrite = J::VariableWrite; - 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 + 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 ) } +} - 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 - ) - } +module Ssa = Impl::MakeSsa; + +import Ssa +private import Cached +cached +private module Cached { /** 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 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() + ) + } +} + +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. */ -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))) - } - - /** Gets an access of this SSA variable. */ - VarRead getAUse() { result = getAUse(this) } +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() } /** - * 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. + * DEPRECATED: Use `getARead()` instead. * - * Subsequent uses can be found by following the steps defined by - * `baseSsaAdjacentUseUse`. + * Gets an access of this SSA variable. */ - VarRead getAFirstUse() { firstUse(this, result) } + 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) } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index 5af246424772..1e6156aaa6f6 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -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() ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll index 674c2380a5f5..9786286389c8 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll @@ -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()) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 1721569e45a9..3dcdc1887614 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -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, diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll index 23e9f680c974..00e7d15ee8b8 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -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 ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll index 275a0afafc08..7f90c3c4a26a 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll @@ -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 { +private module SsaImplInput implements SsaImplCommon::InputSig { class SourceVariable = SsaSourceVariable; /** @@ -184,11 +177,8 @@ private module SsaInput implements SsaImplCommon::InputSig 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 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 } } -import SsaImplCommon::Make as Impl +import SsaImplCommon::Make 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(VariableAssign 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; 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; +deprecated 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) + ) +} + +deprecated predicate ssaImplicitInit(WriteDefinition def) { + exists(SsaSourceVariable v, BasicBlock bb, int i | + def.definesAt(v, bb, i) and + hasEntryDef(v, bb) and + i = -1 + ) +} - string toString() { result = read.toString() } +/** + * 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) +} + +deprecated 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() + ) +} + +deprecated predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def) { + Impl::ssaDefReachesEndOfBlock(bb, def, _) +} - Location getLocation() { result = read.getLocation() } +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; diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/BoundSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/BoundSpecific.qll index a1c690b7df4c..3ed92f4c551d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/BoundSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/BoundSpecific.qll @@ -8,7 +8,9 @@ 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 { + Expr getAUse() { result = super.getARead() } +} class Expr = J::Expr; diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll index e124b8f71378..e0968c9cf175 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll @@ -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; diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index 6363b8f7ed35..4a4181604770 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -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() } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll index dbd7736acde4..f826f192dcaf 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll @@ -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 } diff --git a/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll b/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll index bd293eed6b3a..2af5df281070 100644 --- a/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll +++ b/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll @@ -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 } + /** + * 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.getARead() = n.asExpr() + ) + } + predicate simpleLocalSmallStep(Node n1, Node n2) { - exists(BaseSsaVariable v, BaseSsaVariable def | - def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr() + exists(Base::SsaDefinition def | + def.(Base::SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr() or - def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter()) + def.(Base::SsaParameterInit).getParameter() = n1.asParameter() | - v.getAnUltimateDefinition() = def and - v.getAUse() = n2.asExpr() + 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() diff --git a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll index a3da9118acc7..239f4dd0fbc1 100644 --- a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll +++ b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll @@ -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 | diff --git a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll index 78bf1ad0bdc1..0119930caf43 100644 --- a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll +++ b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll @@ -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() ) diff --git a/java/ql/lib/semmle/code/java/frameworks/InputStream.qll b/java/ql/lib/semmle/code/java/frameworks/InputStream.qll index f6097e8c4492..782d46df89d1 100644 --- a/java/ql/lib/semmle/code/java/frameworks/InputStream.qll +++ b/java/ql/lib/semmle/code/java/frameworks/InputStream.qll @@ -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() ) } } diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll index c3b58873d1f0..3df890c95f41 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/Intent.qll @@ -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 diff --git a/java/ql/lib/semmle/code/java/security/CommandArguments.qll b/java/ql/lib/semmle/code/java/security/CommandArguments.qll index f161a83d17b0..1ac42192e040 100644 --- a/java/ql/lib/semmle/code/java/security/CommandArguments.qll +++ b/java/ql/lib/semmle/code/java/security/CommandArguments.qll @@ -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 diff --git a/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll b/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll index 4294ac84f687..c8d52f4191cc 100644 --- a/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll +++ b/java/ql/lib/semmle/code/java/security/ListOfConstantsSanitizer.qll @@ -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 ) } diff --git a/java/ql/lib/semmle/code/java/security/NumericCastTaintedQuery.qll b/java/ql/lib/semmle/code/java/security/NumericCastTaintedQuery.qll index 841ff4f85153..4b2d7709fbd9 100644 --- a/java/ql/lib/semmle/code/java/security/NumericCastTaintedQuery.qll +++ b/java/ql/lib/semmle/code/java/security/NumericCastTaintedQuery.qll @@ -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 ) } diff --git a/java/ql/lib/semmle/code/java/security/Validation.qll b/java/ql/lib/semmle/code/java/security/Validation.qll index 69f57474317f..51b8defb5ec1 100644 --- a/java/ql/lib/semmle/code/java/security/Validation.qll +++ b/java/ql/lib/semmle/code/java/security/Validation.qll @@ -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 diff --git a/java/ql/src/Likely Bugs/Collections/ArrayIndexOutOfBounds.ql b/java/ql/src/Likely Bugs/Collections/ArrayIndexOutOfBounds.ql index 845aae01a3e8..701084ac794c 100644 --- a/java/ql/src/Likely Bugs/Collections/ArrayIndexOutOfBounds.ql +++ b/java/ql/src/Likely Bugs/Collections/ArrayIndexOutOfBounds.ql @@ -19,10 +19,10 @@ import semmle.code.java.dataflow.RangeUtils import semmle.code.java.dataflow.RangeAnalysis pragma[nomagic] -predicate ssaArrayLengthBound(SsaVariable arr, Bound b) { +predicate ssaArrayLengthBound(SsaDefinition arr, Bound b) { exists(FieldAccess len | len.getField() instanceof ArrayLengthField and - len.getQualifier() = arr.getAUse() and + len.getQualifier() = arr.getARead() and b.getExpr() = len ) } @@ -31,9 +31,9 @@ predicate ssaArrayLengthBound(SsaVariable arr, Bound b) { * Holds if the index expression of `aa` is less than or equal to the array length plus `k`. */ predicate boundedArrayAccess(ArrayAccess aa, int k) { - exists(SsaVariable arr, Expr index, Bound b, int delta | + exists(SsaDefinition arr, Expr index, Bound b, int delta | aa.getIndexExpr() = index and - aa.getArray() = arr.getAUse() and + aa.getArray() = arr.getARead() and bounded(index, b, delta, true, _) | ssaArrayLengthBound(arr, b) and diff --git a/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.ql b/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.ql index e60c65953d1e..5c2fd94a9179 100644 --- a/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.ql +++ b/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.ql @@ -127,7 +127,7 @@ Expr overFlowCand() { c.getIntValue() >= 0 ) or - exists(SsaExplicitUpdate x | result = x.getAUse() and x.getDefiningExpr() = overFlowCand()) + exists(SsaExplicitWrite x | result = x.getARead() and x.getDefiningExpr() = overFlowCand()) or result.(AssignExpr).getRhs() = overFlowCand() or @@ -142,27 +142,27 @@ Expr overFlowCand() { predicate positiveOrNegative(Expr e) { positive(e) or negative(e) } /** Gets an expression that equals `v` plus a positive or negative value. */ -Expr increaseOrDecreaseOfVar(SsaVariable v) { +Expr increaseOrDecreaseOfVar(SsaDefinition v) { exists(AssignAddExpr add | result = add and positiveOrNegative(add.getDest()) and - add.getRhs() = v.getAUse() + add.getRhs() = v.getARead() ) or exists(AddExpr add, Expr e | result = add and - add.hasOperands(v.getAUse(), e) and + add.hasOperands(v.getARead(), e) and positiveOrNegative(e) ) or exists(SubExpr sub | result = sub and - sub.getLeftOperand() = v.getAUse() and + sub.getLeftOperand() = v.getARead() and positiveOrNegative(sub.getRightOperand()) ) or - exists(SsaExplicitUpdate x | - result = x.getAUse() and x.getDefiningExpr() = increaseOrDecreaseOfVar(v) + exists(SsaExplicitWrite x | + result = x.getARead() and x.getDefiningExpr() = increaseOrDecreaseOfVar(v) ) or result.(AssignExpr).getRhs() = increaseOrDecreaseOfVar(v) @@ -172,7 +172,7 @@ Expr increaseOrDecreaseOfVar(SsaVariable v) { predicate overFlowTest(ComparisonExpr comp) { ( - exists(SsaVariable v | comp.hasOperands(increaseOrDecreaseOfVar(v), v.getAUse())) + exists(SsaDefinition v | comp.hasOperands(increaseOrDecreaseOfVar(v), v.getARead())) or comp.getLesserOperand() = overFlowCand() and comp.getGreaterOperand().(IntegerLiteral).getIntValue() = 0 @@ -195,9 +195,9 @@ predicate concurrentModificationTest(BinaryExpr test) { */ pragma[nomagic] predicate guardedTest(EqualityTest test, Guard guard, boolean isEq, int i1, int i2) { - exists(SsaVariable v, CompileTimeConstantExpr c1, CompileTimeConstantExpr c2 | - guard.isEquality(v.getAUse(), c1, isEq) and - test.hasOperands(v.getAUse(), c2) and + exists(SsaDefinition v, CompileTimeConstantExpr c1, CompileTimeConstantExpr c2 | + guard.isEquality(v.getARead(), c1, isEq) and + test.hasOperands(v.getARead(), c2) and i1 = c1.getIntValue() and i2 = c2.getIntValue() and v.getSourceVariable().getType() instanceof IntegralType diff --git a/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll b/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll index 2933ae5305e9..787cda448073 100644 --- a/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll +++ b/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.qll @@ -27,14 +27,14 @@ class BoundKind extends string { */ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) { exists( - ConditionBlock cb, SsaVariable v, BinaryExpr cond, boolean condIsTrue, int k1, int k2, + ConditionBlock cb, SsaDefinition v, BinaryExpr cond, boolean condIsTrue, int k1, int k2, CompileTimeConstantExpr c1, CompileTimeConstantExpr c2 | s1.getCondition() = cond and cb.getCondition() = cond and - cond.hasOperands(v.getAUse(), c1) and + cond.hasOperands(v.getARead(), c1) and c1.getIntValue() = k1 and - test.hasOperands(v.getAUse(), c2) and + test.hasOperands(v.getARead(), c2) and c2.getIntValue() = k2 and v.getSourceVariable().getVariable() instanceof LocalScopeVariable and cb.controls(test.getBasicBlock(), condIsTrue) and @@ -49,7 +49,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) { ) or exists(ComparisonExpr comp | comp = cond | - comp.getLesserOperand() = v.getAUse() and + comp.getLesserOperand() = v.getARead() and ( condIsTrue = true and boundKind.isUpper() and @@ -60,7 +60,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) { (if comp.isStrict() then bound = k1 else bound = k1 + 1) ) or - comp.getGreaterOperand() = v.getAUse() and + comp.getGreaterOperand() = v.getARead() and ( condIsTrue = true and boundKind.isLower() and @@ -88,7 +88,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) { ) or exists(ComparisonExpr comp | comp = test | - comp.getLesserOperand() = v.getAUse() and + comp.getLesserOperand() = v.getARead() and ( boundKind.providesLowerBound() and testIsTrue = false and @@ -107,7 +107,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) { ) ) or - comp.getGreaterOperand() = v.getAUse() and + comp.getGreaterOperand() = v.getARead() and ( boundKind.providesLowerBound() and testIsTrue = true and diff --git a/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLocking.qll b/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLocking.qll index ac279049ed17..19c2dbc783e1 100644 --- a/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLocking.qll +++ b/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLocking.qll @@ -8,9 +8,9 @@ import semmle.code.java.dataflow.SSA private Expr getAFieldRead(Field f) { result = f.getAnAccess() or - exists(SsaExplicitUpdate v | v.getSourceVariable().getVariable() instanceof LocalScopeVariable | - result = v.getAUse() and - v.getDefiningExpr().(VariableAssign).getSource() = getAFieldRead(f) + exists(SsaExplicitWrite v | v.getSourceVariable().getVariable() instanceof LocalScopeVariable | + result = v.getARead() and + v.getValue() = getAFieldRead(f) ) or result.(AssignExpr).getSource() = getAFieldRead(f) diff --git a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql index c7d33eff4a99..4a4be748a283 100644 --- a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql +++ b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql @@ -52,10 +52,10 @@ predicate failedLock(LockType t, BasicBlock lockblock, BasicBlock exblock) { ( lock.asExpr() = t.getLockAccess() or - exists(SsaExplicitUpdate lockbool | + exists(SsaExplicitWrite lockbool | // Using the value of `t.getLockAccess()` ensures that it is a `tryLock` call. - lock.asExpr() = lockbool.getAUse() and - lockbool.getDefiningExpr().(VariableAssign).getSource() = t.getLockAccess() + lock.asExpr() = lockbool.getARead() and + lockbool.getValue() = t.getLockAccess() ) ) and ( diff --git a/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql b/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql index e48eb1f5ce65..8f8d04e6eb2d 100644 --- a/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql +++ b/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql @@ -37,11 +37,11 @@ predicate requiresInstanceOf(Expr e, VarAccess va, RefType t) { * `v` is not of type `sup`, which is a supertype of `t`. */ predicate contradictoryTypeCheck(Expr e, Variable v, RefType t, RefType sup, Expr cond) { - exists(SsaVariable ssa | + exists(SsaDefinition ssa | ssa.getSourceVariable().getVariable() = v and - requiresInstanceOf(e, ssa.getAUse(), t) and + requiresInstanceOf(e, ssa.getARead(), t) and sup = t.getAnAncestor() and - instanceOfCheck(cond, ssa.getAUse(), sup) and + instanceOfCheck(cond, ssa.getARead(), sup) and cond.(Guard).controls(e.getBasicBlock(), false) and not t instanceof ErrorType and not sup instanceof ErrorType diff --git a/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql b/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql index d4fbc480e1b9..bf03191bdac6 100644 --- a/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql +++ b/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql @@ -75,9 +75,9 @@ where loopWhileTrue(loop) and loopExitGuard(loop, cond) ) and // None of the ssa variables in `cond` are updated inside the loop. - forex(SsaVariable ssa, VarRead use | ssa.getAUse() = use and use.getParent*() = cond | - not ssa.getCfgNode().getEnclosingStmt().getEnclosingStmt*() = loop or - ssa.getCfgNode().asExpr().getParent*() = loop.(ForStmt).getAnInit() + forex(SsaDefinition ssa, VarRead use | ssa.getARead() = use and use.getParent*() = cond | + not ssa.getControlFlowNode().getEnclosingStmt().getEnclosingStmt*() = loop or + ssa.getControlFlowNode().asExpr().getParent*() = loop.(ForStmt).getAnInit() ) and // And `cond` does not use method calls, field reads, or array reads. not exists(MethodCall ma | ma.getParent*() = cond) and diff --git a/java/ql/src/Violations of Best Practice/Dead Code/DeadLocals.qll b/java/ql/src/Violations of Best Practice/Dead Code/DeadLocals.qll index 5afab52c37e2..f0b1ab85c60c 100644 --- a/java/ql/src/Violations of Best Practice/Dead Code/DeadLocals.qll +++ b/java/ql/src/Violations of Best Practice/Dead Code/DeadLocals.qll @@ -14,7 +14,7 @@ private predicate emptyDecl(LocalVariableDeclExpr decl) { /** A dead variable update. */ predicate deadLocal(VariableUpdate upd) { upd.getDestVar() instanceof LocalScopeVariable and - not exists(SsaExplicitUpdate ssa | upd = ssa.getDefiningExpr()) and + not exists(SsaExplicitWrite ssa | upd = ssa.getDefiningExpr()) and not emptyDecl(upd) and not readImplicitly(upd, _) } diff --git a/java/ql/src/experimental/Security/CWE/CWE-470/LoadClassNoSignatureCheck.ql b/java/ql/src/experimental/Security/CWE/CWE-470/LoadClassNoSignatureCheck.ql index d328b79f5b15..ddf00714f406 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-470/LoadClassNoSignatureCheck.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-470/LoadClassNoSignatureCheck.ql @@ -42,9 +42,9 @@ class CheckSignaturesGuard extends Guard instanceof EqualityTest { } predicate signatureChecked(Expr safe) { - exists(CheckSignaturesGuard g, SsaVariable v | - v.getAUse() = g.getCheckedExpr() and - safe = v.getAUse() and + exists(CheckSignaturesGuard g, SsaDefinition v | + v.getARead() = g.getCheckedExpr() and + safe = v.getARead() and g.controls(safe.getBasicBlock(), g.(EqualityTest).polarity()) ) } diff --git a/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll index aa68a4332910..b86a4c542461 100644 --- a/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll +++ b/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -277,10 +277,7 @@ private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig { predicate sinkModelSanitizer(DataFlow::Node node) { // exclude variable capture jump steps - exists(Ssa::SsaImplicitInit closure | - closure.captures(_) and - node.asExpr() = closure.getAFirstUse() - ) + exists(Ssa::SsaCapturedDefinition closure | node.asExpr() = Ssa::ssaGetAFirstUse(closure)) } predicate apiSource(DataFlow::Node source) { diff --git a/java/ql/test/library-tests/ssa-large/countssa.ql b/java/ql/test/library-tests/ssa-large/countssa.ql index 8d7b5939c5c0..3d07a8dad292 100644 --- a/java/ql/test/library-tests/ssa-large/countssa.ql +++ b/java/ql/test/library-tests/ssa-large/countssa.ql @@ -3,6 +3,6 @@ import semmle.code.java.dataflow.SSA from int uses, int live where - uses = strictcount(SsaVariable ssa, VarRead use | use = ssa.getAUse()) and - live = strictcount(SsaVariable ssa, BasicBlock b | ssa.isLiveAtEndOfBlock(b)) + uses = strictcount(SsaDefinition ssa, VarRead use | use = ssa.getARead()) and + live = strictcount(SsaDefinition ssa, BasicBlock b | ssa.isLiveAtEndOfBlock(b)) select uses, live diff --git a/java/ql/test/library-tests/ssa/captures.ql b/java/ql/test/library-tests/ssa/captures.ql index ae89b91c43a1..1b6217598666 100644 --- a/java/ql/test/library-tests/ssa/captures.ql +++ b/java/ql/test/library-tests/ssa/captures.ql @@ -1,6 +1,6 @@ import java import semmle.code.java.dataflow.SSA -from SsaImplicitInit closure, SsaVariable captured +from SsaCapturedDefinition closure, SsaDefinition captured where closure.captures(captured) select closure, captured diff --git a/java/ql/test/library-tests/ssa/firstUse.ql b/java/ql/test/library-tests/ssa/firstUse.ql index bbb5c2d3e385..d38570744354 100644 --- a/java/ql/test/library-tests/ssa/firstUse.ql +++ b/java/ql/test/library-tests/ssa/firstUse.ql @@ -1,6 +1,6 @@ import java import semmle.code.java.dataflow.SSA -from SsaVariable ssa, VarRead use -where use = ssa.getAFirstUse() +from SsaDefinition ssa, VarRead use +where use = ssaGetAFirstUse(ssa) select ssa, use diff --git a/java/ql/test/library-tests/ssa/ssaDef.ql b/java/ql/test/library-tests/ssa/ssaDef.ql index c487c539e782..a0702d58e0b5 100644 --- a/java/ql/test/library-tests/ssa/ssaDef.ql +++ b/java/ql/test/library-tests/ssa/ssaDef.ql @@ -1,7 +1,7 @@ import java import semmle.code.java.dataflow.SSA -from SsaVariable ssa, SsaSourceVariable v, string s +from SsaDefinition ssa, SsaSourceVariable v, string s where ssa.getSourceVariable() = v and ( @@ -9,4 +9,4 @@ where or not exists(ssa.toString()) and s = "error" ) -select v, ssa.getCfgNode(), s +select v, ssa.getControlFlowNode(), s diff --git a/java/ql/test/library-tests/ssa/ssaPhi.ql b/java/ql/test/library-tests/ssa/ssaPhi.ql index 8aa0942e90a0..c896c26b0156 100644 --- a/java/ql/test/library-tests/ssa/ssaPhi.ql +++ b/java/ql/test/library-tests/ssa/ssaPhi.ql @@ -1,6 +1,6 @@ import java import semmle.code.java.dataflow.SSA -from SsaPhiNode ssa, SsaSourceVariable v, SsaVariable phiInput -where ssa.getAPhiInput() = phiInput and ssa.getSourceVariable() = v -select v, ssa.getCfgNode(), phiInput.getCfgNode() +from SsaPhiDefinition ssa, SsaSourceVariable v, SsaDefinition phiInput +where ssa.getAnInput() = phiInput and ssa.getSourceVariable() = v +select v, ssa.getControlFlowNode(), phiInput.getControlFlowNode() diff --git a/java/ql/test/library-tests/ssa/ssaUse.ql b/java/ql/test/library-tests/ssa/ssaUse.ql index cab6f47c9555..7bcec95cab88 100644 --- a/java/ql/test/library-tests/ssa/ssaUse.ql +++ b/java/ql/test/library-tests/ssa/ssaUse.ql @@ -1,6 +1,6 @@ import java import semmle.code.java.dataflow.SSA -from SsaVariable ssa, SsaSourceVariable v, Expr use -where use = ssa.getAUse() and ssa.getSourceVariable() = v -select v, ssa.getCfgNode(), ssa.toString(), use +from SsaDefinition ssa, SsaSourceVariable v, Expr use +where use = ssa.getARead() and ssa.getSourceVariable() = v +select v, ssa.getControlFlowNode(), ssa.toString(), use diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 5edf51127b8e..26f3d9f97719 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1617,6 +1617,9 @@ module Make< /** A static single assignment (SSA) definition. */ class SsaDefinition extends FinalDefinition { + /** Gets a textual representation of this SSA definition. */ + string toString() { result = super.toString() } + /** * Gets the control flow node of this SSA definition. * @@ -1708,6 +1711,8 @@ module Make< class SsaParameterInit extends SsaExplicitWrite { SsaParameterInit() { parameterInit(this, _) } + override string toString() { result = "SSA param(" + this.getSourceVariable() + ")" } + /** * Gets the parameter that this definition represents. This is equivalent * to `getDefinition().isParameterInit(result)` @@ -1725,6 +1730,8 @@ module Make< */ class SsaImplicitWrite extends SsaWriteDefinition { SsaImplicitWrite() { not explicitWrite(this, _) } + + override string toString() { result = "SSA implicit def(" + this.getSourceVariable() + ")" } } /** @@ -1734,6 +1741,8 @@ module Make< */ class SsaImplicitEntryDefinition extends SsaImplicitWrite { SsaImplicitEntryDefinition() { this.definesAt(_, any(EntryBasicBlock bb), -1) } + + override string toString() { result = "SSA entry def(" + this.getSourceVariable() + ")" } } /** An SSA definition that represents an uncertain variable update. */ @@ -1761,7 +1770,7 @@ module Make< * ``` * a phi definition for `x` is inserted just before the call `puts x`. */ - class SsaPhiDefinition extends SsaDefinition { + class SsaPhiDefinition extends SsaDefinition instanceof PhiNode { /** Holds if `inp` is an input to this phi definition along the edge originating in `bb`. */ predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) { phiHasInputFromBlockCached(this, inp, bb)