From 4d0d06dd082c915f819065236429c66ed0b933d4 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 1 Jul 2024 14:28:50 +0200 Subject: [PATCH] wip --- .../ir/dataflow/internal/DataFlowPrivate.qll | 4 +- .../cpp/ir/dataflow/internal/SsaInternals.qll | 4 +- .../dataflow/internal/SsaInternalsCommon.qll | 10 +- csharp/ql/lib/semmle/code/cil/Ssa.qll | 2 +- .../lib/semmle/code/cil/internal/SsaImpl.qll | 5 +- .../csharp/controlflow/internal/PreSsa.qll | 8 +- .../lib/semmle/code/csharp/dataflow/SSA.qll | 2 +- .../code/csharp/dataflow/internal/BaseSSA.qll | 6 +- .../dataflow/internal/DataFlowPrivate.qll | 4 +- .../code/csharp/dataflow/internal/SsaImpl.qll | 10 +- .../dataflow/internal/DataFlowPrivate.qll | 6 + .../dataflow/new/internal/VariableCapture.qll | 4 + ruby/ql/lib/codeql/ruby/dataflow/SSA.qll | 3 - .../dataflow/internal/DataFlowDispatch.qll | 3 +- .../dataflow/internal/DataFlowPrivate.qll | 375 ++++++------- .../ruby/dataflow/internal/DataFlowPublic.qll | 85 +-- .../codeql/ruby/dataflow/internal/SsaImpl.qll | 75 +-- .../internal/TaintTrackingPrivate.qll | 2 +- .../codeql/dataflow/VariableCapture.qll | 23 +- shared/ssa/codeql/ssa/Ssa.qll | 509 ++++++++++++++++++ swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 8 +- .../dataflow/internal/DataFlowPrivate.qll | 7 + 22 files changed, 810 insertions(+), 345 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 4bbb675a9d16a..3b26093ff5def 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -1809,7 +1809,7 @@ module IteratorFlow { * Holds if `(bb, i)` contains a write to an iterator that may have been obtained * by calling `begin` (or related functions) on the variable `v`. */ - predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { certain = false and exists(GetsIteratorCall beginCall, Instruction writeToDeref, IRBlock bbQual, int iQual | isIteratorStoreInstruction(beginCall, writeToDeref) and @@ -1820,7 +1820,7 @@ module IteratorFlow { } /** Holds if `(bb, i)` reads the container variable `v`. */ - predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { Ssa::variableRead(bb, i, v, certain) } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll index 203437a918617..34fdb500139a9 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll @@ -981,7 +981,7 @@ private module SsaInput implements SsaImplCommon::InputSig { * Holds if the `i`'th write in block `bb` writes to the variable `v`. * `certain` is `true` if the write is guaranteed to overwrite the entire variable. */ - predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { DataFlowImplCommon::forceCachingInSameStage() and ( exists(DefImpl def | def.hasIndexInBlock(bb, i, v) | @@ -999,7 +999,7 @@ private module SsaInput implements SsaImplCommon::InputSig { * Holds if the `i`'th read in block `bb` reads to the variable `v`. * `certain` is `true` if the read is guaranteed. For C++, this is always the case. */ - predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { exists(UseImpl use | use.hasIndexInBlock(bb, i, v) | if use.isCertain() then certain = true else certain = false ) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll index 0920e5a386579..65f1c57b80c36 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll @@ -757,13 +757,19 @@ import Cached * between the SSA pruning stage, and the final SSA stage. */ module InputSigCommon { - class BasicBlock = IRBlock; + class BasicBlock extends IRBlock { + ControlFlowNode getNode(int i) { result = this.getInstruction(i) } + + int length() { result = this.getInstructionCount() } + } + + class ControlFlowNode = Instruction; BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class ExitBasicBlock extends IRBlock { + class ExitBasicBlock extends BasicBlock { ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction } } } diff --git a/csharp/ql/lib/semmle/code/cil/Ssa.qll b/csharp/ql/lib/semmle/code/cil/Ssa.qll index c1dcad4fef5ac..4aebad1a602f1 100644 --- a/csharp/ql/lib/semmle/code/cil/Ssa.qll +++ b/csharp/ql/lib/semmle/code/cil/Ssa.qll @@ -35,7 +35,7 @@ deprecated module Ssa { } /** Gets the location of this SSA definition. */ - Location getLocation() { result = this.getVariableUpdate().getLocation() } + override Location getLocation() { result = this.getVariableUpdate().getLocation() } } /** A phi node. */ diff --git a/csharp/ql/lib/semmle/code/cil/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/cil/internal/SsaImpl.qll index 70e77c66ddb77..41acf8ed1f6f2 100644 --- a/csharp/ql/lib/semmle/code/cil/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/cil/internal/SsaImpl.qll @@ -1,14 +1,17 @@ private import cil +private import CIL private import codeql.ssa.Ssa as SsaImplCommon deprecated private module SsaInput implements SsaImplCommon::InputSig { class BasicBlock = CIL::BasicBlock; + class ControlFlowNode = CIL::ControlFlowNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class ExitBasicBlock = CIL::ExitBasicBlock; + class ExitBasicBlock extends BasicBlock, CIL::ExitBasicBlock { } class SourceVariable = CIL::StackVariable; diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll index c7c1de2308b6f..0984d0ac41ba5 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll @@ -80,7 +80,11 @@ module PreSsa { } module SsaInput implements SsaImplCommon::InputSig { - class BasicBlock = PreBasicBlocks::PreBasicBlock; + class BasicBlock extends PreBasicBlocks::PreBasicBlock { + ControlFlowNode getNode(int i) { result = this.getElement(i) } + } + + class ControlFlowNode = ControlFlowElement; BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) } @@ -192,7 +196,7 @@ module PreSsa { SsaImpl::ssaDefReachesEndOfBlock(bb, this, _) } - Location getLocation() { + override Location getLocation() { result = this.getDefinition().getLocation() or exists(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v | diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll index 652be1e53bc0d..37eab10238b80 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll @@ -444,7 +444,7 @@ module Ssa { } /** Gets the location of this SSA definition. */ - Location getLocation() { none() } + override Location getLocation() { none() } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll index d48d0c39d9335..7a20ceb7abd05 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll @@ -47,13 +47,15 @@ module BaseSsa { class BasicBlock = ControlFlow::BasicBlock; + class ControlFlowNode = ControlFlow::Node; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class ExitBasicBlock = ControlFlow::BasicBlocks::ExitBlock; + class ExitBasicBlock extends BasicBlock, ControlFlow::BasicBlocks::ExitBlock { } class SourceVariable = PreSsa::SimpleLocalScopeVariable; @@ -111,7 +113,7 @@ module BaseSsa { not result instanceof PhiNode } - Location getLocation() { + override Location getLocation() { result = this.getDefinition().getLocation() or exists(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v | diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index b32fb5ffb38df..5817af3c13277 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -264,7 +264,7 @@ module VariableCapture { private module CaptureInput implements Shared::InputSig { private import csharp as Cs - private import semmle.code.csharp.controlflow.ControlFlowGraph + private import semmle.code.csharp.controlflow.ControlFlowGraph as Cfg private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks private import TaintTrackingPrivate as TaintTrackingPrivate @@ -272,6 +272,8 @@ module VariableCapture { Callable getEnclosingCallable() { result = super.getCallable() } } + class ControlFlowNode = Cfg::ControlFlow::Node; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll index 2673dcf1e9793..8fd12d90ae7a8 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -10,11 +10,13 @@ private import semmle.code.csharp.controlflow.internal.PreSsa private module SsaInput implements SsaImplCommon::InputSig { class BasicBlock = ControlFlow::BasicBlock; + class ControlFlowNode = ControlFlow::Node; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class ExitBasicBlock = ControlFlow::BasicBlocks::ExitBlock; + class ExitBasicBlock extends BasicBlock, ControlFlow::BasicBlocks::ExitBlock { } class SourceVariable = Ssa::SourceVariable; @@ -24,7 +26,7 @@ private module SsaInput implements SsaImplCommon::InputSig { * * This includes implicit writes via calls. */ - predicate variableWrite(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { + predicate variableWrite(BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { variableWriteDirect(bb, i, v, certain) or variableWriteQualifier(bb, i, v, certain) @@ -38,7 +40,7 @@ private module SsaInput implements SsaImplCommon::InputSig { * * This includes implicit reads via calls. */ - predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { + predicate variableRead(BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { variableReadActual(bb, i, v) and certain = true or @@ -1094,7 +1096,7 @@ class DefinitionExt extends Impl::DefinitionExt { override string toString() { result = this.(Ssa::Definition).toString() } /** Gets the location of this definition. */ - Location getLocation() { result = this.(Ssa::Definition).getLocation() } + override Location getLocation() { result = this.(Ssa::Definition).getLocation() } /** Gets the enclosing callable of this definition. */ Callable getEnclosingCallable() { result = this.(Ssa::Definition).getEnclosingCallable() } 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 dd8d1ee3d7674..a8f12a4da51a5 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -72,11 +72,17 @@ private module CaptureInput implements VariableCapture::InputSig { class BasicBlock instanceof J::BasicBlock { string toString() { result = super.toString() } + ControlFlowNode getNode(int i) { result = super.getNode(i) } + + int length() { result = super.length() } + Callable getEnclosingCallable() { result = super.getEnclosingCallable() } Location getLocation() { result = super.getLocation() } } + class ControlFlowNode = J::ControlFlowNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { bbIDominates(result, bb) } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll b/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll index f1ba86344a493..86dcee2bfe367 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll @@ -24,6 +24,8 @@ private module CaptureInput implements Shared::InputSig { } class BasicBlock extends PY::BasicBlock { + int length() { result = count(int i | exists(this.getNode(i))) } + Callable getEnclosingCallable() { result = this.getScope() } // Note `PY:BasicBlock` does not have a `getLocation`. @@ -34,6 +36,8 @@ private module CaptureInput implements Shared::InputSig { Location getLocation() { result = super.getNode(0).getLocation() } } + class ControlFlowNode = PY::ControlFlowNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll b/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll index 23623e4d6178d..f21dadb7c5abe 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll @@ -176,9 +176,6 @@ module Ssa { override string toString() { result = this.getControlFlowNode().toString() } - /** Gets the location of this SSA definition. */ - Location getLocation() { result = this.getControlFlowNode().getLocation() } - /** Gets the scope of this SSA definition. */ CfgScope getScope() { result = this.getBasicBlock().getScope() } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll index 94a6657c0a248..2e2713b3cc213 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll @@ -1093,8 +1093,7 @@ private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruct singletonMethodOnInstance(_, _, nodeFromPreExpr.getExpr()) ) | - nodeFromPreExpr = - LocalFlow::getParameterDefNode(p.getParameter()).getDefinitionExt().getARead() + nodeFromPreExpr = getParameterDef(p.getParameter()).getARead() or nodeFromPreExpr = p.(SelfParameterNodeImpl).getSelfDefinition().getARead() ) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index 1a2622454ab19..b1821c631efc6 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -72,134 +72,151 @@ CfgNodes::ExprCfgNode getAPostUpdateNodeForArg(Argument arg) { not exists(getALastEvalNode(result)) } -/** An SSA definition into which another SSA definition may flow. */ -class SsaInputDefinitionExt extends SsaImpl::DefinitionExt { - SsaInputDefinitionExt() { - this instanceof Ssa::PhiNode - or - this instanceof SsaImpl::PhiReadNode - } +/** Gets the SSA definition node corresponding to parameter `p`. */ +pragma[nomagic] +SsaImpl::DefinitionExt getParameterDef(NamedParameter p) { + exists(BasicBlock bb, int i | + bb.getNode(i).getAstNode() = p.getDefiningAccess() and + result.definesAt(_, bb, i, _) + ) +} - predicate hasInputFromBlock(SsaImpl::DefinitionExt def, BasicBlock bb, int i, BasicBlock input) { - SsaImpl::lastRefBeforeRedefExt(def, bb, i, input, this) +class NormalParameter extends Parameter { + NormalParameter() { + this instanceof SimpleParameter or + this instanceof OptionalParameter or + this instanceof KeywordParameter or + this instanceof HashSplatParameter or + this instanceof SplatParameter or + this instanceof BlockParameter } } -/** Provides predicates related to local data flow. */ -module LocalFlow { - /** - * Holds if `nodeFrom` is a node for SSA definition `def`, which can reach `next`. - */ - pragma[nomagic] - private predicate localFlowSsaInputFromDef( - SsaDefinitionExtNode nodeFrom, SsaImpl::DefinitionExt def, SsaInputNode nodeTo - ) { - exists(BasicBlock bb, int i, BasicBlock input, SsaInputDefinitionExt next | - next.hasInputFromBlock(def, bb, i, input) and - def = nodeFrom.getDefinitionExt() and - def.definesAt(_, bb, i, _) and - nodeTo = TSsaInputNode(next, input) - ) - } +/** Provides logic related to SSA. */ +module SsaFlow { + module Input implements SsaImpl::Impl::DataFlowIntegrationInputSig { + class Expr extends CfgNodes::ExprCfgNode { + predicate hasCfgNode(SsaImpl::SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) } + } - /** - * Holds if `nodeFrom` is a last read of SSA definition `def`, which - * can reach `nodeTo`. - */ - pragma[nomagic] - predicate localFlowSsaInputFromRead(SsaImpl::DefinitionExt def, Node nodeFrom, SsaInputNode nodeTo) { - exists( - BasicBlock bb, int i, CfgNodes::ExprCfgNode exprFrom, BasicBlock input, - SsaInputDefinitionExt next - | - next.hasInputFromBlock(def, bb, i, input) and - exprFrom = bb.getNode(i) and - exprFrom.getExpr() instanceof VariableReadAccess and - exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode().asExpr()] and - nodeTo = TSsaInputNode(next, input) - ) - } + /** + * Holds if SSA definition `def` assigns `value` to the underlying variable. + * + * This is either a direct assignment, `x = value`, or an assignment via + * simple pattern matching + * + * ```rb + * case value + * in Foo => x then ... + * in y => then ... + * end + * ``` + */ + predicate ssaDefAssigns(SsaImpl::WriteDefinition def, Expr value) { + def.(Ssa::WriteDefinition).assigns(value) + or + exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::AstCfgNode pattern | + case.getValue() = value and + pattern = case.getBranch(_).(CfgNodes::ExprNodes::InClauseCfgNode).getPattern() + | + def.(Ssa::WriteDefinition).getWriteAccess() = + [pattern, pattern.(CfgNodes::ExprNodes::AsPatternCfgNode).getVariableAccess()] + ) + } - /** Gets the SSA definition node corresponding to parameter `p`. */ - pragma[nomagic] - SsaDefinitionExtNode getParameterDefNode(NamedParameter p) { - exists(BasicBlock bb, int i | - bb.getNode(i).getAstNode() = p.getDefiningAccess() and - result.getDefinitionExt().definesAt(_, bb, i, _) - ) - } + private newtype TParameter = + TNormalParameter(NormalParameter p) or + TSelfMethodParameter(MethodBase m) or + TSelfToplevelParameter(Toplevel t) - /** - * Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node. - */ - pragma[nomagic] - predicate localFlowSsaParamInput(ParameterNodeImpl nodeFrom, SsaDefinitionExtNode nodeTo) { - nodeTo = getParameterDefNode(nodeFrom.getParameter()) - or - nodeTo.getDefinitionExt() = nodeFrom.(SelfParameterNodeImpl).getSelfDefinition() - } + // We could also use `class Parameter = ParameterNodeImpl`, which would be simpler, but + // it makes the `TNode` construction unecessarily recursive. + class Parameter extends TParameter { + NormalParameter asParameter() { this = TNormalParameter(result) } - /** - * Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo` - * involving SSA definition `def`. - */ - predicate localSsaFlowStepUseUse(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) { - SsaImpl::adjacentReadPairExt(def, nodeFrom.asExpr(), nodeTo.asExpr()) - } + MethodBase asMethodSelf() { this = TSelfMethodParameter(result) } - /** - * Holds if SSA definition `def` assigns `value` to the underlying variable. - * - * This is either a direct assignment, `x = value`, or an assignment via - * simple pattern matching - * - * ```rb - * case value - * in Foo => x then ... - * in y => then ... - * end - * ``` - */ - predicate ssaDefAssigns(Ssa::WriteDefinition def, CfgNodes::ExprCfgNode value) { - def.assigns(value) - or - exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::AstCfgNode pattern | - case.getValue() = value and - pattern = case.getBranch(_).(CfgNodes::ExprNodes::InClauseCfgNode).getPattern() - | - def.getWriteAccess() = pattern + Toplevel asToplevelSelf() { this = TSelfToplevelParameter(result) } + + ParameterNodeImpl toParameterNode() { + result = TNormalParameterNode(this.asParameter()) + or + result = TSelfMethodParameterNode(this.asMethodSelf()) + or + result = TSelfToplevelParameterNode(this.asToplevelSelf()) + } + + string toString() { + result = + [ + this.asParameter().toString(), this.asMethodSelf().toString(), + this.asToplevelSelf().toString() + ] + } + + Location getLocation() { + result = + [ + this.asParameter().getLocation(), this.asMethodSelf().getLocation(), + this.asToplevelSelf().getLocation() + ] + } + } + + predicate ssaDefInitializesParam(SsaImpl::WriteDefinition def, Parameter p) { + def = getParameterDef(p.asParameter()) or - def.getWriteAccess() = pattern.(CfgNodes::ExprNodes::AsPatternCfgNode).getVariableAccess() - ) + def.(Ssa::SelfDefinition).getSourceVariable().getDeclaringScope() = + [p.asMethodSelf().(Scope), p.asToplevelSelf()] + } } - /** - * Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving - * SSA definition `def`. - */ - pragma[nomagic] - predicate localSsaFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) { - // Flow from assignment into SSA definition - ssaDefAssigns(def, nodeFrom.asExpr()) and - nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def - or - // Flow from SSA definition to first read - def = nodeFrom.(SsaDefinitionExtNode).getDefinitionExt() and - SsaImpl::firstReadExt(def, nodeTo.asExpr()) - or - // Flow from post-update read to next read - localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode(), nodeTo) + module Impl = SsaImpl::Impl::DataFlowIntegration; + + Impl::Node asNode(Node n) { + n = TSsaNode(result) or - // Flow into phi (read) SSA definition node from def - localFlowSsaInputFromDef(nodeFrom, def, nodeTo) + result.(Impl::ExprNode).getExpr() = n.asExpr() or - nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def and - def = nodeFrom.(SsaInputNode).getDefinitionExt() + result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr() or - localFlowSsaParamInput(nodeFrom, nodeTo) and - def = nodeTo.(SsaDefinitionExtNode).getDefinitionExt() + result.(Impl::ParameterNode).getParameter().toParameterNode() = n + } + + predicate localFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) { + Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo)) + } + + module BarrierGuardsInput implements Impl::BarrierGuardsInputSig { + class Guard extends CfgNodes::AstCfgNode { + predicate hasCfgNode(SsaImpl::SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) } + } + + /** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */ + predicate guardControlsBlock(Guard guard, SsaImpl::SsaInput::BasicBlock bb, boolean branch) { + exists(ConditionBlock conditionBlock, SuccessorTypes::ConditionalSuccessor s | + guard = conditionBlock.getLastNode() and + s.getValue() = branch and + conditionBlock.controls(bb, s) + ) + } + + /** Gets an immediate conditional successor of basic block `bb`, if any. */ + SsaImpl::SsaInput::BasicBlock getAConditionalBasicBlockSuccessor( + SsaImpl::SsaInput::BasicBlock bb, boolean branch + ) { + exists(SuccessorTypes::ConditionalSuccessor s | + result = bb.getASuccessor(s) and + s.getValue() = branch + ) + } } + module BarrierGuardsImpl = Impl::BarrierGuards; +} + +/** Provides predicates related to local data flow. */ +module LocalFlow { pragma[nomagic] predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue() @@ -240,7 +257,7 @@ module LocalFlow { p.(KeywordParameter).getDefaultValue() = nodeFrom.asExpr().getExpr() ) or - nodeTo.(BlockArgumentNode).getParameterNode(true) = nodeFrom + nodeTo.(ImplicitBlockArgumentNode).getParameterNode(true) = nodeFrom } predicate flowSummaryLocalStep( @@ -253,7 +270,10 @@ module LocalFlow { } predicate localMustFlowStep(Node node1, Node node2) { - LocalFlow::localFlowSsaParamInput(node1, node2) + exists(SsaFlow::Input::Parameter p | + SsaFlow::Input::ssaDefInitializesParam(node2.(SsaDefinitionExtNode).getDefinitionExt(), p) and + node1 = p.toParameterNode() + ) or exists(SsaImpl::Definition def | def.(Ssa::WriteDefinition).assigns(node1.asExpr()) and @@ -267,7 +287,7 @@ module LocalFlow { or node1.asExpr() = node2.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue() or - node1 = node2.(BlockArgumentNode).getParameterNode(true) + node1 = node2.(ImplicitBlockArgumentNode).getParameterNode(true) or node1 = unique(FlowSummaryNode n1 | @@ -347,13 +367,12 @@ module VariableCapture { or exists(Ssa::Definition def | def.getARead() = e2 and - LocalFlow::ssaDefAssigns(def.getAnUltimateDefinition(), e1) + SsaFlow::Input::ssaDefAssigns(def.getAnUltimateDefinition(), e1) ) } private module CaptureInput implements Shared::InputSig { - private import ruby as R - private import codeql.ruby.controlflow.ControlFlowGraph + private import codeql.ruby.controlflow.ControlFlowGraph as Cfg private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks private import TaintTrackingPrivate as TaintTrackingPrivate @@ -361,6 +380,8 @@ module VariableCapture { Callable getEnclosingCallable() { result = this.getScope() } } + class ControlFlowNode = Cfg::CfgNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } @@ -537,24 +558,15 @@ private module Cached { newtype TNode = TExprNode(CfgNodes::ExprCfgNode n) or TReturningNode(CfgNodes::ReturningCfgNode n) { exists(n.getReturnedValueNode()) } or - TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) or - TSsaInputNode(SsaInputDefinitionExt def, BasicBlock input) { - def.hasInputFromBlock(_, _, _, input) - } or + TSsaNode(SsaFlow::Impl::SsaNode node) or TCapturedVariableNode(VariableCapture::CapturedVariable v) or - TNormalParameterNode(Parameter p) { - p instanceof SimpleParameter or - p instanceof OptionalParameter or - p instanceof KeywordParameter or - p instanceof HashSplatParameter or - p instanceof SplatParameter - } or + TNormalParameterNode(NormalParameter p) or TSelfMethodParameterNode(MethodBase m) or TSelfToplevelParameterNode(Toplevel t) or TLambdaSelfReferenceNode(Callable c) { lambdaCreationExpr(_, _, c) } or - TBlockParameterNode(MethodBase m) or - TBlockArgumentNode(CfgNodes::ExprNodes::CallCfgNode yield) { - yield = any(BlockParameterNode b).getAYieldCall() + TImplicitBlockParameterNode(MethodBase m) { not m.getAParameter() instanceof BlockParameter } or + TImplicitBlockArgumentNode(CfgNodes::ExprNodes::CallCfgNode yield) { + yield = any(ImplicitBlockParameterNode b).getAYieldCall() } or TSynthHashSplatParameterNode(DataFlowCallable c) { isParameterNode(_, c, any(ParameterPosition p | p.isKeyword(_))) @@ -599,7 +611,7 @@ private module Cached { class TSelfParameterNode = TSelfMethodParameterNode or TSelfToplevelParameterNode; class TSourceParameterNode = - TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or + TNormalParameterNode or TImplicitBlockParameterNode or TSelfParameterNode or TSynthHashSplatParameterNode or TSynthSplatParameterNode; cached @@ -619,14 +631,8 @@ private module Cached { or exists(SsaImpl::DefinitionExt def | // captured variables are handled by the shared `VariableCapture` library - not def instanceof VariableCapture::CapturedSsaDefinitionExt - | - LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) - or - LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and - not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) - or - LocalFlow::localFlowSsaInputFromRead(def, nodeFrom, nodeTo) and + not def instanceof VariableCapture::CapturedSsaDefinitionExt and + SsaFlow::localFlowStep(def, nodeFrom, nodeTo) and not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) ) or @@ -642,11 +648,7 @@ private module Cached { predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or - LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo) - or - LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) - or - LocalFlow::localFlowSsaInputFromRead(_, nodeFrom, nodeTo) + SsaFlow::localFlowStep(_, nodeFrom, nodeTo) or // Simple flow through library code is included in the exposed local // step relation, even though flow is technically inter-procedural @@ -660,11 +662,7 @@ private module Cached { predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or - LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo) - or - LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) - or - LocalFlow::localFlowSsaInputFromRead(_, nodeFrom, nodeTo) + SsaFlow::localFlowStep(_, nodeFrom, nodeTo) or VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo) or @@ -674,7 +672,8 @@ private module Cached { /** Holds if `n` wraps an SSA definition without ingoing flow. */ private predicate entrySsaDefinition(SsaDefinitionExtNode n) { - n.getDefinitionExt() = any(SsaImpl::WriteDefinition def | not LocalFlow::ssaDefAssigns(def, _)) + n.getDefinitionExt() = + any(SsaImpl::WriteDefinition def | not SsaFlow::Input::ssaDefAssigns(def, _)) } pragma[nomagic] @@ -714,13 +713,13 @@ private module Cached { // Ensure all entry SSA definitions are local sources, except those that correspond // to parameters (which are themselves local sources) entrySsaDefinition(n) and - not LocalFlow::localFlowSsaParamInput(_, n) + not SsaFlow::Input::ssaDefInitializesParam(n.(SsaDefinitionExtNode).getDefinitionExt(), _) or isStoreTargetNode(n) or TypeTrackingInput::loadStep(_, n, _) or - n instanceof BlockArgumentNode + n instanceof ImplicitBlockArgumentNode } cached @@ -828,8 +827,6 @@ predicate nodeIsHidden(Node n) { or n instanceof SsaInputNode or - n = LocalFlow::getParameterDefNode(_) - or exists(AstNode desug | isDesugarNode(desug) and desug.isSynthesized() and @@ -860,18 +857,30 @@ predicate nodeIsHidden(Node n) { or n instanceof CaptureNode or - n instanceof BlockArgumentNode + n instanceof ImplicitBlockArgumentNode } -/** An SSA definition, viewed as a node in a data flow graph. */ -class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode { +/** An SSA node. */ +abstract class SsaNode extends NodeImpl, TSsaNode { + SsaFlow::Impl::SsaNode node; SsaImpl::DefinitionExt def; - SsaDefinitionExtNode() { this = TSsaDefinitionExtNode(def) } + SsaNode() { + this = TSsaNode(node) and + def = node.getDefinitionExt() + } - /** Gets the underlying SSA definition. */ SsaImpl::DefinitionExt getDefinitionExt() { result = def } + override Location getLocationImpl() { result = node.getLocation() } + + override string toStringImpl() { result = node.toString() } +} + +/** An SSA definition, viewed as a node in a data flow graph. */ +class SsaDefinitionExtNode extends SsaNode { + override SsaFlow::Impl::SsaDefinitionExtNode node; + /** Gets the underlying variable. */ Variable getVariable() { result = def.getSourceVariable() } @@ -880,13 +889,21 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode { not def instanceof Ssa::WriteDefinition or isDesugarNode(def.(Ssa::WriteDefinition).getWriteAccess().getExpr()) + or + def = getParameterDef(_) } override CfgScope getCfgScope() { result = def.getBasicBlock().getScope() } +} - override Location getLocationImpl() { result = def.getLocation() } +class SsaDefinitionNodeImpl extends SsaDefinitionExtNode { + Ssa::Definition ssaDef; - override string toStringImpl() { result = def.toString() } + SsaDefinitionNodeImpl() { ssaDef = def } + + override Location getLocationImpl() { result = ssaDef.getLocation() } + + override string toStringImpl() { result = ssaDef.toString() } } /** @@ -924,20 +941,13 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode { * * both inputs into the phi read node after the outer condition are guarded. */ -class SsaInputNode extends NodeImpl, TSsaInputNode { - SsaImpl::DefinitionExt def; +class SsaInputNode extends SsaNode { + override SsaFlow::Impl::SsaInputNode node; BasicBlock input; - SsaInputNode() { this = TSsaInputNode(def, input) } - - /** Gets the underlying SSA definition. */ - SsaImpl::DefinitionExt getDefinitionExt() { result = def } + SsaInputNode() { node.isInputInto(def, input) } override CfgScope getCfgScope() { result = input.getScope() } - - override Location getLocationImpl() { result = input.getLastNode().getLocation() } - - override string toStringImpl() { result = "[input] " + def } } /** An SSA definition for a `self` variable. */ @@ -1054,6 +1064,9 @@ private module ParameterNodes { // There are no positional parameters after the splat not exists(SimpleParameter p, int m | m > n | p = callable.getParameter(m)) ) + or + parameter = callable.getAParameter().(BlockParameter) and + pos.isBlock() ) } @@ -1161,16 +1174,14 @@ private module ParameterNodes { * The value of a block parameter at function entry, viewed as a node in a data * flow graph. */ - class BlockParameterNode extends ParameterNodeImpl, TBlockParameterNode { + class ImplicitBlockParameterNode extends ParameterNodeImpl, TImplicitBlockParameterNode { private MethodBase method; - BlockParameterNode() { this = TBlockParameterNode(method) } + ImplicitBlockParameterNode() { this = TImplicitBlockParameterNode(method) } final MethodBase getMethod() { result = method } - override Parameter getParameter() { - result = method.getAParameter() and result instanceof BlockParameter - } + override Parameter getParameter() { none() } override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { c.asCfgScope() = method and pos.isBlock() @@ -1451,15 +1462,15 @@ module ArgumentNodes { } } - class BlockArgumentNode extends NodeImpl, ArgumentNode, TBlockArgumentNode { + class ImplicitBlockArgumentNode extends NodeImpl, ArgumentNode, TImplicitBlockArgumentNode { CfgNodes::ExprNodes::CallCfgNode yield; - BlockArgumentNode() { this = TBlockArgumentNode(yield) } + ImplicitBlockArgumentNode() { this = TImplicitBlockArgumentNode(yield) } CfgNodes::ExprNodes::CallCfgNode getYieldCall() { result = yield } pragma[nomagic] - BlockParameterNode getParameterNode(boolean inSameScope) { + ImplicitBlockParameterNode getParameterNode(boolean inSameScope) { result.getAYieldCall() = yield and if nodeGetEnclosingCallable(this) = nodeGetEnclosingCallable(result) then inSameScope = true @@ -1892,7 +1903,7 @@ predicate jumpStep(Node pred, Node succ) { or any(AdditionalJumpStep s).step(pred, succ) or - succ.(BlockArgumentNode).getParameterNode(false) = pred + succ.(ImplicitBlockArgumentNode).getParameterNode(false) = pred } private ContentSet getArrayContent(int n) { @@ -2240,7 +2251,7 @@ private predicate lambdaCallExpr( */ predicate lambdaSourceCall(CfgNodes::ExprNodes::CallCfgNode call, LambdaCallKind kind, Node receiver) { kind = TYieldCallKind() and - call = receiver.(BlockArgumentNode).getYieldCall() + call = receiver.(ImplicitBlockArgumentNode).getYieldCall() or kind = TLambdaCallKind() and lambdaCallExpr(call, receiver.asExpr()) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index d4e07e0653e10..af7e7ec08a752 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -360,16 +360,12 @@ class PostUpdateNode extends Node { } /** An SSA definition, viewed as a node in a data flow graph. */ -class SsaDefinitionNode extends Node instanceof SsaDefinitionExtNode { - Ssa::Definition def; - - SsaDefinitionNode() { this = TSsaDefinitionExtNode(def) } - +class SsaDefinitionNode extends Node instanceof SsaDefinitionNodeImpl { /** Gets the underlying SSA definition. */ - Ssa::Definition getDefinition() { result = def } + Ssa::Definition getDefinition() { result = super.getDefinitionExt() } /** Gets the underlying variable. */ - Variable getVariable() { result = def.getSourceVariable() } + Variable getVariable() { result = this.getDefinition().getSourceVariable() } } cached @@ -381,7 +377,7 @@ private module Cached { } cached - predicate hasYieldCall(BlockParameterNode block, CallNode yield) { + predicate hasYieldCall(ImplicitBlockParameterNode block, CallNode yield) { exists(MethodBase method, YieldCall call | block.getMethod() = method and call.getEnclosingMethod() = method and @@ -872,55 +868,6 @@ private predicate sameSourceVariable(Ssa::Definition def1, Ssa::Definition def2) module BarrierGuard { private import SsaImpl as SsaImpl - pragma[nomagic] - private predicate guardChecksSsaDef(CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def) { - guardChecks(g, def.getARead(), branch) - } - - pragma[nomagic] - private predicate guardControlsSsaRead( - CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def, Node n - ) { - def.getARead() = n.asExpr() and - guardControlsBlock(g, n.asExpr().getBasicBlock(), branch) - } - - pragma[nomagic] - private predicate guardControlsPhiInput( - CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def, BasicBlock input, - SsaInputDefinitionExt phi - ) { - phi.hasInputFromBlock(def, _, _, input) and - ( - guardControlsBlock(g, input, branch) - or - exists(SuccessorTypes::ConditionalSuccessor s | - g = input.getLastNode() and - s.getValue() = branch and - input.getASuccessor(s) = phi.getBasicBlock() - ) - ) - } - - /** Gets a node that is safely guarded by the given guard check. */ - Node getABarrierNode() { - exists(CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def | - guardChecksSsaDef(g, branch, def) and - guardControlsSsaRead(g, branch, def, result) - ) - or - exists( - CfgNodes::AstCfgNode g, boolean branch, Ssa::Definition def, BasicBlock input, - SsaInputDefinitionExt phi - | - guardChecksSsaDef(g, branch, def) and - guardControlsPhiInput(g, branch, def, input, phi) and - result = TSsaInputNode(phi, input) - ) - or - result.asExpr() = getAMaybeGuardedCapturedDef().getARead() - } - /** * Gets an implicit entry definition for a captured variable that * may be guarded, because a call to the capturing callable is guarded. @@ -928,6 +875,7 @@ module BarrierGuard { * This is restricted to calls where the variable is captured inside a * block. */ + pragma[nomagic] private Ssa::CapturedEntryDefinition getAMaybeGuardedCapturedDef() { exists( CfgNodes::ExprCfgNode g, boolean branch, CfgNodes::ExprCfgNode testedNode, @@ -935,20 +883,25 @@ module BarrierGuard { | def.getARead() = testedNode and guardChecks(g, testedNode, branch) and - guardControlsBlock(g, call.getBasicBlock(), branch) and + SsaFlow::BarrierGuardsInput::guardControlsBlock(g, call.getBasicBlock(), branch) and result.getBasicBlock().getScope() = call.getExpr().(MethodCall).getBlock() and sameSourceVariable(def, result) ) } -} -/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */ -private predicate guardControlsBlock(CfgNodes::AstCfgNode guard, BasicBlock bb, boolean branch) { - exists(ConditionBlock conditionBlock, SuccessorTypes::ConditionalSuccessor s | - guard = conditionBlock.getLastNode() and - s.getValue() = branch and - conditionBlock.controls(bb, s) - ) + private predicate guardChecksAdj( + SsaFlow::BarrierGuardsInput::Guard g, SsaFlow::Input::Expr e, boolean branch + ) { + guardChecks(g, e, branch) + } + + /** Gets a node that is safely guarded by the given guard check. */ + Node getABarrierNode() { + SsaFlow::asNode(result) = + SsaFlow::BarrierGuardsImpl::BarrierGuard::getABarrierNode() + or + result.asExpr() = getAMaybeGuardedCapturedDef().getARead() + } } /** diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index 1490a8c7ca332..477110dbe4c6f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -6,16 +6,19 @@ private import codeql.ruby.dataflow.SSA private import codeql.ruby.ast.Variable private import Cfg::CfgNodes::ExprNodes -private module SsaInput implements SsaImplCommon::InputSig { +module SsaInput implements SsaImplCommon::InputSig { + private import codeql.ruby.controlflow.ControlFlowGraph as Cfg private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks class BasicBlock = BasicBlocks::BasicBlock; + class ControlFlowNode = Cfg::CfgNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class ExitBasicBlock = BasicBlocks::ExitBasicBlock; + class ExitBasicBlock extends BasicBlock, BasicBlocks::ExitBasicBlock { } class SourceVariable = LocalVariable; @@ -62,7 +65,7 @@ private module SsaInput implements SsaImplCommon::InputSig { } } -private import SsaImplCommon::Make as Impl +import SsaImplCommon::Make as Impl class Definition = Impl::Definition; @@ -280,15 +283,6 @@ private predicate adjacentDefSkipUncertainReads( SsaInput::variableRead(bb2, i2, _, true) } -/** Same as `adjacentDefReadExt`, but skips uncertain reads. */ -pragma[nomagic] -private predicate adjacentDefSkipUncertainReadsExt( - DefinitionExt def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 -) { - adjacentDefReachesReadExt(def, bb1, i1, bb2, i2) and - SsaInput::variableRead(bb2, i2, _, true) -} - private predicate adjacentDefReachesUncertainReadExt( DefinitionExt def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 ) { @@ -390,19 +384,6 @@ private module Cached { ) } - /** - * Holds if the value defined at SSA definition `def` can reach a read at `read`, - * without passing through any other non-pseudo read. - */ - cached - predicate firstReadExt(DefinitionExt def, VariableReadAccessCfgNode read) { - exists(Cfg::BasicBlock bb1, int i1, Cfg::BasicBlock bb2, int i2 | - def.definesAt(_, bb1, i1, _) and - adjacentDefSkipUncertainReadsExt(def, bb1, i1, bb2, i2) and - read = bb2.getNode(i2) - ) - } - /** * Holds if the read at `read2` is a read of the same SSA definition `def` * as the read at `read1`, and `read2` can be reached from `read1` without @@ -420,23 +401,6 @@ private module Cached { ) } - /** - * Holds if the read at `read2` is a read of the same SSA definition `def` - * as the read at `read1`, and `read2` can be reached from `read1` without - * passing through another non-pseudo read. - */ - cached - predicate adjacentReadPairExt( - DefinitionExt def, VariableReadAccessCfgNode read1, VariableReadAccessCfgNode read2 - ) { - exists(Cfg::BasicBlock bb1, int i1, Cfg::BasicBlock bb2, int i2 | - read1 = bb1.getNode(i1) and - variableReadActual(bb1, i1, _) and - adjacentDefSkipUncertainReadsExt(def, bb1, i1, bb2, i2) and - read2 = bb2.getNode(i2) - ) - } - /** * Holds if the read of `def` at `read` may be a last read. That is, `read` * can either reach another definition of the underlying source variable or @@ -451,28 +415,6 @@ private module Cached { ) } - /** - * Holds if the reference to `def` at index `i` in basic block `bb` can reach - * another definition `next` of the same underlying source variable, without - * passing through another write or non-pseudo read. - * - * The reference is either a read of `def` or `def` itself. - */ - cached - predicate lastRefBeforeRedefExt( - DefinitionExt def, Cfg::BasicBlock bb, int i, Cfg::BasicBlock input, DefinitionExt next - ) { - exists(LocalVariable v | - Impl::lastRefRedefExt(def, v, bb, i, input, next) and - not SsaInput::variableRead(bb, i, v, false) - ) - or - exists(SsaInput::BasicBlock bb0, int i0 | - Impl::lastRefRedefExt(def, _, bb0, i0, input, next) and - adjacentDefReachesUncertainReadExt(def, bb, i, bb0, i0) - ) - } - cached Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) { Impl::uncertainWriteDefinitionInput(def, result) @@ -494,8 +436,7 @@ class DefinitionExt extends Impl::DefinitionExt { override string toString() { result = this.(Ssa::Definition).toString() } - /** Gets the location of this definition. */ - Location getLocation() { result = this.(Ssa::Definition).getLocation() } + override Location getLocation() { result = this.(Ssa::Definition).getLocation() } } /** @@ -506,5 +447,5 @@ class DefinitionExt extends Impl::DefinitionExt { class PhiReadNode extends DefinitionExt, Impl::PhiReadNode { override string toString() { result = "SSA phi read(" + this.getSourceVariable() + ")" } - override Location getLocation() { result = this.getBasicBlock().getLocation() } + override Location getLocation() { result = Impl::PhiReadNode.super.getLocation() } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll index 903c8b562e763..6a17f8b74a9f4 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll @@ -89,7 +89,7 @@ private module Cached { clause = case.getBranch(_) and def = nodeTo.(SsaDefinitionExtNode).getDefinitionExt() and def.getControlFlowNode() = variablesInPattern(clause.getPattern()) and - not LocalFlow::ssaDefAssigns(def, value) + not SsaFlow::Input::ssaDefAssigns(def, value) ) or // operation involving `nodeFrom` diff --git a/shared/dataflow/codeql/dataflow/VariableCapture.qll b/shared/dataflow/codeql/dataflow/VariableCapture.qll index c48b46e8a7ba0..73f22fced7375 100644 --- a/shared/dataflow/codeql/dataflow/VariableCapture.qll +++ b/shared/dataflow/codeql/dataflow/VariableCapture.qll @@ -17,6 +17,12 @@ signature module InputSig { /** Gets a textual representation of this basic block. */ string toString(); + /** Gets the `i`th node in this basic block. */ + ControlFlowNode getNode(int i); + + /** Gets the length of this basic block. */ + int length(); + /** Gets the enclosing callable. */ Callable getEnclosingCallable(); @@ -24,6 +30,15 @@ signature module InputSig { Location getLocation(); } + /** A control flow node. */ + class ControlFlowNode { + /** Gets a textual representation of this control flow node. */ + string toString(); + + /** Gets the location of this control flow node. */ + Location getLocation(); + } + /** * Gets the basic block that immediately dominates basic block `bb`, if any. * @@ -672,6 +687,8 @@ module Flow Input> implements OutputSig private module CaptureSsaInput implements Ssa::InputSig { final class BasicBlock = Input::BasicBlock; + final class ControlFlowNode = Input::ControlFlowNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = Input::getImmediateBasicBlockDominator(bb) } @@ -717,10 +734,10 @@ module Flow Input> implements OutputSig TSynthPhi(CaptureSsa::DefinitionExt phi) { phi instanceof CaptureSsa::PhiNode or phi instanceof CaptureSsa::PhiReadNode } or - TExprNode(Expr expr, boolean isPost) { - expr instanceof VariableRead and isPost = [false, true] + TExprNode(Expr expr, Boolean isPost) { + expr instanceof VariableRead or - synthRead(_, _, _, _, expr) and isPost = [false, true] + synthRead(_, _, _, _, expr) } or TParamNode(CapturedParameter p) or TThisParamNode(Callable c) { captureAccess(_, c) } or diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index ee397375e655e..26d051f10b2e9 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -15,10 +15,25 @@ signature module InputSig { /** Gets a textual representation of this basic block. */ string toString(); + /** Gets the `i`th node in this basic block. */ + ControlFlowNode getNode(int i); + + /** Gets the length of this basic block. */ + int length(); + /** Gets the location of this basic block. */ Location getLocation(); } + /** A control flow node. */ + class ControlFlowNode { + /** Gets a textual representation of this control flow node. */ + string toString(); + + /** Gets the location of this control flow node. */ + Location getLocation(); + } + /** * Gets the basic block that immediately dominates basic block `bb`, if any. * @@ -905,6 +920,13 @@ module Make Input> { /** Gets a textual representation of this SSA definition. */ string toString() { result = "SSA def(" + this.getSourceVariable() + ")" } + + /** Gets the location of this SSA definition. */ + Location getLocation() { + exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | + if i = -1 then result = bb.getLocation() else result = bb.getNode(i).getLocation() + ) + } } /** An SSA definition that corresponds to a write. */ @@ -961,6 +983,13 @@ module Make Input> { /** Gets a textual representation of this SSA definition. */ string toString() { result = this.(Definition).toString() } + + /** Gets the location of this SSA definition. */ + Location getLocation() { + exists(BasicBlock bb, int i | this.definesAt(_, bb, i, _) | + if i = -1 then result = bb.getLocation() else result = bb.getNode(i).getLocation() + ) + } } /** @@ -1134,4 +1163,484 @@ module Make Input> { ) } } + + /** Provides the input to `DataFlowIntegration`. */ + signature module DataFlowIntegrationInputSig { + /** + * An expression with a value. That is, we expect these expressions to be + * represented in the data flow graph. + */ + class Expr { + /** Gets a textual representation of this expression. */ + string toString(); + + /** Holds if the `i`th node of basic block `bb` evaluates this expression. */ + predicate hasCfgNode(BasicBlock bb, int i); + } + + /** Holds is SSA definition `def` assigns `value` to the underlying variable. */ + predicate ssaDefAssigns(WriteDefinition def, Expr value); + + /** A parameter. */ + class Parameter { + /** Gets a textual representation of this parameter. */ + string toString(); + + /** Gets the location of this parameter. */ + Location getLocation(); + } + + /** Holds if SSA definition `def` initializes parameter `p` at function entry. */ + predicate ssaDefInitializesParam(WriteDefinition def, Parameter p); + } + + /** + * Constructs the type `Node` and associated value step relation, which are + * intended to be included in the `DataFlow::Node` type and local step relation. + * + * Additionally, this module also provides a barrier guards implementation. + */ + module DataFlowIntegration { + private import codeql.util.Boolean + + final private class DefinitionExtFinal = DefinitionExt; + + pragma[nomagic] + private predicate adjacentDefReachesReadExt( + DefinitionExt def, SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 + ) { + adjacentDefReadExt(def, v, bb1, i1, bb2, i2) and + ( + def.definesAt(v, bb1, i1, _) + or + variableRead(bb1, i1, v, true) + ) + or + exists(BasicBlock bb3, int i3 | + adjacentDefReachesReadExt(def, v, bb1, i1, bb3, i3) and + variableRead(bb3, i3, v, false) and + adjacentDefReadExt(def, v, bb3, i3, bb2, i2) + ) + } + + pragma[nomagic] + private predicate adjacentDefReachesUncertainReadExt( + DefinitionExt def, SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 + ) { + adjacentDefReachesReadExt(def, v, bb1, i1, bb2, i2) and + variableRead(bb2, i2, v, false) + } + + /** + * Holds if the reference to `def` at index `i` in basic block `bb` can reach + * another definition `next` of the same underlying source variable, without + * passing through another write or non-pseudo read. + * + * The reference is either a read of `def` or `def` itself. + */ + pragma[nomagic] + private predicate lastRefBeforeRedefExt( + DefinitionExt def, SourceVariable v, BasicBlock bb, int i, BasicBlock input, + DefinitionExt next + ) { + lastRefRedefExt(def, v, bb, i, input, next) and + not variableRead(bb, i, v, false) + or + exists(BasicBlock bb0, int i0 | + lastRefRedefExt(def, v, bb0, i0, input, next) and + adjacentDefReachesUncertainReadExt(def, v, bb, i, bb0, i0) + ) + } + + /** Same as `adjacentDefReadExt`, but skips uncertain reads. */ + pragma[nomagic] + private predicate adjacentDefSkipUncertainReadsExt( + DefinitionExt def, SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 + ) { + adjacentDefReachesReadExt(def, v, bb1, i1, bb2, i2) and + variableRead(bb2, i2, v, true) + } + + pragma[nomagic] + private predicate adjacentReadPairExt(DefinitionExt def, ReadNode read1, ReadNode read2) { + exists(SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 | + read1.readsAt(bb1, i1, v) and + adjacentDefSkipUncertainReadsExt(def, v, bb1, i1, bb2, i2) and + read2.readsAt(bb2, i2, v) + ) + } + + /** An SSA definition into which another SSA definition may flow. */ + private class SsaInputDefinitionExt extends DefinitionExtFinal { + SsaInputDefinitionExt() { + this instanceof PhiNode + or + this instanceof PhiReadNode + } + + /** Holds if `def` may flow into this definition via basic block `input`. */ + predicate hasInputFromBlock( + DefinitionExt def, SourceVariable v, BasicBlock bb, int i, BasicBlock input + ) { + lastRefBeforeRedefExt(def, v, bb, i, input, this) + } + } + + private newtype TNode = + TParamNode(DfInput::Parameter p) or + TExprNode(DfInput::Expr e, Boolean isPost) { + exists(BasicBlock bb, int i | + variableRead(bb, i, _, true) and + e.hasCfgNode(bb, i) + ) + or + isPost = false + } or + TSsaDefinitionNode(DefinitionExt def) or + TSsaInputNode(SsaInputDefinitionExt def, BasicBlock input) { + def.hasInputFromBlock(_, _, _, _, input) + } + + /** + * A data flow node that we need to reference in the value step relation. + * + * Note that only the `SsaNode` subclass is expected to be added as additional + * nodes in `DataFlow::Node`. The other subclasses are expected to already be + * present and are included here in order to reference them in the step relation. + */ + abstract private class NodeImpl extends TNode { + /** Gets the location of this node. */ + abstract Location getLocation(); + + /** Gets a textual representation of this node. */ + abstract string toString(); + } + + final class Node = NodeImpl; + + /** A parameter node. */ + private class ParameterNodeImpl extends NodeImpl, TParamNode { + private DfInput::Parameter p; + + ParameterNodeImpl() { this = TParamNode(p) } + + /** Gets the underlying parameter. */ + DfInput::Parameter getParameter() { result = p } + + override string toString() { result = p.toString() } + + override Location getLocation() { result = p.getLocation() } + } + + final class ParameterNode = ParameterNodeImpl; + + /** A (post-update) expression node. */ + abstract private class ExprNodePreOrPostImpl extends NodeImpl, TExprNode { + DfInput::Expr e; + boolean isPost; + + ExprNodePreOrPostImpl() { this = TExprNode(e, isPost) } + + /** Gets the underlying expression. */ + DfInput::Expr getExpr() { result = e } + + override Location getLocation() { + exists(BasicBlock bb, int i | + e.hasCfgNode(bb, i) and + result = bb.getNode(i).getLocation() + ) + } + } + + final class ExprNodePreOrPost = ExprNodePreOrPostImpl; + + /** An expression node. */ + private class ExprNodeImpl extends ExprNodePreOrPostImpl { + ExprNodeImpl() { isPost = false } + + override string toString() { result = e.toString() } + } + + final class ExprNode = ExprNodeImpl; + + /** A post-update expression node. */ + private class ExprPostUpdateNodeImpl extends ExprNodePreOrPostImpl { + ExprPostUpdateNodeImpl() { isPost = true } + + /** Gets the pre-update expression node. */ + ExprNode getPreUpdateNode() { result = TExprNode(e, false) } + + override string toString() { result = e.toString() + " [postupdate]" } + } + + final class ExprPostUpdateNode = ExprPostUpdateNodeImpl; + + private class ReadNodeImpl extends ExprNodeImpl { + private BasicBlock bb_; + private int i_; + private SourceVariable v_; + + ReadNodeImpl() { + variableRead(bb_, i_, v_, true) and + this.getExpr().hasCfgNode(bb_, i_) + } + + SourceVariable getVariable() { result = v_ } + + pragma[nomagic] + predicate readsAt(BasicBlock bb, int i, SourceVariable v) { + bb = bb_ and + i = i_ and + v = v_ + } + } + + final private class ReadNode = ReadNodeImpl; + + /** A synthesized SSA data flow node. */ + abstract private class SsaNodeImpl extends NodeImpl { + /** Gets the underlying SSA definition. */ + abstract DefinitionExt getDefinitionExt(); + } + + final class SsaNode = SsaNodeImpl; + + /** An SSA definition, viewed as a node in a data flow graph. */ + private class SsaDefinitionExtNodeImpl extends SsaNodeImpl, TSsaDefinitionNode { + private DefinitionExt def; + + SsaDefinitionExtNodeImpl() { this = TSsaDefinitionNode(def) } + + override DefinitionExt getDefinitionExt() { result = def } + + override Location getLocation() { result = def.getLocation() } + + override string toString() { result = def.toString() } + } + + final class SsaDefinitionExtNode = SsaDefinitionExtNodeImpl; + + /** + * A node that represents an input to an SSA phi (read) definition. + * + * This allows for barrier guards to filter input to phi nodes. For example, in + * + * ```rb + * x = taint + * if x != "safe" then + * x = "safe" + * end + * sink x + * ``` + * + * the `false` edge out of `x != "safe"` guards the input from `x = taint` into the + * `phi` node after the condition. + * + * It is also relevant to filter input into phi read nodes: + * + * ```rb + * x = taint + * if b then + * if x != "safe1" then + * return + * end + * else + * if x != "safe2" then + * return + * end + * end + * + * sink x + * ``` + * + * both inputs into the phi read node after the outer condition are guarded. + */ + private class SsaInputNodeImpl extends SsaNodeImpl, TSsaInputNode { + private SsaInputDefinitionExt def_; + private BasicBlock input_; + + SsaInputNodeImpl() { this = TSsaInputNode(def_, input_) } + + /** Holds if this node represents input into SSA definition `def` via basic block `input`. */ + predicate isInputInto(DefinitionExt def, BasicBlock input) { + def = def_ and + input = input_ + } + + override SsaInputDefinitionExt getDefinitionExt() { result = def_ } + + override Location getLocation() { result = input_.getNode(input_.length() - 1).getLocation() } + + override string toString() { result = "[input] " + def_.toString() } + } + + final class SsaInputNode = SsaInputNodeImpl; + + /** + * Holds if `nodeFrom` is a node for SSA definition `def`, which can input + * node `nodeTo`. + */ + pragma[nomagic] + private predicate inputFromDef( + DefinitionExt def, SsaDefinitionExtNode nodeFrom, SsaInputNode nodeTo + ) { + exists(SourceVariable v, BasicBlock bb, int i, BasicBlock input, SsaInputDefinitionExt next | + next.hasInputFromBlock(def, v, bb, i, input) and + def = nodeFrom.getDefinitionExt() and + def.definesAt(v, bb, i, _) and + nodeTo = TSsaInputNode(next, input) + ) + } + + /** + * Holds if `nodeFrom` is a last read of SSA definition `def`, which + * can reach input node `nodeTo`. + */ + pragma[nomagic] + private predicate inputFromRead(DefinitionExt def, ReadNode nodeFrom, SsaInputNode nodeTo) { + exists(SourceVariable v, BasicBlock bb, int i, BasicBlock input, SsaInputDefinitionExt next | + next.hasInputFromBlock(def, v, bb, i, input) and + nodeFrom.readsAt(bb, i, v) and + nodeTo = TSsaInputNode(next, input) + ) + } + + pragma[nomagic] + private predicate firstReadExt(DefinitionExt def, ReadNode read) { + exists(SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 | + def.definesAt(v, bb1, i1, _) and + adjacentDefSkipUncertainReadsExt(def, v, bb1, i1, bb2, i2) and + read.readsAt(bb2, i2, v) + ) + } + + /** Holds if there is a local flow step from `nodeFrom` to `nodeTo`. */ + predicate localFlowStep(DefinitionExt def, Node nodeFrom, Node nodeTo) { + ( + // Flow from assignment into SSA definition + DfInput::ssaDefAssigns(def, nodeFrom.(ExprNode).getExpr()) + or + // Flow from parameter into entry definition + DfInput::ssaDefInitializesParam(def, nodeFrom.(ParameterNode).getParameter()) + ) and + nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def + or + // Flow from SSA definition to first read + def = nodeFrom.(SsaDefinitionExtNode).getDefinitionExt() and + firstReadExt(def, nodeTo) + or + // Flow from (post-update) read to next read + adjacentReadPairExt(def, [nodeFrom, nodeFrom.(ExprPostUpdateNode).getPreUpdateNode()], nodeTo) and + nodeFrom != nodeTo + or + // Flow into phi (read) SSA definition node from def + inputFromDef(def, nodeFrom, nodeTo) + or + // Flow into phi (read) SSA definition node from (post-update) read + inputFromRead(def, [nodeFrom, nodeFrom.(ExprPostUpdateNode).getPreUpdateNode()], nodeTo) + or + // Flow from input node to def + nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def and + def = nodeFrom.(SsaInputNode).getDefinitionExt() + } + + /** Provides the input to `BarrierGuards`. */ + signature module BarrierGuardsInputSig { + /** A (potential) guard. */ + class Guard { + /** Gets a textual representation of this guard. */ + string toString(); + + /** Holds if the `i`th node of basic block `bb` evaluates this guard. */ + predicate hasCfgNode(BasicBlock bb, int i); + } + + /** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */ + predicate guardControlsBlock(Guard guard, BasicBlock bb, boolean branch); + + /** Gets an immediate conditional successor of basic block `bb`, if any. */ + BasicBlock getAConditionalBasicBlockSuccessor(BasicBlock bb, boolean branch); + } + + /** Provides an implementatino of the `BarrierGuard` module. */ + module BarrierGuards { + /** + * Holds if the guard `g` validates the expression `e` upon evaluating to `branch`. + * + * The expression `e` is expected to be a syntactic part of the guard `g`. + * For example, the guard `g` might be a call `isSafe(x)` and the expression `e` + * the argument `x`. + */ + signature predicate guardChecksSig(BgInput::Guard g, DfInput::Expr e, boolean branch); + + /** + * Provides a set of barrier nodes for a guard that validates an expression. + * + * This is expected to be used in `isBarrier`/`isSanitizer` definitions + * in data flow and taint tracking. + */ + module BarrierGuard { + pragma[nomagic] + private DfInput::Expr getARead(Definition def) { + exists(SourceVariable v, BasicBlock bb, int i | + ssaDefReachesRead(v, def, bb, i) and + variableRead(bb, i, v, true) and + result.hasCfgNode(bb, i) + ) + } + + pragma[nomagic] + private predicate guardChecksSsaDef(BgInput::Guard g, boolean branch, Definition def) { + guardChecks(g, getARead(def), branch) + } + + pragma[nomagic] + private predicate guardControlsSsaRead( + BgInput::Guard g, boolean branch, Definition def, ExprNode n + ) { + exists(BasicBlock bb, DfInput::Expr e | + e = n.getExpr() and + getARead(def) = e and + BgInput::guardControlsBlock(g, bb, branch) and + e.hasCfgNode(bb, _) + ) + } + + pragma[nomagic] + private predicate guardControlsPhiInput( + BgInput::Guard g, boolean branch, Definition def, BasicBlock input, + SsaInputDefinitionExt phi + ) { + phi.hasInputFromBlock(def, _, _, _, input) and + ( + BgInput::guardControlsBlock(g, input, branch) + or + exists(int last | + last = input.length() - 1 and + g.hasCfgNode(input, last) and + BgInput::getAConditionalBasicBlockSuccessor(input, branch) = phi.getBasicBlock() + ) + ) + } + + /** Gets a node that is safely guarded by the given guard check. */ + pragma[nomagic] + Node getABarrierNode() { + exists(BgInput::Guard g, boolean branch, Definition def | + guardChecksSsaDef(g, branch, def) and + guardControlsSsaRead(g, branch, def, result) + ) + or + exists( + BgInput::Guard g, boolean branch, Definition def, BasicBlock input, + SsaInputDefinitionExt phi + | + guardChecksSsaDef(g, branch, def) and + guardControlsPhiInput(g, branch, def, input, phi) and + result.(SsaInputNode).isInputInto(phi, input) + ) + } + } + } + } } diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index 7a8c5a300d004..971cc8cc705cb 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -8,18 +8,20 @@ module Ssa { private module SsaInput implements SsaImplCommon::InputSig { private import internal.DataFlowPrivate - private import codeql.swift.controlflow.ControlFlowGraph + private import codeql.swift.controlflow.ControlFlowGraph as Cfg private import codeql.swift.controlflow.CfgNodes class BasicBlock = BasicBlocks::BasicBlock; + class ControlFlowNode = Cfg::ControlFlowNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class ExitBasicBlock = BasicBlocks::ExitBasicBlock; + class ExitBasicBlock extends BasicBlock, BasicBlocks::ExitBasicBlock { } private newtype TSourceVariable = TNormalSourceVariable(VarDecl v) or @@ -138,7 +140,7 @@ module Ssa { cached class Definition extends SsaImpl::Definition { cached - Location getLocation() { none() } + override Location getLocation() { none() } cached ControlFlowNode getARead() { diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index eb2caea8c2f80..de54324e005be 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -886,16 +886,23 @@ private predicate closureFlowStep(CaptureInput::Expr e1, CaptureInput::Expr e2) private module CaptureInput implements VariableCapture::InputSig { private import swift as S + private import codeql.swift.controlflow.ControlFlowGraph as Cfg private import codeql.swift.controlflow.BasicBlocks as B class BasicBlock instanceof B::BasicBlock { string toString() { result = super.toString() } + ControlFlowNode getNode(int i) { result = super.getNode(i) } + + int length() { result = super.length() } + Callable getEnclosingCallable() { result = super.getScope() } Location getLocation() { result = super.getLocation() } } + class ControlFlowNode = Cfg::ControlFlowNode; + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.(B::BasicBlock).immediatelyDominates(bb) }