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 0f45ae39fced..9d3ffa05269c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll @@ -89,11 +89,6 @@ private ControlFlowNode captureNode(TrackedVar capturedvar, TrackedVar closureva ) } -/** Holds if `VarAccess` `use` of `v` occurs in `b` at index `i`. */ -private predicate variableUse(TrackedVar v, VarRead use, BasicBlock b, int i) { - v.getAnAccess() = use and b.getNode(i) = use -} - /** Holds if the value of `v` is captured in `b` at index `i`. */ private predicate variableCapture(TrackedVar capturedvar, TrackedVar closurevar, BasicBlock b, int i) { b.getNode(i) = captureNode(capturedvar, closurevar) @@ -177,66 +172,6 @@ class UntrackedDef extends Definition { Location getLocation() { result = read.getLocation() } } -pragma[noinline] -private predicate adjacentDefRead( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2, - SsaInput::SourceVariable v -) { - Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and - v = def.getSourceVariable() -} - -pragma[nomagic] -predicate adjacentDefReachesRead( - Definition def, SsaInput::SourceVariable v, SsaInput::BasicBlock bb1, int i1, - SsaInput::BasicBlock bb2, int i2 -) { - adjacentDefRead(def, bb1, i1, bb2, i2, v) and - ( - def.definesAt(v, bb1, i1) - or - SsaInput::variableRead(bb1, i1, v, true) - ) - or - exists(SsaInput::BasicBlock bb3, int i3 | - adjacentDefReachesRead(def, v, bb1, i1, bb3, i3) and - SsaInput::variableRead(bb3, i3, _, false) and - Impl::adjacentDefRead(def, bb3, i3, bb2, i2) - ) -} - -/** Same as `adjacentDefRead`, but skips uncertain reads. */ -pragma[nomagic] -private predicate adjacentDefSkipUncertainReads( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 -) { - exists(SsaInput::SourceVariable v | - adjacentDefReachesRead(def, v, bb1, i1, bb2, i2) and - SsaInput::variableRead(bb2, i2, v, true) - ) -} - -pragma[nomagic] -private predicate adjacentDefReachesUncertainRead( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 -) { - exists(SsaInput::SourceVariable v | - adjacentDefReachesRead(def, v, bb1, i1, bb2, i2) and - SsaInput::variableRead(bb2, i2, v, false) - ) -} - -pragma[nomagic] -predicate lastRefBeforeRedef(Definition def, BasicBlock bb, int i, Definition next) { - Impl::lastRefRedef(def, bb, i, next) and - not SsaInput::variableRead(bb, i, def.getSourceVariable(), false) - or - exists(SsaInput::BasicBlock bb0, int i0 | - Impl::lastRefRedef(def, bb0, i0, next) and - adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) - ) -} - cached private module Cached { cached @@ -556,12 +491,27 @@ private module Cached { ) } + pragma[nomagic] + private predicate captureDefReaches(Definition def, SsaInput::BasicBlock bb2, int i2) { + variableCapture(def.getSourceVariable(), _, _, _) and + exists(SsaInput::BasicBlock bb1, int i1 | + Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and + def.definesAt(_, bb1, i1) + ) + or + exists(SsaInput::BasicBlock bb3, int i3 | + captureDefReaches(def, bb3, i3) and + SsaInput::variableRead(bb3, i3, _, _) and + Impl::adjacentDefRead(def, bb3, i3, bb2, i2) + ) + } + /** Holds if `init` is a closure variable that captures the value of `capturedvar`. */ cached predicate captures(SsaImplicitInit init, SsaVariable capturedvar) { - exists(BasicBlock bb2, int i2 | - adjacentDefReachesRead(capturedvar, _, _, _, bb2, i2) and - variableCapture(capturedvar.getSourceVariable(), init.getSourceVariable(), bb2, i2) + exists(BasicBlock bb, int i | + captureDefReaches(capturedvar, bb, i) and + variableCapture(capturedvar.getSourceVariable(), init.getSourceVariable(), bb, i) ) } @@ -574,16 +524,22 @@ private module Cached { Impl::uncertainWriteDefinitionInput(redef, def) } - /** - * Holds if the value defined at SSA definition `def` can reach a read at `use`, - * without passing through any other read. - */ pragma[nomagic] - private predicate firstUseSameVar(Definition def, VarRead use) { - exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 | - def.definesAt(_, bb1, i1) and - adjacentDefSkipUncertainReads(def, bb1, i1, bb2, i2) and - use = bb2.getNode(i2) + private predicate defReaches(Definition def, DataFlowIntegration::Node node) { + exists(DataFlowIntegration::SsaDefinitionExtNode nodeFrom | + nodeFrom.getDefinitionExt() = def and + DataFlowIntegrationImpl::localFlowStep(_, nodeFrom, node, false) + ) + or + exists(DataFlowIntegration::Node mid | + defReaches(def, mid) and + DataFlowIntegrationImpl::localFlowStep(_, mid, node, _) + | + // flow into phi input node + mid instanceof DataFlowIntegration::SsaInputNode + or + // flow into definition + mid instanceof DataFlowIntegration::SsaDefinitionExtNode ) } @@ -593,14 +549,9 @@ private module Cached { */ cached predicate firstUse(Definition def, VarRead use) { - firstUseSameVar(def, use) - or - exists(Definition redef, BasicBlock b1, int i1 | - redef instanceof SsaUncertainImplicitUpdate or redef instanceof SsaPhiNode - | - lastRefBeforeRedef(def, b1, i1, redef) and - def.definesAt(_, b1, i1) and - firstUse(redef, use) + exists(DataFlowIntegration::ExprNode nodeTo | + nodeTo.getExpr() = use and + defReaches(def, nodeTo) ) } @@ -646,6 +597,31 @@ private module Cached { cached module SsaPublic { + pragma[nomagic] + private predicate useReaches(VarRead use, DataFlowIntegration::Node node, boolean sameVar) { + exists(DataFlowIntegration::ExprNode nodeFrom | + nodeFrom.getExpr() = use and + DataFlowIntegration::localFlowStep(_, nodeFrom, node, true) and + sameVar = true + ) + or + exists(DataFlowIntegration::Node mid, boolean sameVarMid | + useReaches(use, mid, sameVarMid) and + DataFlowIntegration::localFlowStep(_, mid, node, _) + | + // flow into phi input node + mid instanceof DataFlowIntegration::SsaInputNode and + sameVar = false + or + // flow into definition + exists(Impl::DefinitionExt def | + def = mid.(DataFlowIntegration::SsaDefinitionExtNode).getDefinitionExt() + | + if def instanceof Impl::PhiReadNode then sameVar = sameVarMid else sameVar = false + ) + ) + } + /** * Holds if `use1` and `use2` form an adjacent use-use-pair of the same SSA * variable, that is, the value read in `use1` can reach `use2` without passing @@ -653,10 +629,9 @@ private module Cached { */ cached predicate adjacentUseUseSameVar(VarRead use1, VarRead use2) { - exists(TrackedVar v, BasicBlock b1, int i1, BasicBlock b2, int i2 | - adjacentDefSkipUncertainReads(_, b1, i1, b2, i2) and - variableUse(v, use1, b1, i1) and - variableUse(v, use2, b2, i2) + exists(DataFlowIntegration::ExprNode nodeTo | + nodeTo.getExpr() = use2 and + useReaches(use1, nodeTo, true) ) } @@ -668,14 +643,9 @@ private module Cached { */ cached predicate adjacentUseUse(VarRead use1, VarRead use2) { - adjacentUseUseSameVar(use1, use2) - or - exists(TrackedSsaDef def, BasicBlock b1, int i1 | - lastRefBeforeRedef(_, b1, i1, def) and - variableUse(_, use1, b1, i1) and - firstUse(def, use2) - | - def instanceof SsaUncertainImplicitUpdate or def instanceof SsaPhiNode + exists(DataFlowIntegration::ExprNode nodeTo | + nodeTo.getExpr() = use2 and + useReaches(use1, nodeTo, _) ) } }