From 119837bb1d37477e5a001e488d58f850a0ff1ee0 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 18 Aug 2025 13:58:24 +0200 Subject: [PATCH 1/9] BasicBlock: Add CFG signature. --- .../codeql/controlflow/BasicBlock.qll | 127 +++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/BasicBlock.qll b/shared/controlflow/codeql/controlflow/BasicBlock.qll index 132920e329f..2d259d7c4ec 100644 --- a/shared/controlflow/codeql/controlflow/BasicBlock.qll +++ b/shared/controlflow/codeql/controlflow/BasicBlock.qll @@ -12,7 +12,11 @@ private import codeql.util.Location /** Provides the language-specific input specification. */ signature module InputSig { - class SuccessorType; + /** The type of a control flow successor. */ + class SuccessorType { + /** Gets a textual representation of this successor type. */ + string toString(); + } /** Hold if `t` represents a conditional successor type. */ predicate successorTypeIsCondition(SuccessorType t); @@ -47,12 +51,131 @@ signature module InputSig { predicate nodeIsPostDominanceExit(Node node); } +signature module CfgSig { + /** 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(); + } + + /** The type of a control flow successor. */ + class SuccessorType { + /** Gets a textual representation of this successor type. */ + string toString(); + } + + /** + * A basic block, that is, a maximal straight-line sequence of control flow nodes + * without branches or joins. + */ + class BasicBlock { + /** Gets a textual representation of this basic block. */ + string toString(); + + /** Gets the location of this basic block. */ + Location getLocation(); + + /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ + ControlFlowNode getNode(int pos); + + /** Gets the last control flow node in this basic block. */ + ControlFlowNode getLastNode(); + + /** Gets the length of this basic block. */ + int length(); + + /** Gets an immediate successor of this basic block, if any. */ + BasicBlock getASuccessor(); + + /** Gets an immediate successor of this basic block of a given type, if any. */ + BasicBlock getASuccessor(SuccessorType t); + + /** + * Holds if this basic block strictly dominates basic block `bb`. + * + * That is, all paths reaching `bb` from the entry point basic block must + * go through this basic block and this basic block is different from `bb`. + */ + predicate strictlyDominates(BasicBlock bb); + + /** + * Holds if this basic block dominates basic block `bb`. + * + * That is, all paths reaching `bb` from the entry point basic block must + * go through this basic block. + */ + predicate dominates(BasicBlock bb); + + /** + * Holds if `df` is in the dominance frontier of this basic block. That is, + * this basic block dominates a predecessor of `df`, but does not dominate + * `df` itself. I.e., it is equivaluent to: + * ``` + * this.dominates(df.getAPredecessor()) and not this.strictlyDominates(df) + * ``` + */ + predicate inDominanceFrontier(BasicBlock df); + + /** + * Gets the basic block that immediately dominates this basic block, if any. + * + * That is, the result is the unique basic block satisfying: + * 1. The result strictly dominates this basic block. + * 2. There exists no other basic block that is strictly dominated by the + * result and which strictly dominates this basic block. + * + * All basic blocks, except entry basic blocks, have a unique immediate + * dominator. + */ + BasicBlock getImmediateDominator(); + + /** + * Holds if this basic block strictly post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block and this basic block is + * different from `bb`. + */ + predicate strictlyPostDominates(BasicBlock bb); + + /** + * Holds if this basic block post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block. + */ + predicate postDominates(BasicBlock bb); + } + + /** + * Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1` + * and `bb2` is a dominating edge. + * + * An edge `(bb1, bb2)` is dominating if there exists a basic block that can + * only be reached from the entry block by going through `(bb1, bb2)`. This + * implies that `(bb1, bb2)` dominates its endpoint `bb2`. I.e., `bb2` can + * only be reached from the entry block by going via `(bb1, bb2)`. + * + * This is a necessary and sufficient condition for an edge to dominate some + * block, and therefore `dominatingEdge(bb1, bb2) and bb2.dominates(bb3)` + * means that the edge `(bb1, bb2)` dominates `bb3`. + */ + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2); +} + /** * Provides a basic block construction on top of a control flow graph. */ -module Make Input> { +module Make Input> implements CfgSig { private import Input + class ControlFlowNode = Input::Node; + + class SuccessorType = Input::SuccessorType; + /** * A basic block, that is, a maximal straight-line sequence of control flow nodes * without branches or joins. From bb3abc815f527aff46a41c8d2b2808c728ab26bc Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 18 Aug 2025 14:03:49 +0200 Subject: [PATCH 2/9] SSA: Update input to use member predicates. --- .../ir/dataflow/internal/SsaImplCommon.qll | 10 ++- .../ql/consistency-queries/CfgConsistency.ql | 4 +- .../code/csharp/controlflow/BasicBlocks.qll | 2 + .../controlflow/internal/PreBasicBlocks.qll | 19 ++++- .../csharp/controlflow/internal/PreSsa.qll | 20 ++--- .../code/csharp/dataflow/internal/BaseSSA.qll | 6 -- .../dataflow/internal/DataFlowPrivate.qll | 12 +-- .../code/csharp/dataflow/internal/SsaImpl.qll | 4 - .../code/java/controlflow/BasicBlocks.qll | 6 ++ .../code/java/dataflow/internal/BaseSSA.qll | 4 - .../dataflow/internal/DataFlowPrivate.qll | 12 +-- .../code/java/dataflow/internal/SsaImpl.qll | 4 - .../dataflow/internal/VariableCapture.qll | 16 ++-- .../dataflow/internal/sharedlib/Ssa.qll | 29 ++++--- python/ql/lib/semmle/python/Flow.qll | 12 ++- .../dataflow/new/internal/VariableCapture.qll | 14 ++-- .../dataflow/internal/DataFlowPrivate.qll | 12 +-- .../codeql/ruby/dataflow/internal/SsaImpl.qll | 4 - .../rust/dataflow/internal/DataFlowImpl.qll | 12 +-- .../codeql/rust/dataflow/internal/SsaImpl.qll | 4 - .../codeql/dataflow/VariableCapture.qll | 47 +++-------- shared/ssa/codeql/ssa/Ssa.qll | 82 +++++-------------- swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 6 -- .../dataflow/internal/DataFlowPrivate.qll | 12 +-- 24 files changed, 147 insertions(+), 206 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll index 617e2be8cc3..d86e1ec613f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll @@ -778,11 +778,13 @@ module InputSigCommon { ControlFlowNode getNode(int i) { result = this.getInstruction(i) } int length() { result = this.getInstructionCount() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result.immediatelyDominates(this) } + + predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df } } class ControlFlowNode = Instruction; - - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } } diff --git a/csharp/ql/consistency-queries/CfgConsistency.ql b/csharp/ql/consistency-queries/CfgConsistency.ql index 3caf64f9aec..5fb52b3b09e 100644 --- a/csharp/ql/consistency-queries/CfgConsistency.ql +++ b/csharp/ql/consistency-queries/CfgConsistency.ql @@ -45,8 +45,8 @@ predicate bbIntraSuccInconsistency(ControlFlowElement pred, ControlFlowElement s bb.getASuccessor().getFirstElement() = succ ) and not exists(PreBasicBlock bb, int i | - bb.getElement(i) = pred and - bb.getElement(i + 1) = succ + bb.getNode(i) = pred and + bb.getNode(i + 1) = succ ) } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll index 9e548838ade..7079f2cb0a0 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -58,6 +58,8 @@ final class BasicBlock extends BasicBlocksImpl::BasicBlock { result.getFirstNode() = this.getLastNode().getAFalseSuccessor() } + BasicBlock getASuccessor() { result = super.getASuccessor() } + /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ ControlFlow::Node getNode(int pos) { result = super.getNode(pos) } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll index 08debc21c0d..3e6bf7f1f76 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll @@ -63,16 +63,20 @@ class PreBasicBlock extends ControlFlowElement { PreBasicBlock getAPredecessor() { result.getASuccessor() = this } - ControlFlowElement getElement(int pos) { bbIndex(this, result, pos) } + ControlFlowElement getNode(int pos) { bbIndex(this, result, pos) } - ControlFlowElement getAnElement() { result = this.getElement(_) } + deprecated ControlFlowElement getElement(int pos) { result = this.getNode(pos) } + + ControlFlowElement getAnElement() { result = this.getNode(_) } ControlFlowElement getFirstElement() { result = this } - ControlFlowElement getLastElement() { result = this.getElement(this.length() - 1) } + ControlFlowElement getLastElement() { result = this.getNode(this.length() - 1) } int length() { result = strictcount(this.getAnElement()) } + PreBasicBlock getImmediateDominator() { bbIDominates(result, this) } + predicate immediatelyDominates(PreBasicBlock bb) { bbIDominates(this, bb) } pragma[inline] @@ -84,6 +88,15 @@ class PreBasicBlock extends ControlFlowElement { or this.strictlyDominates(bb) } + + predicate inDominanceFrontier(PreBasicBlock df) { + this = df.getAPredecessor() and not bbIDominates(this, df) + or + exists(PreBasicBlock prev | prev.inDominanceFrontier(df) | + bbIDominates(this, prev) and + not bbIDominates(this, df) + ) + } } private Completion getConditionalCompletion(ConditionalCompletion cc) { 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 6507bbbe04b..9205974853e 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll @@ -14,7 +14,7 @@ module PreSsa { private predicate definitionAt( AssignableDefinition def, SsaInput::BasicBlock bb, int i, SsaInput::SourceVariable v ) { - bb.getElement(i) = def.getExpr() and + bb.getNode(i) = def.getExpr() and v = def.getTarget() and // In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x` not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = def | @@ -80,16 +80,10 @@ module PreSsa { } module SsaInput implements SsaImplCommon::InputSig { - class BasicBlock extends PreBasicBlocks::PreBasicBlock { - ControlFlowNode getNode(int i) { result = this.getElement(i) } - } + class BasicBlock = PreBasicBlocks::PreBasicBlock; class ControlFlowNode = ControlFlowElement; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - private class ExitBasicBlock extends BasicBlock { ExitBasicBlock() { scopeLast(_, this.getLastElement(), _) } } @@ -142,7 +136,7 @@ module PreSsa { predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { exists(AssignableRead read | - read = bb.getElement(i) and + read = bb.getNode(i) and read.getTarget() = v and certain = true ) @@ -163,7 +157,7 @@ module PreSsa { final AssignableRead getARead() { exists(SsaInput::BasicBlock bb, int i | SsaImpl::ssaDefReachesRead(_, this, bb, i) and - result = bb.getElement(i) + result = bb.getNode(i) ) } @@ -177,7 +171,7 @@ module PreSsa { final AssignableRead getAFirstRead() { exists(SsaInput::BasicBlock bb, int i | SsaImpl::firstUse(this, bb, i, true) and - result = bb.getElement(i) + result = bb.getNode(i) ) } @@ -214,9 +208,9 @@ module PreSsa { predicate adjacentReadPairSameVar(AssignableRead read1, AssignableRead read2) { exists(SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 | - read1 = bb1.getElement(i1) and + read1 = bb1.getNode(i1) and SsaImpl::adjacentUseUse(bb1, i1, bb2, i2, _, true) and - read2 = bb2.getElement(i2) + read2 = bb2.getNode(i2) ) } } 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 ec1b5a0188e..dc056e2f0eb 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll @@ -49,12 +49,6 @@ module BaseSsa { class ControlFlowNode = ControlFlow::Node; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result = bb.getImmediateDominator() - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class SourceVariable = PreSsa::SimpleLocalScopeVariable; predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { 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 ff2bf709251..c5e96e46450 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -283,16 +283,16 @@ module VariableCapture { class BasicBlock extends BasicBlocks::BasicBlock { Callable getEnclosingCallable() { result = super.getCallable() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } } class ControlFlowNode = Cfg::ControlFlow::Node; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result = bb.getImmediateDominator() - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - private predicate thisAccess(ControlFlow::Node cfn, InstanceCallable c) { ThisFlow::thisAccessExpr(cfn.getAstNode()) and cfn.getEnclosingCallable().getEnclosingCallable*() = c 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 d1490c84916..dd77506ed68 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -13,10 +13,6 @@ private module SsaInput implements SsaImplCommon::InputSig { class ControlFlowNode = ControlFlow::Node; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class SourceVariable = Ssa::SourceVariable; /** diff --git a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll index e974f711ec4..b7e52f658fd 100644 --- a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll +++ b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll @@ -98,6 +98,12 @@ class BasicBlock extends BbImpl::BasicBlock { /** Gets an immediate successor of this basic block of a given type, if any. */ BasicBlock getASuccessor(Input::SuccessorType t) { result = super.getASuccessor(t) } + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } + /** * DEPRECATED: Use `getASuccessor` instead. * 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 5c0fbb88d66..444d8f3cbd9 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll @@ -164,10 +164,6 @@ private module SsaInput implements SsaImplCommon::InputSig { class ControlFlowNode = J::ControlFlowNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class SourceVariable = BaseSsaSourceVariable; /** 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 8b9087ecbdc..22b5cc62d57 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -82,16 +82,16 @@ private module CaptureInput implements VariableCapture::InputSig { Callable getEnclosingCallable() { result = super.getEnclosingCallable() } Location getLocation() { result = super.getLocation() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } } class ControlFlowNode = J::ControlFlowNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result.(J::BasicBlock).immediatelyDominates(bb) - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.(J::BasicBlock).getASuccessor() } - //TODO: support capture of `this` in lambdas class CapturedVariable instanceof LocalScopeVariable { CapturedVariable() { 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 51da69e9d64..20e7fc02336 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll @@ -173,10 +173,6 @@ private module SsaInput implements SsaImplCommon::InputSig { class ControlFlowNode = J::ControlFlowNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class SourceVariable = SsaSourceVariable; /** diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll index 6cdb95bc4d9..7881f487e9b 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll @@ -108,8 +108,18 @@ module VariableCaptureConfig implements InputSig { class ControlFlowNode = js::ControlFlowNode; - class BasicBlock extends js::BasicBlock { + final private class JsBasicBlock = js::BasicBlock; + + class BasicBlock extends JsBasicBlock { Callable getEnclosingCallable() { result = this.getContainer().getFunctionBoundary() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { + df.(js::ReachableJoinBlock).inDominanceFrontierOf(this) + } } class Callable extends js::StmtContainer { @@ -235,10 +245,6 @@ module VariableCaptureConfig implements InputSig { } } - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } - predicate entryBlock(BasicBlock bb) { bb instanceof js::EntryBasicBlock } } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll index 1172a64a057..2d672468946 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll @@ -12,7 +12,17 @@ private import semmle.javascript.dataflow.internal.VariableOrThis module SsaConfig implements InputSig { class ControlFlowNode = js::ControlFlowNode; - class BasicBlock = js::BasicBlock; + final private class JsBasicBlock = js::BasicBlock; + + class BasicBlock extends JsBasicBlock { + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { + df.(js::ReachableJoinBlock).inDominanceFrontierOf(this) + } + } class SourceVariable extends LocalVariableOrThis { SourceVariable() { not this.isCaptured() } @@ -40,11 +50,6 @@ module SsaConfig implements InputSig { certain = true and bb.getNode(i).(ThisUse).getBindingContainer() = v.asThisContainer() } - - predicate getImmediateBasicBlockDominator = BasicBlockInternal::immediateDominator/1; - - pragma[inline] - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } } import Make @@ -55,7 +60,7 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { class Expr extends js::ControlFlowNode { Expr() { this = any(SsaConfig::SourceVariable v).getAUse() } - predicate hasCfgNode(js::BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(SsaConfig::BasicBlock bb, int i) { this = bb.getNode(i) } } predicate ssaDefHasSource(WriteDefinition def) { @@ -82,7 +87,9 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { * Holds if the evaluation of this guard to `branch` corresponds to the edge * from `bb1` to `bb2`. */ - predicate hasValueBranchEdge(js::BasicBlock bb1, js::BasicBlock bb2, GuardValue branch) { + predicate hasValueBranchEdge( + SsaConfig::BasicBlock bb1, SsaConfig::BasicBlock bb2, GuardValue branch + ) { exists(js::ConditionGuardNode g | g.getTest() = this and bb1 = this.getBasicBlock() and @@ -96,13 +103,15 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { * branch edge from `bb1` to `bb2`. That is, following the edge from * `bb1` to `bb2` implies that this guard evaluated to `branch`. */ - predicate valueControlsBranchEdge(js::BasicBlock bb1, js::BasicBlock bb2, GuardValue branch) { + predicate valueControlsBranchEdge( + SsaConfig::BasicBlock bb1, SsaConfig::BasicBlock bb2, GuardValue branch + ) { this.hasValueBranchEdge(bb1, bb2, branch) } } pragma[inline] - predicate guardDirectlyControlsBlock(Guard guard, js::BasicBlock bb, GuardValue branch) { + predicate guardDirectlyControlsBlock(Guard guard, SsaConfig::BasicBlock bb, GuardValue branch) { exists(js::ConditionGuardNode g | g.getTest() = guard and g.dominates(bb) and diff --git a/python/ql/lib/semmle/python/Flow.qll b/python/ql/lib/semmle/python/Flow.qll index 90633651f11..8fd8502ede4 100644 --- a/python/ql/lib/semmle/python/Flow.qll +++ b/python/ql/lib/semmle/python/Flow.qll @@ -1082,9 +1082,15 @@ class BasicBlock extends @py_flow_node { * Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor * of `other` but does not strictly dominate `other` */ - pragma[noinline] - predicate dominanceFrontier(BasicBlock other) { - this.dominates(other.getAPredecessor()) and not this.strictlyDominates(other) + predicate dominanceFrontier(BasicBlock other) { this.inDominanceFrontier(other) } + + predicate inDominanceFrontier(BasicBlock df) { + this = df.getAPredecessor() and not this = df.getImmediateDominator() + or + exists(BasicBlock prev | prev.inDominanceFrontier(df) | + this = prev.getImmediateDominator() and + not this = df.getImmediateDominator() + ) } private ControlFlowNode firstNode() { result = this } 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 6cb80881e2a..8ba5247ce52 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll @@ -23,7 +23,9 @@ private module CaptureInput implements Shared::InputSig { predicate isConstructor() { none() } } - class BasicBlock extends PY::BasicBlock { + final private class PyBasicBlock = PY::BasicBlock; + + class BasicBlock extends PyBasicBlock { int length() { result = count(int i | exists(this.getNode(i))) } Callable getEnclosingCallable() { result = this.getScope() } @@ -34,14 +36,16 @@ private module CaptureInput implements Shared::InputSig { // and we just need a way to identify the basic block // during debugging, so this will be serviceable. Location getLocation() { result = super.getNode(0).getLocation() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } } class ControlFlowNode = PY::ControlFlowNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class CapturedVariable extends LocalVariable { Function f; diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index 59fe0238c7f..266400edb5b 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -297,16 +297,16 @@ module VariableCapture { class BasicBlock extends BasicBlocks::BasicBlock { Callable getEnclosingCallable() { result = this.getScope() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } } class ControlFlowNode = Cfg::CfgNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result = bb.getImmediateDominator() - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class CapturedVariable extends LocalVariable { CapturedVariable() { this.isCaptured() and diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index fd1619b1c63..fe7e2498a71 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -17,10 +17,6 @@ module SsaInput implements SsaImplCommon::InputSig { class ControlFlowNode = Cfg::CfgNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class SourceVariable = LocalVariable; /** diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 727f14bb94a..70ae830c2d0 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -862,16 +862,16 @@ module VariableCapture { class BasicBlock extends BasicBlocks::BasicBlock { Callable getEnclosingCallable() { result = this.getScope() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } } class ControlFlowNode = CfgNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result = bb.getImmediateDominator() - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class CapturedVariable extends Variable { CapturedVariable() { this.isCaptured() } diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll index 9b6d254dec1..6429055fccd 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll @@ -59,10 +59,6 @@ module SsaInput implements SsaImplCommon::InputSig { class ControlFlowNode = CfgNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class SourceVariable = Variable; predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { diff --git a/shared/dataflow/codeql/dataflow/VariableCapture.qll b/shared/dataflow/codeql/dataflow/VariableCapture.qll index 922391221a4..60a94bea351 100644 --- a/shared/dataflow/codeql/dataflow/VariableCapture.qll +++ b/shared/dataflow/codeql/dataflow/VariableCapture.qll @@ -30,6 +30,12 @@ signature module InputSig { /** Gets the location of this basic block. */ Location getLocation(); + + BasicBlock getASuccessor(); + + BasicBlock getImmediateDominator(); + + predicate inDominanceFrontier(BasicBlock df); } /** A control flow node. */ @@ -41,33 +47,8 @@ signature module InputSig { Location getLocation(); } - /** - * Gets the basic block that immediately dominates basic block `bb`, if any. - * - * That is, all paths reaching `bb` from some entry point basic block must go - * through the result. - * - * Example: - * - * ```csharp - * int M(string s) { - * if (s == null) - * throw new ArgumentNullException(nameof(s)); - * return s.Length; - * } - * ``` - * - * The basic block starting on line 2 is an immediate dominator of - * the basic block on line 4 (all paths from the entry point of `M` - * to `return s.Length;` must go through the null check. - */ - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb); - - /** Gets an immediate successor of basic block `bb`, if any. */ - BasicBlock getABasicBlockSuccessor(BasicBlock bb); - /** Holds if `bb` is a control-flow entry point. */ - default predicate entryBlock(BasicBlock bb) { not exists(getImmediateBasicBlockDominator(bb)) } + default predicate entryBlock(BasicBlock bb) { not exists(bb.getImmediateDominator()) } /** A variable that is captured in a closure. */ class CapturedVariable { @@ -332,17 +313,17 @@ module Flow Input> implements OutputSig query predicate uniqueDominator(RelevantBasicBlock bb, string msg) { msg = "BasicBlock has multiple immediate dominators" and - 2 <= strictcount(getImmediateBasicBlockDominator(bb)) + 2 <= strictcount(bb.getImmediateDominator()) } query predicate localDominator(RelevantBasicBlock bb, string msg) { msg = "BasicBlock has non-local dominator" and - bb.getEnclosingCallable() != getImmediateBasicBlockDominator(bb).getEnclosingCallable() + bb.getEnclosingCallable() != bb.getImmediateDominator().getEnclosingCallable() } query predicate localSuccessor(RelevantBasicBlock bb, string msg) { msg = "BasicBlock has non-local successor" and - bb.getEnclosingCallable() != getABasicBlockSuccessor(bb).getEnclosingCallable() + bb.getEnclosingCallable() != bb.getASuccessor().getEnclosingCallable() } query predicate uniqueDefiningScope(CapturedVariable v, string msg) { @@ -690,14 +671,6 @@ module Flow Input> implements OutputSig final class ControlFlowNode = Input::ControlFlowNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result = Input::getImmediateBasicBlockDominator(bb) - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { - result = Input::getABasicBlockSuccessor(bb) - } - class SourceVariable = CaptureContainer; predicate variableWrite(BasicBlock bb, int i, SourceVariable cc, boolean certain) { diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 2aa136ff719..df25142792d 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -26,6 +26,12 @@ signature module InputSig { /** Gets the location of this basic block. */ Location getLocation(); + + BasicBlock getASuccessor(); + + BasicBlock getImmediateDominator(); + + predicate inDominanceFrontier(BasicBlock df); } /** A control flow node. */ @@ -37,31 +43,6 @@ signature module InputSig { Location getLocation(); } - /** - * Gets the basic block that immediately dominates basic block `bb`, if any. - * - * That is, all paths reaching `bb` from some entry point basic block must go - * through the result. - * - * Example: - * - * ```csharp - * int M(string s) { - * if (s == null) - * throw new ArgumentNullException(nameof(s)); - * return s.Length; - * } - * ``` - * - * The basic block starting on line 2 is an immediate dominator of - * the basic block on line 4 (all paths from the entry point of `M` - * to `return s.Length;` must go through the null check. - */ - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb); - - /** Gets an immediate successor of basic block `bb`, if any. */ - BasicBlock getABasicBlockSuccessor(BasicBlock bb); - /** A variable that can be SSA converted. */ class SourceVariable { /** Gets a textual representation of this variable. */ @@ -111,9 +92,7 @@ signature module InputSig { module Make Input> { private import Input - private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { - getABasicBlockSuccessor(result) = bb - } + private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { result.getASuccessor() = bb } /** * A classification of variable references into reads and @@ -237,9 +216,7 @@ module Make Input> { /** * Holds if source variable `v` is live at the end of basic block `bb`. */ - predicate liveAtExit(BasicBlock bb, SourceVariable v) { - liveAtEntry(getABasicBlockSuccessor(bb), v) - } + predicate liveAtExit(BasicBlock bb, SourceVariable v) { liveAtEntry(bb.getASuccessor(), v) } /** * Holds if variable `v` is live in basic block `bb` at rank `rnk`. @@ -270,25 +247,6 @@ module Make Input> { private import Liveness - /** - * Holds if `df` is in the dominance frontier of `bb`. - * - * This is equivalent to: - * - * ```ql - * bb = getImmediateBasicBlockDominator*(getABasicBlockPredecessor(df)) and - * not bb = getImmediateBasicBlockDominator+(df) - * ``` - */ - private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) { - bb = getABasicBlockPredecessor(df) and not bb = getImmediateBasicBlockDominator(df) - or - exists(BasicBlock prev | inDominanceFrontier(prev, df) | - bb = getImmediateBasicBlockDominator(prev) and - not bb = getImmediateBasicBlockDominator(df) - ) - } - /** * Holds if `bb` is in the dominance frontier of a block containing a * definition of `v`. @@ -297,7 +255,7 @@ module Make Input> { private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) { exists(BasicBlock defbb, Definition def | def.definesAt(v, defbb, _) and - inDominanceFrontier(defbb, bb) + defbb.inDominanceFrontier(bb) ) } @@ -307,7 +265,7 @@ module Make Input> { */ pragma[nomagic] private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) { - exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) | + exists(BasicBlock readbb | readbb.inDominanceFrontier(bb) | ssaDefReachesRead(v, _, readbb, _) and variableRead(readbb, _, v, true) and not variableWrite(readbb, _, v, _) @@ -389,7 +347,7 @@ module Make Input> { */ pragma[nomagic] private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) { - idom = getImmediateBasicBlockDominator(bb) and + idom = bb.getImmediateDominator() and liveAtExit(bb, v) and not any(Definition def).definesAt(v, bb, _) } @@ -439,7 +397,7 @@ module Make Input> { ssaDefReachesReadWithinBlock(v, def, bb, i) or ssaRef(bb, i, v, Read()) and - ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, v) and + ssaDefReachesEndOfBlock(bb.getImmediateDominator(), def, v) and not ssaDefReachesReadWithinBlock(v, _, bb, i) } @@ -483,7 +441,7 @@ module Make Input> { */ pragma[nomagic] private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) { - idom = getImmediateBasicBlockDominator(bb) and + idom = bb.getImmediateDominator() and liveAtExit(bb, v) and not ssaRef(bb, _, v, _) } @@ -517,7 +475,7 @@ module Make Input> { bb1 = bb2 and refRank(bb1, i1, v, _) + 1 = refRank(bb2, i2, v, Read()) or - refReachesEndOfBlock(bb1, i1, getImmediateBasicBlockDominator(bb2), v) and + refReachesEndOfBlock(bb1, i1, bb2.getImmediateDominator(), v) and 1 = refRank(bb2, i2, v, Read()) } @@ -808,12 +766,12 @@ module Make Input> { DefinitionExt def, SourceVariable v, BasicBlock bb1, BasicBlock bb2 ) { defOccursInBlock(def, bb1, v, _) and - bb2 = getABasicBlockSuccessor(bb1) + bb2 = bb1.getASuccessor() or exists(BasicBlock mid | varBlockReachesExt(def, v, bb1, mid) and ssaDefReachesThroughBlock(def, mid) and - bb2 = getABasicBlockSuccessor(mid) + bb2 = mid.getASuccessor() ) } @@ -943,7 +901,7 @@ module Make Input> { // the node. If two definitions dominate a node then one must dominate the // other, so therefore the definition of _closest_ is given by the dominator // tree. Thus, reaching definitions can be calculated in terms of dominance. - ssaDefReachesEndOfBlockExt0(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and + ssaDefReachesEndOfBlockExt0(bb.getImmediateDominator(), def, pragma[only_bind_into](v)) and liveThroughExt(bb, pragma[only_bind_into](v)) } @@ -1150,7 +1108,7 @@ module Make Input> { predicate uncertainWriteDefinitionInput = SsaDefReachesNew::uncertainWriteDefinitionInput/2; /** Holds if `bb` is a control-flow exit point. */ - private predicate exitBlock(BasicBlock bb) { not exists(getABasicBlockSuccessor(bb)) } + private predicate exitBlock(BasicBlock bb) { not exists(bb.getASuccessor()) } /** * NB: If this predicate is exposed, it should be cached. @@ -1418,7 +1376,7 @@ module Make Input> { or ssaDefReachesRead(v, def, bb, i) and not SsaDefReachesNew::ssaDefReachesReadWithinBlock(v, def, bb, i) and - not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _) + not def.definesAt(v, bb.getImmediateDominator*(), _) ) } @@ -1667,7 +1625,7 @@ module Make Input> { DfInput::keepAllPhiInputBackEdges() and exists(getAPhiInputDef(phi, input)) and phi.getBasicBlock() = bbPhi and - getImmediateBasicBlockDominator+(input) = bbPhi + input.getImmediateDominator+() = bbPhi ) } diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index ed75a06e534..e7dff4fe753 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -15,12 +15,6 @@ module Ssa { class ControlFlowNode = Cfg::ControlFlowNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result = bb.getImmediateDominator() - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - private newtype TSourceVariable = TNormalSourceVariable(VarDecl v) or TKeyPathSourceVariable(EntryNode entry) { entry.getScope() instanceof KeyPathExpr } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 4849c5ac235..712d2282b41 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -897,16 +897,16 @@ private module CaptureInput implements VariableCapture::InputSig { Callable getEnclosingCallable() { result = super.getScope() } Location getLocation() { result = super.getLocation() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } } class ControlFlowNode = Cfg::ControlFlowNode; - BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { - result.(B::BasicBlock).immediatelyDominates(bb) - } - - BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.(B::BasicBlock).getASuccessor() } - class CapturedVariable instanceof S::VarDecl { CapturedVariable() { any(S::CapturedDecl capturedDecl).getDecl() = this and From e53b22dfa7ce3663fe690ac4f16d159576ba7529 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 18 Aug 2025 16:00:14 +0200 Subject: [PATCH 3/9] SSA/VariableCapture: Use shared BasicBlock signature. --- .../codeql/controlflow/BasicBlock.qll | 11 ++- .../codeql/dataflow/VariableCapture.qll | 92 +++++++------------ shared/dataflow/qlpack.yml | 1 + shared/ssa/codeql/ssa/Ssa.qll | 43 ++------- shared/ssa/qlpack.yml | 1 + 5 files changed, 52 insertions(+), 96 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/BasicBlock.qll b/shared/controlflow/codeql/controlflow/BasicBlock.qll index 2d259d7c4ec..683182786be 100644 --- a/shared/controlflow/codeql/controlflow/BasicBlock.qll +++ b/shared/controlflow/codeql/controlflow/BasicBlock.qll @@ -164,6 +164,9 @@ signature module CfgSig { * means that the edge `(bb1, bb2)` dominates `bb3`. */ predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2); + + /** Holds if `bb` is an entry basic block. */ + predicate entryBlock(BasicBlock bb); } /** @@ -398,6 +401,9 @@ module Make Input> implements CfgSig Input> implements CfgSig Input> implements CfgSig { - /** - * A basic block, that is, a maximal straight-line sequence of control flow nodes - * without branches or joins. - */ - class BasicBlock { - /** Gets a textual representation of this basic block. */ - string toString(); +signature class BasicBlockSig; - /** 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(); - - /** Gets the location of this basic block. */ - Location getLocation(); - - BasicBlock getASuccessor(); - - BasicBlock getImmediateDominator(); - - predicate inDominanceFrontier(BasicBlock df); - } - - /** 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(); - } - - /** Holds if `bb` is a control-flow entry point. */ - default predicate entryBlock(BasicBlock bb) { not exists(bb.getImmediateDominator()) } +signature module InputSig { + /** Gets the enclosing callable of the basic block. */ + Callable basicBlockGetEnclosingCallable(BasicBlock bb); /** A variable that is captured in a closure. */ class CapturedVariable { @@ -134,7 +101,9 @@ signature module InputSig { } } -signature module OutputSig I> { +signature module OutputSig< + LocationSig Location, BasicBlockSig BasicBlock, InputSig I> +{ /** * A data flow node that we need to reference in the step relations for * captured variables. @@ -236,9 +205,18 @@ signature module OutputSig I> { * Constructs the type `ClosureNode` and associated step relations, which are * intended to be included in the data-flow node and step relations. */ -module Flow Input> implements OutputSig { +module Flow< + LocationSig Location, BB::CfgSig Cfg, InputSig Input> + implements OutputSig +{ private import Input + final private class CfgBb = Cfg::BasicBlock; + + private class BasicBlock extends CfgBb { + Callable getEnclosingCallable() { result = basicBlockGetEnclosingCallable(this) } + } + additional module ConsistencyChecks { final private class FinalExpr = Expr; @@ -318,12 +296,12 @@ module Flow Input> implements OutputSig query predicate localDominator(RelevantBasicBlock bb, string msg) { msg = "BasicBlock has non-local dominator" and - bb.getEnclosingCallable() != bb.getImmediateDominator().getEnclosingCallable() + bb.getEnclosingCallable() != bb.getImmediateDominator().(BasicBlock).getEnclosingCallable() } query predicate localSuccessor(RelevantBasicBlock bb, string msg) { msg = "BasicBlock has non-local successor" and - bb.getEnclosingCallable() != bb.getASuccessor().getEnclosingCallable() + bb.getEnclosingCallable() != bb.getASuccessor().(BasicBlock).getEnclosingCallable() } query predicate uniqueDefiningScope(CapturedVariable v, string msg) { @@ -650,7 +628,7 @@ module Flow Input> implements OutputSig /** Holds if `cc` needs a definition at the entry of its callable scope. */ private predicate entryDef(CaptureContainer cc, BasicBlock bb, int i) { exists(Callable c | - entryBlock(bb) and + Cfg::entryBlock(bb) and pragma[only_bind_out](bb.getEnclosingCallable()) = c and i = min(int j | @@ -666,14 +644,10 @@ module Flow Input> implements OutputSig ) } - private module CaptureSsaInput implements Ssa::InputSig { - final class BasicBlock = Input::BasicBlock; - - final class ControlFlowNode = Input::ControlFlowNode; - + private module CaptureSsaInput implements Ssa::InputSig { class SourceVariable = CaptureContainer; - predicate variableWrite(BasicBlock bb, int i, SourceVariable cc, boolean certain) { + predicate variableWrite(Cfg::BasicBlock bb, int i, SourceVariable cc, boolean certain) { Cached::ref() and ( exists(CapturedVariable v | cc = TVariable(v) and captureWrite(v, bb, i, true, _)) @@ -683,9 +657,9 @@ module Flow Input> implements OutputSig certain = true } - predicate variableRead(BasicBlock bb, int i, SourceVariable cc, boolean certain) { + predicate variableRead(Cfg::BasicBlock bb, int i, SourceVariable cc, boolean certain) { ( - synthThisQualifier(bb, i) and cc = TThis(bb.getEnclosingCallable()) + synthThisQualifier(bb, i) and cc = TThis(bb.(BasicBlock).getEnclosingCallable()) or exists(CapturedVariable v | cc = TVariable(v) | captureRead(v, bb, i, true, _) or synthRead(v, bb, i, true, _) @@ -695,26 +669,30 @@ module Flow Input> implements OutputSig } } - private module CaptureSsa = Ssa::Make; + private module CaptureSsa = Ssa::Make; private module DataFlowIntegrationInput implements CaptureSsa::DataFlowIntegrationInputSig { private import codeql.util.Void - class Expr instanceof Input::ControlFlowNode { + class Expr instanceof Cfg::ControlFlowNode { string toString() { result = super.toString() } - predicate hasCfgNode(BasicBlock bb, int i) { bb.getNode(i) = this } + predicate hasCfgNode(Cfg::BasicBlock bb, int i) { bb.getNode(i) = this } } class GuardValue = Void; class Guard extends Void { - predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val) { none() } + predicate hasValueBranchEdge(Cfg::BasicBlock bb1, Cfg::BasicBlock bb2, GuardValue val) { + none() + } - predicate valueControlsBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val) { none() } + predicate valueControlsBranchEdge(Cfg::BasicBlock bb1, Cfg::BasicBlock bb2, GuardValue val) { + none() + } } - predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, GuardValue val) { none() } + predicate guardDirectlyControlsBlock(Guard guard, Cfg::BasicBlock bb, GuardValue val) { none() } predicate includeWriteDefsInFlowStep() { none() } diff --git a/shared/dataflow/qlpack.yml b/shared/dataflow/qlpack.yml index 20069450f22..7ad5bfbdfc3 100644 --- a/shared/dataflow/qlpack.yml +++ b/shared/dataflow/qlpack.yml @@ -3,6 +3,7 @@ version: 2.0.14-dev groups: shared library: true dependencies: + codeql/controlflow: ${workspace} codeql/ssa: ${workspace} codeql/typetracking: ${workspace} codeql/util: ${workspace} diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index df25142792d..20a447f91a9 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -5,44 +5,14 @@ overlay[local?] module; +private import codeql.controlflow.BasicBlock as BB private import codeql.util.Location private import codeql.util.Unit +signature class BasicBlockSig; + /** Provides the input specification of the SSA implementation. */ -signature module InputSig { - /** - * A basic block, that is, a maximal straight-line sequence of control flow nodes - * without branches or joins. - */ - class BasicBlock { - /** 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(); - - BasicBlock getASuccessor(); - - BasicBlock getImmediateDominator(); - - predicate inDominanceFrontier(BasicBlock df); - } - - /** 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(); - } - +signature module InputSig { /** A variable that can be SSA converted. */ class SourceVariable { /** Gets a textual representation of this variable. */ @@ -89,7 +59,10 @@ signature module InputSig { * NB: If this predicate is exposed, it should be cached. * ``` */ -module Make Input> { +module Make< + LocationSig Location, BB::CfgSig Cfg, InputSig Input> +{ + private import Cfg private import Input private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { result.getASuccessor() = bb } diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml index 30858c2f029..8a103c0ef10 100644 --- a/shared/ssa/qlpack.yml +++ b/shared/ssa/qlpack.yml @@ -3,5 +3,6 @@ version: 2.0.6-dev groups: shared library: true dependencies: + codeql/controlflow: ${workspace} codeql/util: ${workspace} warnOnImplicitThis: true From f459ddc40a1a25b6dfd5b226e3a1eb392906b39b Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 19 Aug 2025 10:34:06 +0200 Subject: [PATCH 4/9] Languages: Adapt to api changes. --- .../ir/dataflow/internal/DataFlowPrivate.qll | 22 ++--- .../code/cpp/ir/dataflow/internal/SsaImpl.qll | 19 ++-- .../ir/dataflow/internal/SsaImplCommon.qll | 20 ----- .../ir/implementation/aliased_ssa/IRBlock.qll | 40 +++++++++ .../ql/consistency-queries/CfgConsistency.ql | 4 +- .../code/csharp/controlflow/BasicBlocks.qll | 24 +++++- .../csharp/controlflow/ControlFlowElement.qll | 2 +- .../semmle/code/csharp/controlflow/Guards.qll | 6 +- .../controlflow/internal/PreBasicBlocks.qll | 86 +++++++++++++------ .../csharp/controlflow/internal/PreSsa.qll | 38 ++++---- .../csharp/controlflow/internal/Splitting.qll | 18 ++-- .../semmle/code/csharp/dataflow/Nullness.qll | 2 +- .../code/csharp/dataflow/internal/BaseSSA.qll | 15 ++-- .../dataflow/internal/DataFlowPrivate.qll | 20 ++--- .../code/csharp/dataflow/internal/SsaImpl.qll | 29 +++---- .../code/java/controlflow/BasicBlocks.qll | 18 ++++ .../code/java/dataflow/internal/BaseSSA.qll | 10 +-- .../dataflow/internal/DataFlowPrivate.qll | 24 +----- .../code/java/dataflow/internal/SsaImpl.qll | 10 +-- .../dataflow/internal/VariableCapture.qll | 34 +++----- .../dataflow/internal/sharedlib/Ssa.qll | 32 ++----- .../internal/BasicBlockInternal.qll | 45 ++++++++++ python/ql/lib/semmle/python/Flow.qll | 52 +++++++++++ .../dataflow/new/internal/VariableCapture.qll | 31 ++----- .../codeql/ruby/controlflow/BasicBlocks.qll | 19 ++++ .../dataflow/internal/DataFlowPrivate.qll | 18 ++-- .../codeql/ruby/dataflow/internal/SsaImpl.qll | 39 ++++----- .../codeql/rust/controlflow/BasicBlocks.qll | 17 +++- .../rust/dataflow/internal/DataFlowImpl.qll | 22 ++--- .../codeql/rust/dataflow/internal/SsaImpl.qll | 20 ++--- shared/controlflow/codeql/controlflow/Cfg.qll | 2 +- .../codeql/swift/controlflow/BasicBlocks.qll | 22 +++++ swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 33 +++---- .../dataflow/internal/DataFlowPrivate.qll | 29 +------ 34 files changed, 454 insertions(+), 368 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 3aa8994a449..582391e81cc 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 @@ -1880,9 +1880,7 @@ module IteratorFlow { } } - private module SsaInput implements SsaImpl::InputSig { - import Ssa::InputSigCommon - + private module SsaInput implements SsaImpl::InputSig { class SourceVariable = IteratorFlow::SourceVariable; /** A call to function that dereferences an iterator. */ @@ -1960,7 +1958,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(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite(IRCfg::BasicBlock bb, int i, SourceVariable v, boolean certain) { certain = false and exists(GetsIteratorCall beginCall, Instruction writeToDeref, IRBlock bbQual, int iQual | isIteratorStoreInstruction(beginCall, writeToDeref) and @@ -1971,12 +1969,12 @@ module IteratorFlow { } /** Holds if `(bb, i)` reads the container variable `v`. */ - predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead(IRCfg::BasicBlock bb, int i, SourceVariable v, boolean certain) { Ssa::variableRead(bb, i, v, certain) } } - private module IteratorSsa = SsaImpl::Make; + private module IteratorSsa = SsaImpl::Make; private module DataFlowIntegrationInput implements IteratorSsa::DataFlowIntegrationInputSig { private import codeql.util.Void @@ -1989,7 +1987,7 @@ module IteratorFlow { ) } - predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { bb.getInstruction(i) = this } + predicate hasCfgNode(IRCfg::BasicBlock bb, int i) { bb.getInstruction(i) = this } } predicate ssaDefHasSource(IteratorSsa::WriteDefinition def) { none() } @@ -1999,20 +1997,16 @@ module IteratorFlow { class GuardValue = Void; class Guard extends Void { - predicate hasValueBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue val - ) { + predicate hasValueBranchEdge(IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue val) { none() } - predicate valueControlsBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue val - ) { + predicate valueControlsBranchEdge(IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue val) { none() } } - predicate guardDirectlyControlsBlock(Guard guard, SsaInput::BasicBlock bb, GuardValue val) { + predicate guardDirectlyControlsBlock(Guard guard, IRCfg::BasicBlock bb, GuardValue val) { none() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll index 1cf45439d8b..d48a48dfb44 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll @@ -891,15 +891,14 @@ private predicate baseSourceVariableIsGlobal( ) } -private module SsaInput implements Ssa::InputSig { - import InputSigCommon +private module SsaInput implements Ssa::InputSig { import SourceVariables /** * 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(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite(IRCfg::BasicBlock bb, int i, SourceVariable v, boolean certain) { DataFlowImplCommon::forceCachingInSameStage() and ( exists(DefImpl def | def.hasIndexInBlock(v, bb, i) | @@ -917,7 +916,7 @@ private module SsaInput implements Ssa::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(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead(IRCfg::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 ) @@ -965,7 +964,7 @@ class GlobalDef extends Definition { GlobalLikeVariable getVariable() { result = impl.getVariable() } } -private module SsaImpl = Ssa::Make; +private module SsaImpl = Ssa::Make; private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationInputSig { private import codeql.util.Boolean @@ -978,7 +977,7 @@ private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationI ) } - predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { bb.getInstruction(i) = this } + predicate hasCfgNode(IRCfg::BasicBlock bb, int i) { bb.getInstruction(i) = this } } Expr getARead(SsaImpl::Definition def) { @@ -1006,9 +1005,7 @@ private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationI class Guard instanceof IRGuards::IRGuardCondition { string toString() { result = super.toString() } - predicate hasValueBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch - ) { + predicate hasValueBranchEdge(IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue branch) { exists(EdgeKind kind | super.getBlock() = bb1 and kind = getConditionalEdge(branch) and @@ -1017,13 +1014,13 @@ private module DataFlowIntegrationInput implements SsaImpl::DataFlowIntegrationI } predicate valueControlsBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch + IRCfg::BasicBlock bb1, IRCfg::BasicBlock bb2, GuardValue branch ) { this.hasValueBranchEdge(bb1, bb2, branch) } } - predicate guardDirectlyControlsBlock(Guard guard, SsaInput::BasicBlock bb, GuardValue branch) { + predicate guardDirectlyControlsBlock(Guard guard, IRCfg::BasicBlock bb, GuardValue branch) { guard.(IRGuards::IRGuardCondition).controls(bb, branch) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll index d86e1ec613f..552f504b84b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll @@ -768,23 +768,3 @@ private module Cached { } import Cached - -/** - * Inputs to the shared SSA library's parameterized module that is shared - * between the SSA pruning stage, and the final SSA stage. - */ -module InputSigCommon { - class BasicBlock extends IRBlock { - ControlFlowNode getNode(int i) { result = this.getInstruction(i) } - - int length() { result = this.getInstructionCount() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result.immediatelyDominates(this) } - - predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df } - } - - class ControlFlowNode = Instruction; -} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index 50395db47e7..58bd5b0a8cb 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -7,6 +7,7 @@ import Instruction private import internal.IRBlockImports as Imports import Imports::EdgeKind private import Cached +private import codeql.controlflow.BasicBlock as BB /** * Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the @@ -263,6 +264,45 @@ private predicate isEntryBlock(TIRBlock block) { block = MkIRBlock(any(EnterFunctionInstruction enter)) } +module IRCfg implements BB::CfgSig { + class ControlFlowNode = Instruction; + + class SuccessorType = EdgeKind; + + class BasicBlock extends IRBlock { + ControlFlowNode getNode(int i) { result = this.getInstruction(i) } + + ControlFlowNode getLastNode() { result = super.getLastInstruction() } + + int length() { result = this.getInstructionCount() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getASuccessor(SuccessorType t) { result = super.getSuccessor(t) } + + predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) } + + predicate dominates(BasicBlock bb) { super.dominates(bb) } + + BasicBlock getImmediateDominator() { result.immediatelyDominates(this) } + + predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df } + + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) } + + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } + } + + pragma[nomagic] + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + bb1.getASuccessor() = bb2 and + bb1 = bb2.getImmediateDominator() and + forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) + } + + predicate entryBlock(BasicBlock bb) { isEntryBlock(bb) } +} + cached private module Cached { cached diff --git a/csharp/ql/consistency-queries/CfgConsistency.ql b/csharp/ql/consistency-queries/CfgConsistency.ql index 5fb52b3b09e..d7f0c091538 100644 --- a/csharp/ql/consistency-queries/CfgConsistency.ql +++ b/csharp/ql/consistency-queries/CfgConsistency.ql @@ -29,7 +29,7 @@ predicate bbSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) succBB.getFirstNode() = succ.getAControlFlowNode() ) and not exists(PreBasicBlock predBB, PreBasicBlock succBB | - predBB.getLastElement() = pred and + predBB.getLastNode() = pred and succBB = predBB.getASuccessor() and succBB.getFirstElement() = succ ) @@ -41,7 +41,7 @@ predicate bbIntraSuccInconsistency(ControlFlowElement pred, ControlFlowElement s succ.getAControlFlowNode() = bb.getNode(i + 1) ) and not exists(PreBasicBlock bb | - bb.getLastElement() = pred and + bb.getLastNode() = pred and bb.getASuccessor().getFirstElement() = succ ) and not exists(PreBasicBlock bb, int i | diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll index 7079f2cb0a0..8271b0ac484 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -6,6 +6,7 @@ import csharp private import ControlFlow::SuccessorTypes private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as CfgImpl private import CfgImpl::BasicBlocks as BasicBlocksImpl +private import codeql.controlflow.BasicBlock as BB /** * A basic block, that is, a maximal straight-line sequence of control flow nodes @@ -13,7 +14,12 @@ private import CfgImpl::BasicBlocks as BasicBlocksImpl */ final class BasicBlock extends BasicBlocksImpl::BasicBlock { /** Gets an immediate successor of this basic block of a given type, if any. */ - BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) { result = this.getASuccessor(t) } + BasicBlock getASuccessor(ControlFlow::SuccessorType t) { result = super.getASuccessor(t) } + + /** DEPRECATED: Use `getASuccessor` instead. */ + deprecated BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) { + result = this.getASuccessor(t) + } /** Gets an immediate predecessor of this basic block of a given type, if any. */ BasicBlock getAPredecessorByType(ControlFlow::SuccessorType t) { @@ -332,3 +338,19 @@ final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBl super.edgeDominates(controlled, s) } } + +private class BasicBlockAlias = BasicBlock; + +module Cfg implements BB::CfgSig { + class ControlFlowNode = ControlFlow::Node; + + class SuccessorType = ControlFlow::SuccessorType; + + class BasicBlock = BasicBlockAlias; + + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + BasicBlocksImpl::dominatingEdge(bb1, bb2) + } + + predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } +} diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll index 5649f43b881..1cd09725b6e 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll @@ -90,7 +90,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { // all other nodes can use regular CFG dominance this instanceof Impl::SplitAstNode and cb.getLastNode() = this.getAControlFlowNode() and - succ = cb.getASuccessorByType(s) + succ = cb.getASuccessor(s) } pragma[noinline] diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll index 313fabc0235..2688472f71c 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll @@ -999,7 +999,7 @@ module Internal { pragma[nomagic] private predicate preControlsDirect(Guard g, PreBasicBlocks::PreBasicBlock bb, AbstractValue v) { exists(PreBasicBlocks::ConditionBlock cb, ConditionalSuccessor s | cb.controls(bb, s) | - v.branch(cb.getLastElement(), s, g) + v.branch(cb.getLastNode(), s, g) ) } @@ -1146,9 +1146,9 @@ module Internal { /** Gets the successor block that is reached when guard `g` has abstract value `v`. */ private PreBasicBlocks::PreBasicBlock getConditionalSuccessor(Guard g, AbstractValue v) { exists(PreBasicBlocks::ConditionBlock pred, ConditionalSuccessor s | - v.branch(pred.getLastElement(), s, g) + v.branch(pred.getLastNode(), s, g) | - result = pred.getASuccessorByType(s) + result = pred.getASuccessor(s) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll index 3e6bf7f1f76..774aa9958c8 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll @@ -13,6 +13,7 @@ import csharp private import Completion private import ControlFlowGraphImpl private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg +private import codeql.controlflow.BasicBlock as BB private predicate startsBB(ControlFlowElement cfe) { not succ(_, cfe, _) and @@ -55,11 +56,15 @@ private predicate bbIDominates(PreBasicBlock dom, PreBasicBlock bb) = class PreBasicBlock extends ControlFlowElement { PreBasicBlock() { startsBB(this) } - PreBasicBlock getASuccessorByType(Cfg::SuccessorType t) { - succ(this.getLastElement(), result, any(Completion c | t = c.getAMatchingSuccessorType())) + PreBasicBlock getASuccessor(Cfg::SuccessorType t) { + succ(this.getLastNode(), result, any(Completion c | t = c.getAMatchingSuccessorType())) } - PreBasicBlock getASuccessor() { result = this.getASuccessorByType(_) } + deprecated PreBasicBlock getASuccessorByType(Cfg::SuccessorType t) { + result = this.getASuccessor(t) + } + + PreBasicBlock getASuccessor() { result = this.getASuccessor(_) } PreBasicBlock getAPredecessor() { result.getASuccessor() = this } @@ -71,7 +76,9 @@ class PreBasicBlock extends ControlFlowElement { ControlFlowElement getFirstElement() { result = this } - ControlFlowElement getLastElement() { result = this.getNode(this.length() - 1) } + ControlFlowElement getLastNode() { result = this.getNode(this.length() - 1) } + + deprecated ControlFlowElement getLastElement() { result = this.getLastNode() } int length() { result = strictcount(this.getAnElement()) } @@ -97,45 +104,72 @@ class PreBasicBlock extends ControlFlowElement { not bbIDominates(this, df) ) } + + /** Unsupported. Do not use. */ + predicate strictlyPostDominates(PreBasicBlock bb) { none() } + + /** Unsupported. Do not use. */ + predicate postDominates(PreBasicBlock bb) { + this.strictlyPostDominates(bb) or + this = bb + } } private Completion getConditionalCompletion(ConditionalCompletion cc) { result.getInnerCompletion() = cc } +pragma[nomagic] +private predicate conditionBlockImmediatelyControls( + ConditionBlock cond, PreBasicBlock succ, ConditionalCompletion cc +) { + exists(ControlFlowElement last, Completion c | + last = cond.getLastNode() and + c = getConditionalCompletion(cc) and + succ(last, succ, c) and + // In the pre-CFG, we need to account for case where one predecessor node has + // two edges to the same successor node. Assertion expressions are examples of + // such nodes. + not exists(Completion other | + succ(last, succ, other) and + other != c + ) and + forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != cond | + succ.dominates(pred) + ) + ) +} + class ConditionBlock extends PreBasicBlock { ConditionBlock() { exists(Completion c | c = getConditionalCompletion(_) | - succ(this.getLastElement(), _, c) + succ(this.getLastNode(), _, c) or - scopeLast(_, this.getLastElement(), c) - ) - } - - pragma[nomagic] - private predicate immediatelyControls(PreBasicBlock succ, ConditionalCompletion cc) { - exists(ControlFlowElement last, Completion c | - last = this.getLastElement() and - c = getConditionalCompletion(cc) and - succ(last, succ, c) and - // In the pre-CFG, we need to account for case where one predecessor node has - // two edges to the same successor node. Assertion expressions are examples of - // such nodes. - not exists(Completion other | - succ(last, succ, other) and - other != c - ) and - forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != this | - succ.dominates(pred) - ) + scopeLast(_, this.getLastNode(), c) ) } pragma[nomagic] predicate controls(PreBasicBlock controlled, Cfg::SuccessorTypes::ConditionalSuccessor s) { - exists(PreBasicBlock succ, ConditionalCompletion c | this.immediatelyControls(succ, c) | + exists(PreBasicBlock succ, ConditionalCompletion c | + conditionBlockImmediatelyControls(this, succ, c) + | succ.dominates(controlled) and s = c.getAMatchingSuccessorType() ) } } + +module PreCfg implements BB::CfgSig { + class ControlFlowNode = ControlFlowElement; + + class SuccessorType = Cfg::SuccessorType; + + class BasicBlock = PreBasicBlock; + + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + conditionBlockImmediatelyControls(bb1, bb2, _) + } + + predicate entryBlock(BasicBlock bb) { entryBB(bb) } +} 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 9205974853e..ce9fdd406fd 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreSsa.qll @@ -12,7 +12,7 @@ module PreSsa { private import codeql.ssa.Ssa as SsaImplCommon private predicate definitionAt( - AssignableDefinition def, SsaInput::BasicBlock bb, int i, SsaInput::SourceVariable v + AssignableDefinition def, PreBasicBlocks::PreBasicBlock bb, int i, SsaInput::SourceVariable v ) { bb.getNode(i) = def.getExpr() and v = def.getTarget() and @@ -30,7 +30,9 @@ module PreSsa { ) } - predicate implicitEntryDef(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v) { + predicate implicitEntryDef( + Callable c, PreBasicBlocks::PreBasicBlock bb, SsaInput::SourceVariable v + ) { c = v.getACallable() and scopeFirst(c, bb) and ( @@ -79,13 +81,9 @@ module PreSsa { } } - module SsaInput implements SsaImplCommon::InputSig { - class BasicBlock = PreBasicBlocks::PreBasicBlock; - - class ControlFlowNode = ControlFlowElement; - - private class ExitBasicBlock extends BasicBlock { - ExitBasicBlock() { scopeLast(_, this.getLastElement(), _) } + module SsaInput implements SsaImplCommon::InputSig { + private class ExitBasicBlock extends PreBasicBlocks::PreBasicBlock { + ExitBasicBlock() { scopeLast(_, this.getLastNode(), _) } } pragma[noinline] @@ -123,7 +121,9 @@ module PreSsa { Callable getACallable() { result = c } } - predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite( + PreBasicBlocks::PreBasicBlock bb, int i, SourceVariable v, boolean certain + ) { exists(AssignableDefinition def | definitionAt(def, bb, i, v) and if def.getTargetAccess().isRefArgument() then certain = false else certain = true @@ -134,7 +134,9 @@ module PreSsa { certain = true } - predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead( + PreBasicBlocks::PreBasicBlock bb, int i, SourceVariable v, boolean certain + ) { exists(AssignableRead read | read = bb.getNode(i) and read.getTarget() = v and @@ -151,25 +153,25 @@ module PreSsa { } } - private module SsaImpl = SsaImplCommon::Make; + private module SsaImpl = SsaImplCommon::Make; class Definition extends SsaImpl::Definition { final AssignableRead getARead() { - exists(SsaInput::BasicBlock bb, int i | + exists(PreBasicBlocks::PreBasicBlock bb, int i | SsaImpl::ssaDefReachesRead(_, this, bb, i) and result = bb.getNode(i) ) } final AssignableDefinition getDefinition() { - exists(SsaInput::BasicBlock bb, int i, SsaInput::SourceVariable v | + exists(PreBasicBlocks::PreBasicBlock bb, int i, SsaInput::SourceVariable v | this.definesAt(v, bb, i) and definitionAt(result, bb, i, v) ) } final AssignableRead getAFirstRead() { - exists(SsaInput::BasicBlock bb, int i | + exists(PreBasicBlocks::PreBasicBlock bb, int i | SsaImpl::firstUse(this, bb, i, true) and result = bb.getNode(i) ) @@ -185,14 +187,14 @@ module PreSsa { not result instanceof PhiNode } - final predicate isLiveAtEndOfBlock(SsaInput::BasicBlock bb) { + final predicate isLiveAtEndOfBlock(PreBasicBlocks::PreBasicBlock bb) { SsaImpl::ssaDefReachesEndOfBlock(bb, this, _) } override Location getLocation() { result = this.getDefinition().getLocation() or - exists(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v | + exists(Callable c, PreBasicBlocks::PreBasicBlock bb, SsaInput::SourceVariable v | this.definesAt(v, bb, -1) and implicitEntryDef(c, bb, v) and result = c.getLocation() @@ -207,7 +209,7 @@ module PreSsa { } predicate adjacentReadPairSameVar(AssignableRead read1, AssignableRead read2) { - exists(SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 | + exists(PreBasicBlocks::PreBasicBlock bb1, int i1, PreBasicBlocks::PreBasicBlock bb2, int i2 | read1 = bb1.getNode(i1) and SsaImpl::adjacentUseUse(bb1, i1, bb2, i2, _, true) and read2 = bb2.getNode(i2) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll index b6efdfcf1ea..f57dd0703fc 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll @@ -899,7 +899,7 @@ module BooleanSplitting { /** Holds if control flow element `cfe` starts a split of this kind. */ predicate startsSplit(AstNode cfe) { - this.correlatesConditions(any(ConditionBlock cb | cb.getLastElement() = cfe), _, _) + this.correlatesConditions(any(ConditionBlock cb | cb.getLastNode() = cfe), _, _) } /** @@ -941,7 +941,7 @@ module BooleanSplitting { /** * Holds if condition `cb` is a read of the SSA variable in this split. */ - private predicate defCondition(ConditionBlock cb) { cb.getLastElement() = def.getARead() } + private predicate defCondition(ConditionBlock cb) { cb.getLastNode() = def.getARead() } /** * Holds if condition `cb` is a read of the SSA variable in this split, @@ -950,7 +950,7 @@ module BooleanSplitting { */ private predicate defConditionReachableFromRead(ConditionBlock cb, AssignableRead read) { this.defCondition(cb) and - read = cb.getLastElement() + read = cb.getLastNode() or exists(AssignableRead mid | this.defConditionReachableFromRead(cb, mid) | PreSsa::adjacentReadPairSameVar(read, mid) and @@ -970,9 +970,9 @@ module BooleanSplitting { override predicate correlatesConditions(ConditionBlock cb1, ConditionBlock cb2, boolean inverted) { this.firstDefCondition(cb1) and exists(AssignableRead read1, AssignableRead read2 | - read1 = cb1.getLastElement() and + read1 = cb1.getLastNode() and PreSsa::adjacentReadPairSameVar+(read1, read2) and - read2 = cb2.getLastElement() and + read2 = cb2.getLastNode() and inverted = false ) } @@ -1088,7 +1088,7 @@ module BooleanSplitting { */ private predicate appliesToBlock(PreBasicBlock bb, Completion c) { this.appliesTo(bb) and - exists(AstNode last | last = bb.getLastElement() | + exists(AstNode last | last = bb.getLastNode() | (succ(last, _, c) or scopeLast(_, last, c)) and // Respect the value recorded in this split for all correlated conditions forall(boolean inverted | bb = this.getACorrelatedCondition(inverted) | @@ -1102,7 +1102,7 @@ module BooleanSplitting { override predicate hasExit(AstNode pred, AstNode succ, Completion c) { exists(PreBasicBlock bb | this.appliesToBlock(bb, c) | - pred = bb.getLastElement() and + pred = bb.getLastNode() and succ(pred, succ, c) and // Exit this split if we can no longer reach a correlated condition not super.getSubKind().canReachCorrelatedCondition(succ) @@ -1111,7 +1111,7 @@ module BooleanSplitting { override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { exists(PreBasicBlock bb | this.appliesToBlock(bb, c) | - last = bb.getLastElement() and + last = bb.getLastNode() and scopeLast(scope, last, c) ) } @@ -1121,7 +1121,7 @@ module BooleanSplitting { pred = bb.getAnElement() and this.appliesSucc(pred, succ, c) and ( - pred = bb.getLastElement() + pred = bb.getLastNode() implies ( // We must still be able to reach a correlated condition to stay in this split diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll index 7e8ed0aadc0..35241877995 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll @@ -297,7 +297,7 @@ private predicate defNullImpliesStep( not exists(SuccessorTypes::ConditionalSuccessor s, NullValue nv | bb1.getLastNode() = getANullCheck(def1, s, nv).getAControlFlowNode() | - bb2 = bb1.getASuccessorByType(s) and + bb2 = bb1.getASuccessor(s) and nv.isNonNull() ) } 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 dc056e2f0eb..6e2d7d239f7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll @@ -5,6 +5,7 @@ import csharp */ module BaseSsa { private import AssignableDefinitions + private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks private import codeql.ssa.Ssa as SsaImplCommon /** @@ -42,16 +43,12 @@ module BaseSsa { ) } - private module SsaInput implements SsaImplCommon::InputSig { + private module SsaInput implements SsaImplCommon::InputSig { private import semmle.code.csharp.controlflow.internal.PreSsa - class BasicBlock = ControlFlow::BasicBlock; - - class ControlFlowNode = ControlFlow::Node; - class SourceVariable = PreSsa::SimpleLocalScopeVariable; - predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite(ControlFlow::BasicBlock bb, int i, SourceVariable v, boolean certain) { exists(AssignableDefinition def | definitionAt(def, bb, i, v) and if def.isCertain() then certain = true else certain = false @@ -62,7 +59,7 @@ module BaseSsa { certain = true } - predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead(ControlFlow::BasicBlock bb, int i, SourceVariable v, boolean certain) { exists(AssignableRead read | read.getAControlFlowNode() = bb.getNode(i) and read.getTarget() = v and @@ -71,7 +68,7 @@ module BaseSsa { } } - private module SsaImpl = SsaImplCommon::Make; + private module SsaImpl = SsaImplCommon::Make; class Definition extends SsaImpl::Definition { final AssignableRead getARead() { @@ -108,7 +105,7 @@ module BaseSsa { override Location getLocation() { result = this.getDefinition().getLocation() or - exists(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v | + exists(Callable c, ControlFlow::BasicBlock bb, SsaInput::SourceVariable v | this.definesAt(v, bb, -1) and implicitEntryDef(c, bb, v) and result = c.getLocation() 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 c5e96e46450..95d76059f76 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -264,6 +264,7 @@ predicate hasNodePath(ControlFlowReachabilityConfiguration conf, ExprNode n1, No /** Provides logic related to captured variables. */ module VariableCapture { private import codeql.dataflow.VariableCapture as Shared + private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) { e1 = LocalFlow::getALastEvalNode(e2) @@ -275,24 +276,15 @@ module VariableCapture { ) } - private module CaptureInput implements Shared::InputSig { + private module CaptureInput implements Shared::InputSig { private import csharp as Cs private import semmle.code.csharp.controlflow.ControlFlowGraph as Cfg - private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks private import TaintTrackingPrivate as TaintTrackingPrivate - class BasicBlock extends BasicBlocks::BasicBlock { - Callable getEnclosingCallable() { result = super.getCallable() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } + Callable basicBlockGetEnclosingCallable(BasicBlocks::BasicBlock bb) { + result = bb.getCallable() } - class ControlFlowNode = Cfg::ControlFlow::Node; - private predicate thisAccess(ControlFlow::Node cfn, InstanceCallable c) { ThisFlow::thisAccessExpr(cfn.getAstNode()) and cfn.getEnclosingCallable().getEnclosingCallable*() = c @@ -359,7 +351,7 @@ module VariableCapture { } class Expr extends ControlFlow::Node { - predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(BasicBlocks::BasicBlock bb, int i) { this = bb.getNode(i) } } class VariableWrite extends Expr { @@ -411,7 +403,7 @@ module VariableCapture { class ClosureExpr = CaptureInput::ClosureExpr; - module Flow = Shared::Flow; + module Flow = Shared::Flow; private Flow::ClosureNode asClosureNode(Node n) { result = n.(CaptureNode).getSynthesizedCaptureNode() 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 dd77506ed68..7fbd179ace0 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -6,13 +6,10 @@ import csharp private import codeql.ssa.Ssa as SsaImplCommon private import AssignableDefinitions private import semmle.code.csharp.controlflow.internal.PreSsa +private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks private import semmle.code.csharp.controlflow.Guards as Guards -private module SsaInput implements SsaImplCommon::InputSig { - class BasicBlock = ControlFlow::BasicBlock; - - class ControlFlowNode = ControlFlow::Node; - +private module SsaInput implements SsaImplCommon::InputSig { class SourceVariable = Ssa::SourceVariable; /** @@ -21,7 +18,7 @@ private module SsaInput implements SsaImplCommon::InputSig { * * This includes implicit writes via calls. */ - predicate variableWrite(BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { + predicate variableWrite(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { variableWriteDirect(bb, i, v, certain) or variableWriteQualifier(bb, i, v, certain) @@ -35,7 +32,7 @@ private module SsaInput implements SsaImplCommon::InputSig { * * This includes implicit reads via calls. */ - predicate variableRead(BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { + predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) { variableReadActual(bb, i, v) and certain = true or @@ -44,7 +41,7 @@ private module SsaInput implements SsaImplCommon::InputSig { } } -import SsaImplCommon::Make as Impl +import SsaImplCommon::Make as Impl class Definition = Impl::Definition; @@ -725,7 +722,7 @@ private predicate variableReadPseudo(ControlFlow::BasicBlock bb, int i, Ssa::Sou pragma[noinline] deprecated private predicate adjacentDefRead( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2, + Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2, SsaInput::SourceVariable v ) { Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and @@ -733,8 +730,8 @@ deprecated private predicate adjacentDefRead( } deprecated private predicate adjacentDefReachesRead( - Definition def, SsaInput::SourceVariable v, SsaInput::BasicBlock bb1, int i1, - SsaInput::BasicBlock bb2, int i2 + Definition def, SsaInput::SourceVariable v, ControlFlow::BasicBlock bb1, int i1, + ControlFlow::BasicBlock bb2, int i2 ) { adjacentDefRead(def, bb1, i1, bb2, i2, v) and ( @@ -743,7 +740,7 @@ deprecated private predicate adjacentDefReachesRead( SsaInput::variableRead(bb1, i1, v, true) ) or - exists(SsaInput::BasicBlock bb3, int i3 | + exists(ControlFlow::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) @@ -751,7 +748,7 @@ deprecated private predicate adjacentDefReachesRead( } deprecated private predicate adjacentDefReachesUncertainRead( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 + Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2 ) { exists(SsaInput::SourceVariable v | adjacentDefReachesRead(def, v, bb1, i1, bb2, i2) and @@ -762,12 +759,12 @@ deprecated private predicate adjacentDefReachesUncertainRead( /** Same as `lastRefRedef`, but skips uncertain reads. */ pragma[nomagic] deprecated private predicate lastRefSkipUncertainReads( - Definition def, SsaInput::BasicBlock bb, int i + Definition def, ControlFlow::BasicBlock bb, int i ) { Impl::lastRef(def, bb, i) and not SsaInput::variableRead(bb, i, def.getSourceVariable(), false) or - exists(SsaInput::BasicBlock bb0, int i0 | + exists(ControlFlow::BasicBlock bb0, int i0 | Impl::lastRef(def, bb0, i0) and adjacentDefReachesUncertainRead(def, bb, i, bb0, i0) ) @@ -1050,7 +1047,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue branch) { exists(ControlFlow::SuccessorTypes::ConditionalSuccessor s | this.getAControlFlowNode() = bb1.getLastNode() and - bb2 = bb1.getASuccessorByType(s) and + bb2 = bb1.getASuccessor(s) and s.getValue() = branch ) } diff --git a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll index b7e52f658fd..9054debb04e 100644 --- a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll +++ b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll @@ -104,6 +104,10 @@ class BasicBlock extends BbImpl::BasicBlock { predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) } + + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } + /** * DEPRECATED: Use `getASuccessor` instead. * @@ -151,3 +155,17 @@ class BasicBlock extends BbImpl::BasicBlock { class ExitBlock extends BasicBlock { ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode } } + +private class BasicBlockAlias = BasicBlock; + +module Cfg implements BB::CfgSig { + class ControlFlowNode = BbImpl::ControlFlowNode; + + class SuccessorType = BbImpl::SuccessorType; + + class BasicBlock = BasicBlockAlias; + + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BbImpl::dominatingEdge(bb1, bb2) } + + predicate entryBlock(BasicBlock bb) { BbImpl::entryBlock(bb) } +} 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 444d8f3cbd9..e50581c7e14 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll @@ -157,13 +157,7 @@ private module BaseSsaImpl { private import BaseSsaImpl -private module SsaInput implements SsaImplCommon::InputSig { - private import java as J - - class BasicBlock = J::BasicBlock; - - class ControlFlowNode = J::ControlFlowNode; - +private module SsaInput implements SsaImplCommon::InputSig { class SourceVariable = BaseSsaSourceVariable; /** @@ -195,7 +189,7 @@ private module SsaInput implements SsaImplCommon::InputSig { } } -private module Impl = SsaImplCommon::Make; +private module Impl = SsaImplCommon::Make; private import Cached 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 22b5cc62d57..1721569e45a 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -69,28 +69,10 @@ private predicate closureFlowStep(Expr e1, Expr e2) { ) } -private module CaptureInput implements VariableCapture::InputSig { +private module CaptureInput implements VariableCapture::InputSig { private import java as J - 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() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } - } - - class ControlFlowNode = J::ControlFlowNode; + Callable basicBlockGetEnclosingCallable(BasicBlock bb) { result = bb.getEnclosingCallable() } //TODO: support capture of `this` in lambdas class CapturedVariable instanceof LocalScopeVariable { @@ -165,7 +147,7 @@ class CapturedVariable = CaptureInput::CapturedVariable; class CapturedParameter = CaptureInput::CapturedParameter; -module CaptureFlow = VariableCapture::Flow; +module CaptureFlow = VariableCapture::Flow; private CaptureFlow::ClosureNode asClosureNode(Node n) { result = n.(CaptureNode).getSynthesizedCaptureNode() 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 20e7fc02336..dc2cb7e7d00 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll @@ -166,13 +166,7 @@ private predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, Basic uncertainVariableUpdate(v.getQualifier(), n, b, i) } -private module SsaInput implements SsaImplCommon::InputSig { - private import java as J - - class BasicBlock = J::BasicBlock; - - class ControlFlowNode = J::ControlFlowNode; - +private module SsaInput implements SsaImplCommon::InputSig { class SourceVariable = SsaSourceVariable; /** @@ -214,7 +208,7 @@ private module SsaInput implements SsaImplCommon::InputSig { } } -import SsaImplCommon::Make as Impl +import SsaImplCommon::Make as Impl final class Definition = Impl::Definition; diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll index 7881f487e9b..9e49b41052e 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableCapture.qll @@ -4,7 +4,7 @@ private import semmle.javascript.dataflow.internal.VariableOrThis private import codeql.dataflow.VariableCapture private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon -module VariableCaptureConfig implements InputSig { +module VariableCaptureConfig implements InputSig { private js::Function getLambdaFromVariable(js::LocalVariable variable) { result.getVariable() = variable or @@ -106,20 +106,8 @@ module VariableCaptureConfig implements InputSig { ) } - class ControlFlowNode = js::ControlFlowNode; - - final private class JsBasicBlock = js::BasicBlock; - - class BasicBlock extends JsBasicBlock { - Callable getEnclosingCallable() { result = this.getContainer().getFunctionBoundary() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { - df.(js::ReachableJoinBlock).inDominanceFrontierOf(this) - } + Callable basicBlockGetEnclosingCallable(js::Cfg::BasicBlock bb) { + result = bb.getContainer().getFunctionBoundary() } class Callable extends js::StmtContainer { @@ -135,7 +123,7 @@ module VariableCaptureConfig implements InputSig { class Expr extends js::AST::ValueNode { /** Holds if the `i`th node of basic block `bb` evaluates this expression. */ - predicate hasCfgNode(BasicBlock bb, int i) { + predicate hasCfgNode(js::Cfg::BasicBlock bb, int i) { // Note: this is overridden for FunctionDeclStmt bb.getNode(i) = this } @@ -180,7 +168,7 @@ module VariableCaptureConfig implements InputSig { js::Location getLocation() { none() } // Overridden in subclass - predicate hasCfgNode(BasicBlock bb, int i) { none() } // Overridden in subclass + predicate hasCfgNode(js::Cfg::BasicBlock bb, int i) { none() } // Overridden in subclass // note: langauge-specific js::DataFlow::Node getSource() { none() } // Overridden in subclass @@ -217,7 +205,7 @@ module VariableCaptureConfig implements InputSig { } /** Holds if the `i`th node of basic block `bb` evaluates this expression. */ - override predicate hasCfgNode(BasicBlock bb, int i) { + override predicate hasCfgNode(js::Cfg::BasicBlock bb, int i) { bb.getNode(i) = this.getCfgNodeOverride() or not exists(this.getCfgNodeOverride()) and @@ -236,7 +224,7 @@ module VariableCaptureConfig implements InputSig { override CapturedVariable getVariable() { result = variable } - override predicate hasCfgNode(BasicBlock bb, int i) { + override predicate hasCfgNode(js::Cfg::BasicBlock bb, int i) { // 'i' would normally be bound to 0, but we lower it to -1 so FunctionDeclStmts can be evaluated // at index 0. any(js::SsaImplicitInit def).definesAt(bb, _, variable.asLocalVariable()) and i = -1 @@ -244,11 +232,9 @@ module VariableCaptureConfig implements InputSig { bb.(js::EntryBasicBlock).getContainer() = variable.asThisContainer() and i = -1 } } - - predicate entryBlock(BasicBlock bb) { bb instanceof js::EntryBasicBlock } } -module VariableCaptureOutput = Flow; +module VariableCaptureOutput = Flow; js::DataFlow::Node getNodeFromClosureNode(VariableCaptureOutput::ClosureNode node) { result = TValueNode(node.(VariableCaptureOutput::ExprNode).getExpr()) @@ -294,9 +280,9 @@ private module Debug { relevantContainer(node1.getContainer()) } - predicate readBB(VariableRead read, BasicBlock bb, int i) { read.hasCfgNode(bb, i) } + predicate readBB(VariableRead read, js::Cfg::BasicBlock bb, int i) { read.hasCfgNode(bb, i) } - predicate writeBB(VariableWrite write, BasicBlock bb, int i) { write.hasCfgNode(bb, i) } + predicate writeBB(VariableWrite write, js::Cfg::BasicBlock bb, int i) { write.hasCfgNode(bb, i) } int captureDegree(js::Function fun) { result = strictcount(CapturedVariable v | captures(fun, v)) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll index 2d672468946..7e610c3c23c 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll @@ -9,21 +9,7 @@ private import codeql.ssa.Ssa private import semmle.javascript.internal.BasicBlockInternal as BasicBlockInternal private import semmle.javascript.dataflow.internal.VariableOrThis -module SsaConfig implements InputSig { - class ControlFlowNode = js::ControlFlowNode; - - final private class JsBasicBlock = js::BasicBlock; - - class BasicBlock extends JsBasicBlock { - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { - df.(js::ReachableJoinBlock).inDominanceFrontierOf(this) - } - } - +module SsaConfig implements InputSig { class SourceVariable extends LocalVariableOrThis { SourceVariable() { not this.isCaptured() } } @@ -33,7 +19,7 @@ module SsaConfig implements InputSig { result.getContainer() = container } - predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite(js::Cfg::BasicBlock bb, int i, SourceVariable v, boolean certain) { certain = true and ( bb.defAt(i, v.asLocalVariable(), _) @@ -44,7 +30,7 @@ module SsaConfig implements InputSig { ) } - predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead(js::Cfg::BasicBlock bb, int i, SourceVariable v, boolean certain) { bb.useAt(i, v.asLocalVariable(), _) and certain = true or certain = true and @@ -52,7 +38,7 @@ module SsaConfig implements InputSig { } } -import Make +import Make module SsaDataflowInput implements DataFlowIntegrationInputSig { private import codeql.util.Boolean @@ -60,7 +46,7 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { class Expr extends js::ControlFlowNode { Expr() { this = any(SsaConfig::SourceVariable v).getAUse() } - predicate hasCfgNode(SsaConfig::BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(js::Cfg::BasicBlock bb, int i) { this = bb.getNode(i) } } predicate ssaDefHasSource(WriteDefinition def) { @@ -87,9 +73,7 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { * Holds if the evaluation of this guard to `branch` corresponds to the edge * from `bb1` to `bb2`. */ - predicate hasValueBranchEdge( - SsaConfig::BasicBlock bb1, SsaConfig::BasicBlock bb2, GuardValue branch - ) { + predicate hasValueBranchEdge(js::Cfg::BasicBlock bb1, js::Cfg::BasicBlock bb2, GuardValue branch) { exists(js::ConditionGuardNode g | g.getTest() = this and bb1 = this.getBasicBlock() and @@ -104,14 +88,14 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { * `bb1` to `bb2` implies that this guard evaluated to `branch`. */ predicate valueControlsBranchEdge( - SsaConfig::BasicBlock bb1, SsaConfig::BasicBlock bb2, GuardValue branch + js::Cfg::BasicBlock bb1, js::Cfg::BasicBlock bb2, GuardValue branch ) { this.hasValueBranchEdge(bb1, bb2, branch) } } pragma[inline] - predicate guardDirectlyControlsBlock(Guard guard, SsaConfig::BasicBlock bb, GuardValue branch) { + predicate guardDirectlyControlsBlock(Guard guard, js::Cfg::BasicBlock bb, GuardValue branch) { exists(js::ConditionGuardNode g | g.getTest() = guard and g.dominates(bb) and diff --git a/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll b/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll index 3d71310ee36..7b9c0b4f841 100644 --- a/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll +++ b/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll @@ -6,6 +6,7 @@ import javascript private import semmle.javascript.internal.StmtContainers private import semmle.javascript.internal.CachedStages +private import codeql.controlflow.BasicBlock as BB /** * Holds if `nd` starts a new basic block. @@ -366,4 +367,48 @@ module Public { ) } } + + final private class FinalBasicBlock = BasicBlock; + + module Cfg implements BB::CfgSig { + private import javascript as Js + private import codeql.util.Unit + + class ControlFlowNode = Js::ControlFlowNode; + + class SuccessorType = Unit; + + class BasicBlock extends FinalBasicBlock { + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor() and exists(t) } + + predicate strictlyDominates(BasicBlock bb) { + this.(ReachableBasicBlock).strictlyDominates(bb) + } + + predicate dominates(BasicBlock bb) { this.(ReachableBasicBlock).dominates(bb) } + + predicate inDominanceFrontier(BasicBlock df) { + df.(ReachableJoinBlock).inDominanceFrontierOf(this) + } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + predicate strictlyPostDominates(BasicBlock bb) { + this.(ReachableBasicBlock).strictlyPostDominates(bb) + } + + predicate postDominates(BasicBlock bb) { this.(ReachableBasicBlock).postDominates(bb) } + } + + pragma[nomagic] + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + bb1.getASuccessor() = bb2 and + bb1 = bb2.getImmediateDominator() and + forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) + } + + predicate entryBlock(BasicBlock bb) { entryBB(bb) } + } } diff --git a/python/ql/lib/semmle/python/Flow.qll b/python/ql/lib/semmle/python/Flow.qll index 8fd8502ede4..572bbebf1e1 100644 --- a/python/ql/lib/semmle/python/Flow.qll +++ b/python/ql/lib/semmle/python/Flow.qll @@ -1,6 +1,7 @@ import python private import semmle.python.pointsto.PointsTo private import semmle.python.internal.CachedStages +private import codeql.controlflow.BasicBlock as BB /* * Note about matching parent and child nodes and CFG splitting: @@ -1252,3 +1253,54 @@ private predicate end_bb_likely_reachable(BasicBlock b) { not p = b.getLastNode() ) } + +private class ControlFlowNodeAlias = ControlFlowNode; + +final private class FinalBasicBlock = BasicBlock; + +module Cfg implements BB::CfgSig { + class ControlFlowNode = ControlFlowNodeAlias; + + class SuccessorType = Unit; + + class BasicBlock extends FinalBasicBlock { + // Note `PY:BasicBlock` does not have a `getLocation`. + // (Instead it has a complicated location info logic.) + // Using the location of the first node is simple + // and we just need a way to identify the basic block + // during debugging, so this will be serviceable. + Location getLocation() { result = super.getNode(0).getLocation() } + + int length() { result = count(int i | exists(this.getNode(i))) } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor() and exists(t) } + + predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) } + + predicate dominates(BasicBlock bb) { super.dominates(bb) } + + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } + + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + /** Unsupported. Do not use. */ + predicate strictlyPostDominates(BasicBlock bb) { none() } + + /** Unsupported. Do not use. */ + predicate postDominates(BasicBlock bb) { + this.strictlyPostDominates(bb) or + this = bb + } + } + + pragma[nomagic] + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + bb1.getASuccessor() = bb2 and + bb1 = bb2.getImmediateDominator() and + forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) + } + + predicate entryBlock(BasicBlock bb) { bb.getNode(0).isEntryNode() } +} 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 8ba5247ce52..a7b3b9ceaeb 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/VariableCapture.qll @@ -12,7 +12,7 @@ private import codeql.dataflow.VariableCapture as Shared // The first is the main implementation, the second is a performance motivated restriction. // The restriction is to clear any `CapturedVariableContent` before writing a new one // to avoid long access paths (see the link for a nice explanation). -private module CaptureInput implements Shared::InputSig { +private module CaptureInput implements Shared::InputSig { private import python as PY additional class ExprCfgNode extends ControlFlowNode { @@ -23,28 +23,7 @@ private module CaptureInput implements Shared::InputSig { predicate isConstructor() { none() } } - final private class PyBasicBlock = PY::BasicBlock; - - class BasicBlock extends PyBasicBlock { - int length() { result = count(int i | exists(this.getNode(i))) } - - Callable getEnclosingCallable() { result = this.getScope() } - - // Note `PY:BasicBlock` does not have a `getLocation`. - // (Instead it has a complicated location info logic.) - // Using the location of the first node is simple - // and we just need a way to identify the basic block - // during debugging, so this will be serviceable. - Location getLocation() { result = super.getNode(0).getLocation() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } - } - - class ControlFlowNode = PY::ControlFlowNode; + Callable basicBlockGetEnclosingCallable(Cfg::BasicBlock bb) { result = bb.getScope() } class CapturedVariable extends LocalVariable { Function f; @@ -74,7 +53,7 @@ private module CaptureInput implements Shared::InputSig { } class Expr extends ExprCfgNode { - predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) } } class VariableWrite extends ControlFlowNode { @@ -84,7 +63,7 @@ private module CaptureInput implements Shared::InputSig { CapturedVariable getVariable() { result = v } - predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) } } class VariableRead extends Expr { @@ -126,7 +105,7 @@ class CapturedVariable = CaptureInput::CapturedVariable; class ClosureExpr = CaptureInput::ClosureExpr; -module Flow = Shared::Flow; +module Flow = Shared::Flow; private Flow::ClosureNode asClosureNode(Node n) { result = n.(SynthCaptureNode).getSynthesizedCaptureNode() diff --git a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll index 93253371198..c6d4b86b882 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll @@ -10,6 +10,7 @@ private import internal.ControlFlowGraphImpl as CfgImpl private import CfgNodes private import SuccessorTypes private import CfgImpl::BasicBlocks as BasicBlocksImpl +private import codeql.controlflow.BasicBlock as BB /** * A basic block, that is, a maximal straight-line sequence of control flow nodes @@ -296,3 +297,21 @@ final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBl super.edgeDominates(controlled, s) } } + +private class BasicBlockAlias = BasicBlock; + +private class SuccessorTypeAlias = SuccessorType; + +module Cfg implements BB::CfgSig { + class ControlFlowNode = CfgNode; + + class SuccessorType = SuccessorTypeAlias; + + class BasicBlock = BasicBlockAlias; + + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + BasicBlocksImpl::dominatingEdge(bb1, bb2) + } + + predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index 266400edb5b..91408072ed7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -280,6 +280,7 @@ predicate isNonConstantExpr(CfgNodes::ExprCfgNode n) { /** Provides logic related to captured variables. */ module VariableCapture { private import codeql.dataflow.VariableCapture as Shared + private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks private predicate closureFlowStep(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) { e1 = getALastEvalNode(e2) @@ -290,23 +291,14 @@ module VariableCapture { ) } - private module CaptureInput implements Shared::InputSig { + private module CaptureInput implements Shared::InputSig { private import codeql.ruby.controlflow.ControlFlowGraph as Cfg - private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks private import TaintTrackingPrivate as TaintTrackingPrivate - class BasicBlock extends BasicBlocks::BasicBlock { - Callable getEnclosingCallable() { result = this.getScope() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } + Callable basicBlockGetEnclosingCallable(BasicBlocks::Cfg::BasicBlock bb) { + result = bb.getScope() } - class ControlFlowNode = Cfg::CfgNode; - class CapturedVariable extends LocalVariable { CapturedVariable() { this.isCaptured() and @@ -377,7 +369,7 @@ module VariableCapture { class ClosureExpr = CaptureInput::ClosureExpr; - module Flow = Shared::Flow; + module Flow = Shared::Flow; private Flow::ClosureNode asClosureNode(Node n) { result = n.(CaptureNode).getSynthesizedCaptureNode() diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index fe7e2498a71..af5745f6fd3 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -4,18 +4,16 @@ module; private import codeql.ssa.Ssa as SsaImplCommon private import codeql.ruby.AST private import codeql.ruby.CFG as Cfg +private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl private import codeql.ruby.dataflow.SSA private import codeql.ruby.ast.Variable private import Cfg::CfgNodes::ExprNodes -module SsaInput implements SsaImplCommon::InputSig { +private class BasicBlock = BasicBlocks::Cfg::BasicBlock; + +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; class SourceVariable = LocalVariable; @@ -62,7 +60,7 @@ module SsaInput implements SsaImplCommon::InputSig { } } -import SsaImplCommon::Make as Impl +import SsaImplCommon::Make as Impl class Definition = Impl::Definition; @@ -216,15 +214,14 @@ private predicate hasVariableReadWithCapturedWrite( pragma[noinline] deprecated private predicate adjacentDefReadExt( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2, - SsaInput::SourceVariable v + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SsaInput::SourceVariable v ) { Impl::adjacentDefReadExt(def, _, bb1, i1, bb2, i2) and v = def.getSourceVariable() } deprecated private predicate adjacentDefReachesReadExt( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { exists(SsaInput::SourceVariable v | adjacentDefReadExt(def, bb1, i1, bb2, i2, v) | def.definesAt(v, bb1, i1) @@ -232,7 +229,7 @@ deprecated private predicate adjacentDefReachesReadExt( SsaInput::variableRead(bb1, i1, v, true) ) or - exists(SsaInput::BasicBlock bb3, int i3 | + exists(BasicBlock bb3, int i3 | adjacentDefReachesReadExt(def, bb1, i1, bb3, i3) and SsaInput::variableRead(bb3, i3, _, false) and Impl::adjacentDefReadExt(def, _, bb3, i3, bb2, i2) @@ -240,7 +237,7 @@ deprecated private predicate adjacentDefReachesReadExt( } deprecated private predicate adjacentDefReachesUncertainReadExt( - Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { adjacentDefReachesReadExt(def, bb1, i1, bb2, i2) and SsaInput::variableRead(bb2, i2, _, false) @@ -248,13 +245,11 @@ deprecated private predicate adjacentDefReachesUncertainReadExt( /** Same as `lastRefRedef`, but skips uncertain reads. */ pragma[nomagic] -deprecated private predicate lastRefSkipUncertainReadsExt( - Definition def, SsaInput::BasicBlock bb, int i -) { +deprecated private predicate lastRefSkipUncertainReadsExt(Definition def, BasicBlock bb, int i) { Impl::lastRef(def, bb, i) and not SsaInput::variableRead(bb, i, def.getSourceVariable(), false) or - exists(SsaInput::BasicBlock bb0, int i0 | + exists(BasicBlock bb0, int i0 | Impl::lastRef(def, bb0, i0) and adjacentDefReachesUncertainReadExt(def, bb, i, bb0, i0) ) @@ -475,7 +470,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu private import codeql.util.Boolean class Expr extends Cfg::CfgNodes::ExprCfgNode { - predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) } } Expr getARead(Definition def) { result = Cached::getARead(def) } @@ -491,9 +486,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu * Holds if the evaluation of this guard to `branch` corresponds to the edge * from `bb1` to `bb2`. */ - predicate hasValueBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch - ) { + predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue branch) { exists(Cfg::SuccessorTypes::ConditionalSuccessor s | this.getBasicBlock() = bb1 and bb2 = bb1.getASuccessor(s) and @@ -506,15 +499,13 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu * branch edge from `bb1` to `bb2`. That is, following the edge from * `bb1` to `bb2` implies that this guard evaluated to `branch`. */ - predicate valueControlsBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch - ) { + predicate valueControlsBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue branch) { this.hasValueBranchEdge(bb1, bb2, branch) } } /** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */ - predicate guardDirectlyControlsBlock(Guard guard, SsaInput::BasicBlock bb, GuardValue branch) { + predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, GuardValue branch) { Guards::guardControlsBlock(guard, bb, branch) } } diff --git a/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll b/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll index 4a4b637f3d3..03eac5e31be 100644 --- a/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll +++ b/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll @@ -1,7 +1,10 @@ +private import codeql.controlflow.BasicBlock as BB +private import codeql.Locations +private import codeql.rust.controlflow.ControlFlowGraph as ControlFlowGraph private import internal.ControlFlowGraphImpl as CfgImpl private import CfgImpl::BasicBlocks as BasicBlocksImpl -final class BasicBlock = BasicBlocksImpl::BasicBlock; +class BasicBlock = BasicBlocksImpl::BasicBlock; final class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock; @@ -14,3 +17,15 @@ final class ConditionBasicBlock = BasicBlocksImpl::ConditionBasicBlock; final class JoinBasicBlock = BasicBlocksImpl::JoinBasicBlock; final class JoinPredecessorBasicBlock = BasicBlocksImpl::JoinPredecessorBasicBlock; + +module Cfg implements BB::CfgSig { + class ControlFlowNode = ControlFlowGraph::CfgNode; + + class SuccessorType = ControlFlowGraph::SuccessorType; + + class BasicBlock = BasicBlocksImpl::BasicBlock; + + predicate dominatingEdge = BasicBlocksImpl::dominatingEdge/2; + + predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } +} diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 70ae830c2d0..62fa0aa11bd 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -844,6 +844,7 @@ module RustDataFlow implements InputSig { module VariableCapture { private import codeql.rust.internal.CachedStages private import codeql.dataflow.VariableCapture as SharedVariableCapture + private import codeql.rust.controlflow.BasicBlocks as BasicBlocks private predicate closureFlowStep(ExprCfgNode e1, ExprCfgNode e2) { Stages::DataFlowStage::ref() and @@ -855,22 +856,13 @@ module VariableCapture { ) } - private module CaptureInput implements SharedVariableCapture::InputSig { + private module CaptureInput implements + SharedVariableCapture::InputSig + { private import rust as Ast - private import codeql.rust.controlflow.BasicBlocks as BasicBlocks private import codeql.rust.elements.Variable as Variable - class BasicBlock extends BasicBlocks::BasicBlock { - Callable getEnclosingCallable() { result = this.getScope() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } - } - - class ControlFlowNode = CfgNode; + Callable basicBlockGetEnclosingCallable(BasicBlocks::BasicBlock bb) { result = bb.getScope() } class CapturedVariable extends Variable { CapturedVariable() { this.isCaptured() } @@ -887,7 +879,7 @@ module VariableCapture { } class Expr extends CfgNode { - predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(BasicBlocks::BasicBlock bb, int i) { this = bb.getNode(i) } } class VariableWrite extends Expr { @@ -949,7 +941,7 @@ module VariableCapture { class CapturedVariable = CaptureInput::CapturedVariable; - module Flow = SharedVariableCapture::Flow; + module Flow = SharedVariableCapture::Flow; private Flow::ClosureNode asClosureNode(Node n) { result = n.(CaptureNode).getSynthesizedCaptureNode() diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll index 6429055fccd..df0824fcdfc 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll @@ -54,11 +54,7 @@ private predicate variableReadCertain(BasicBlock bb, int i, VariableAccess va, V ) } -module SsaInput implements SsaImplCommon::InputSig { - class BasicBlock = BasicBlocks::BasicBlock; - - class ControlFlowNode = CfgNode; - +module SsaInput implements SsaImplCommon::InputSig { class SourceVariable = Variable; predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { @@ -87,7 +83,7 @@ module SsaInput implements SsaImplCommon::InputSig { } } -import SsaImplCommon::Make as Impl +import SsaImplCommon::Make as Impl class Definition = Impl::Definition; @@ -320,7 +316,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu private import codeql.util.Boolean class Expr extends CfgNodes::AstCfgNode { - predicate hasCfgNode(SsaInput::BasicBlock bb, int i) { this = bb.getNode(i) } + predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) } } Expr getARead(Definition def) { result = Cached::getARead(def) } @@ -353,9 +349,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu * Holds if the evaluation of this guard to `branch` corresponds to the edge * from `bb1` to `bb2`. */ - predicate hasValueBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch - ) { + predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue branch) { exists(Cfg::ConditionalSuccessor s | this = bb1.getANode() and bb2 = bb1.getASuccessor(s) and @@ -368,15 +362,13 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu * branch edge from `bb1` to `bb2`. That is, following the edge from * `bb1` to `bb2` implies that this guard evaluated to `branch`. */ - predicate valueControlsBranchEdge( - SsaInput::BasicBlock bb1, SsaInput::BasicBlock bb2, GuardValue branch - ) { + predicate valueControlsBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue branch) { this.hasValueBranchEdge(bb1, bb2, branch) } } /** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */ - predicate guardDirectlyControlsBlock(Guard guard, SsaInput::BasicBlock bb, GuardValue branch) { + predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, GuardValue branch) { exists(ConditionBasicBlock conditionBlock, ConditionalSuccessor s | guard = conditionBlock.getLastNode() and s.getValue() = branch and diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index 25796089ab5..6577a067175 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -1609,7 +1609,7 @@ module MakeWithSplitting< private module BasicBlockImpl = BB::Make; - final class BasicBlock = BasicBlockImpl::BasicBlock; + class BasicBlock = BasicBlockImpl::BasicBlock; predicate dominatingEdge = BasicBlockImpl::dominatingEdge/2; diff --git a/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll b/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll index 160903fd895..bc4c1641cb4 100644 --- a/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll +++ b/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll @@ -1,9 +1,11 @@ /** Provides classes representing basic blocks. */ +private import swift private import ControlFlowGraph private import internal.ControlFlowGraphImpl as CfgImpl private import SuccessorTypes private import CfgImpl::BasicBlocks as BasicBlocksImpl +private import codeql.controlflow.BasicBlock as BB /** * A basic block, that is, a maximal straight-line sequence of control flow nodes @@ -111,3 +113,23 @@ final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBl super.edgeDominates(controlled, s) } } + +private class BasicBlockAlias = BasicBlock; + +private class ControlFlowNodeAlias = ControlFlowNode; + +private class SuccessorTypeAlias = SuccessorType; + +module Cfg implements BB::CfgSig { + class ControlFlowNode = ControlFlowNodeAlias; + + class SuccessorType = SuccessorTypeAlias; + + class BasicBlock = BasicBlockAlias; + + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + BasicBlocksImpl::dominatingEdge(bb1, bb2) + } + + predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } +} diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index e7dff4fe753..4f0754ff590 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -6,15 +6,10 @@ module Ssa { private import codeql.swift.controlflow.ControlFlowGraph private import codeql.swift.controlflow.BasicBlocks as BasicBlocks - private module SsaInput implements SsaImplCommon::InputSig { + private module SsaInput implements SsaImplCommon::InputSig { private import internal.DataFlowPrivate - private import codeql.swift.controlflow.ControlFlowGraph as Cfg private import codeql.swift.controlflow.CfgNodes - class BasicBlock = BasicBlocks::BasicBlock; - - class ControlFlowNode = Cfg::ControlFlowNode; - private newtype TSourceVariable = TNormalSourceVariable(VarDecl v) or TKeyPathSourceVariable(EntryNode entry) { entry.getScope() instanceof KeyPathExpr } @@ -55,7 +50,7 @@ module Ssa { override EntryNode asKeyPath() { result = enter } } - predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableWrite(BasicBlocks::BasicBlock bb, int i, SourceVariable v, boolean certain) { exists(AssignExpr assign | bb.getNode(i).getNode().asAstNode() = assign and assign.getDest() = v.getAnAccess() and @@ -93,7 +88,7 @@ module Ssa { ) } - predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { + predicate variableRead(BasicBlocks::BasicBlock bb, int i, SourceVariable v, boolean certain) { exists(DeclRefExpr ref | not isLValue(ref) and bb.getNode(i).getNode().asAstNode() = ref and @@ -127,7 +122,7 @@ module Ssa { /** * INTERNAL: Do not use. */ - module SsaImpl = SsaImplCommon::Make; + module SsaImpl = SsaImplCommon::Make; cached class Definition extends SsaImpl::Definition { @@ -136,7 +131,7 @@ module Ssa { cached ControlFlowNode getARead() { - exists(SsaInput::SourceVariable v, SsaInput::BasicBlock bb, int i | + exists(SsaInput::SourceVariable v, BasicBlocks::BasicBlock bb, int i | SsaImpl::ssaDefReachesRead(v, this, bb, i) and SsaInput::variableRead(bb, i, v, true) and result = bb.getNode(i) @@ -145,7 +140,7 @@ module Ssa { cached ControlFlowNode getAFirstRead() { - exists(SsaInput::BasicBlock bb, int i | + exists(BasicBlocks::BasicBlock bb, int i | SsaImpl::firstUse(this, bb, i, true) and result = bb.getNode(i) ) @@ -154,7 +149,7 @@ module Ssa { cached predicate adjacentReadPair(ControlFlowNode read1, ControlFlowNode read2) { read1 = this.getARead() and - exists(SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 | + exists(BasicBlocks::BasicBlock bb1, int i1, BasicBlocks::BasicBlock bb2, int i2 | read1 = bb1.getNode(i1) and SsaImpl::adjacentUseUse(bb1, i1, bb2, i2, _, true) and read2 = bb2.getNode(i2) @@ -162,7 +157,7 @@ module Ssa { } cached - deprecated predicate lastRefRedef(SsaInput::BasicBlock bb, int i, Definition next) { + deprecated predicate lastRefRedef(BasicBlocks::BasicBlock bb, int i, Definition next) { SsaImpl::lastRefRedef(this, bb, i, next) } } @@ -171,7 +166,7 @@ module Ssa { class WriteDefinition extends Definition, SsaImpl::WriteDefinition { cached override Location getLocation() { - exists(SsaInput::BasicBlock bb, int i | + exists(BasicBlocks::BasicBlock bb, int i | this.definesAt(_, bb, i) and result = bb.getNode(i).getLocation() ) @@ -183,19 +178,19 @@ module Ssa { */ cached predicate assigns(CfgNode value) { - exists(AssignExpr a, SsaInput::BasicBlock bb, int i | + exists(AssignExpr a, BasicBlocks::BasicBlock bb, int i | this.definesAt(_, bb, i) and a = bb.getNode(i).getNode().asAstNode() and value.getNode().asAstNode() = a.getSource() ) or - exists(SsaInput::BasicBlock bb, int blockIndex, NamedPattern np | + exists(BasicBlocks::BasicBlock bb, int blockIndex, NamedPattern np | this.definesAt(_, bb, blockIndex) and np = bb.getNode(blockIndex).getNode().asAstNode() and value.getNode().asAstNode() = np ) or - exists(SsaInput::BasicBlock bb, int blockIndex, ConditionElement ce, Expr init | + exists(BasicBlocks::BasicBlock bb, int blockIndex, ConditionElement ce, Expr init | this.definesAt(_, bb, blockIndex) and ce.getPattern() = bb.getNode(blockIndex).getNode().asAstNode() and init = ce.getInitializer() and @@ -210,14 +205,14 @@ module Ssa { class PhiDefinition extends Definition, SsaImpl::PhiNode { cached override Location getLocation() { - exists(SsaInput::BasicBlock bb | + exists(BasicBlocks::BasicBlock bb | this.definesAt(_, bb, _) and result = bb.getLocation() ) } cached - Definition getPhiInput(SsaInput::BasicBlock bb) { + Definition getPhiInput(BasicBlocks::BasicBlock bb) { SsaImpl::phiHasInputFromBlock(this, result, bb) } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 712d2282b41..9ea57c1ff06 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -882,30 +882,11 @@ private predicate closureFlowStep(CaptureInput::Expr e1, CaptureInput::Expr e2) e2.(Pattern).getImmediateMatchingExpr() = e1 } -private module CaptureInput implements VariableCapture::InputSig { +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() } - - BasicBlock getASuccessor() { result = super.getASuccessor() } - - BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - - predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } - } - - class ControlFlowNode = Cfg::ControlFlowNode; + Callable basicBlockGetEnclosingCallable(BasicBlock bb) { result = bb.getScope() } class CapturedVariable instanceof S::VarDecl { CapturedVariable() { @@ -927,9 +908,7 @@ private module CaptureInput implements VariableCapture::InputSig { Location getLocation() { result = super.getLocation() } - predicate hasCfgNode(BasicBlock bb, int i) { - this = bb.(B::BasicBlock).getNode(i).getNode().asAstNode() - } + predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i).getNode().asAstNode() } } class VariableWrite extends Expr { @@ -1001,7 +980,7 @@ class CapturedVariable = CaptureInput::CapturedVariable; class CapturedParameter = CaptureInput::CapturedParameter; -module CaptureFlow = VariableCapture::Flow; +module CaptureFlow = VariableCapture::Flow; private CaptureFlow::ClosureNode asClosureNode(Node n) { result = n.(CaptureNode).getSynthesizedCaptureNode() From a7b2a2fa9fa8419bd10305ef87eb44647cff7833 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 19 Aug 2025 10:35:01 +0200 Subject: [PATCH 5/9] C++: Sync files. --- .../cpp/ir/implementation/raw/IRBlock.qll | 40 +++++++++++++++++++ .../implementation/unaliased_ssa/IRBlock.qll | 40 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index 50395db47e7..58bd5b0a8cb 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -7,6 +7,7 @@ import Instruction private import internal.IRBlockImports as Imports import Imports::EdgeKind private import Cached +private import codeql.controlflow.BasicBlock as BB /** * Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the @@ -263,6 +264,45 @@ private predicate isEntryBlock(TIRBlock block) { block = MkIRBlock(any(EnterFunctionInstruction enter)) } +module IRCfg implements BB::CfgSig { + class ControlFlowNode = Instruction; + + class SuccessorType = EdgeKind; + + class BasicBlock extends IRBlock { + ControlFlowNode getNode(int i) { result = this.getInstruction(i) } + + ControlFlowNode getLastNode() { result = super.getLastInstruction() } + + int length() { result = this.getInstructionCount() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getASuccessor(SuccessorType t) { result = super.getSuccessor(t) } + + predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) } + + predicate dominates(BasicBlock bb) { super.dominates(bb) } + + BasicBlock getImmediateDominator() { result.immediatelyDominates(this) } + + predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df } + + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) } + + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } + } + + pragma[nomagic] + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + bb1.getASuccessor() = bb2 and + bb1 = bb2.getImmediateDominator() and + forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) + } + + predicate entryBlock(BasicBlock bb) { isEntryBlock(bb) } +} + cached private module Cached { cached diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index 50395db47e7..58bd5b0a8cb 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -7,6 +7,7 @@ import Instruction private import internal.IRBlockImports as Imports import Imports::EdgeKind private import Cached +private import codeql.controlflow.BasicBlock as BB /** * Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the @@ -263,6 +264,45 @@ private predicate isEntryBlock(TIRBlock block) { block = MkIRBlock(any(EnterFunctionInstruction enter)) } +module IRCfg implements BB::CfgSig { + class ControlFlowNode = Instruction; + + class SuccessorType = EdgeKind; + + class BasicBlock extends IRBlock { + ControlFlowNode getNode(int i) { result = this.getInstruction(i) } + + ControlFlowNode getLastNode() { result = super.getLastInstruction() } + + int length() { result = this.getInstructionCount() } + + BasicBlock getASuccessor() { result = super.getASuccessor() } + + BasicBlock getASuccessor(SuccessorType t) { result = super.getSuccessor(t) } + + predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) } + + predicate dominates(BasicBlock bb) { super.dominates(bb) } + + BasicBlock getImmediateDominator() { result.immediatelyDominates(this) } + + predicate inDominanceFrontier(BasicBlock df) { super.dominanceFrontier() = df } + + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) } + + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } + } + + pragma[nomagic] + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { + bb1.getASuccessor() = bb2 and + bb1 = bb2.getImmediateDominator() and + forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) + } + + predicate entryBlock(BasicBlock bb) { isEntryBlock(bb) } +} + cached private module Cached { cached From 3821f172dfeb84e1daab2e2b60a5966d5ac360e3 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 20 Aug 2025 11:30:37 +0200 Subject: [PATCH 6/9] Guards/Java: Use BasicBlock signature in Guards library. --- .../semmle/code/java/controlflow/Guards.qll | 15 ++-- .../controlflow/codeql/controlflow/Guards.qll | 71 ++++--------------- 2 files changed, 20 insertions(+), 66 deletions(-) diff --git a/java/ql/lib/semmle/code/java/controlflow/Guards.qll b/java/ql/lib/semmle/code/java/controlflow/Guards.qll index a3cffbae459..041050e2396 100644 --- a/java/ql/lib/semmle/code/java/controlflow/Guards.qll +++ b/java/ql/lib/semmle/code/java/controlflow/Guards.qll @@ -139,20 +139,17 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre ) } -private module GuardsInput implements SharedGuards::InputSig { +private module SuccessorTypes implements SharedGuards::SuccessorTypesSig { + import SuccessorType +} + +private module GuardsInput implements SharedGuards::InputSig { private import java as J private import semmle.code.java.dataflow.internal.BaseSSA private import semmle.code.java.dataflow.NullGuards as NullGuards - import SuccessorType - - class ControlFlowNode = J::ControlFlowNode; class NormalExitNode = ControlFlow::NormalExitNode; - class BasicBlock = J::BasicBlock; - - predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { J::dominatingEdge(bb1, bb2) } - class AstNode = ExprParent; class Expr = J::Expr; @@ -382,7 +379,7 @@ private module GuardsInput implements SharedGuards::InputSig { } } -private module GuardsImpl = SharedGuards::Make; +private module GuardsImpl = SharedGuards::Make; private module LogicInputCommon { private import semmle.code.java.dataflow.NullGuards as NullGuards diff --git a/shared/controlflow/codeql/controlflow/Guards.qll b/shared/controlflow/codeql/controlflow/Guards.qll index 917f95569ed..f39cb67b7e1 100644 --- a/shared/controlflow/codeql/controlflow/Guards.qll +++ b/shared/controlflow/codeql/controlflow/Guards.qll @@ -50,16 +50,14 @@ overlay[local?] module; +private import codeql.controlflow.BasicBlock as BB private import codeql.util.Boolean private import codeql.util.Location private import codeql.util.Unit -signature module InputSig { - class SuccessorType { - /** Gets a textual representation of this successor type. */ - string toString(); - } +signature class TypSig; +signature module SuccessorTypesSig { class ExceptionSuccessor extends SuccessorType; class ConditionalSuccessor extends SuccessorType { @@ -70,61 +68,12 @@ signature module InputSig { class BooleanSuccessor extends ConditionalSuccessor; class NullnessSuccessor extends ConditionalSuccessor; +} - /** 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(); - } - +signature module InputSig { /** A control flow node indicating normal termination of a callable. */ class NormalExitNode extends ControlFlowNode; - /** - * A basic block, that is, a maximal straight-line sequence of control flow nodes - * without branches or joins. - */ - class BasicBlock { - /** 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 last control flow node in this basic block. */ - ControlFlowNode getLastNode(); - - /** Gets the length of this basic block. */ - int length(); - - /** Gets the location of this basic block. */ - Location getLocation(); - - BasicBlock getASuccessor(SuccessorType t); - - predicate dominates(BasicBlock bb); - - predicate strictlyDominates(BasicBlock bb); - } - - /** - * Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1` - * and `bb2` is a dominating edge. - * - * An edge `(bb1, bb2)` is dominating if there exists a basic block that can - * only be reached from the entry block by going through `(bb1, bb2)`. This - * implies that `(bb1, bb2)` dominates its endpoint `bb2`. I.e., `bb2` can - * only be reached from the entry block by going via `(bb1, bb2)`. - * - * This is a necessary and sufficient condition for an edge to dominate some - * block, and therefore `dominatingEdge(bb1, bb2) and bb2.dominates(bb3)` - * means that the edge `(bb1, bb2)` dominates `bb3`. - */ - predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2); - class AstNode { /** Gets a textual representation of this AST node. */ string toString(); @@ -254,7 +203,15 @@ signature module InputSig { } /** Provides guards-related predicates and classes. */ -module Make Input> { +module Make< + LocationSig Location, BB::CfgSig Cfg, + SuccessorTypesSig SuccessorTypes, + InputSig Input> +{ + private module Cfg_ = Cfg; + + private import Cfg_ + private import SuccessorTypes private import Input private newtype TAbstractSingleValue = From 6ffb9b129fc38db5705cd64a37a7bc1233fab07b Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 22 Aug 2025 10:32:24 +0200 Subject: [PATCH 7/9] C++: Extend final IRBlock --- .../semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll | 4 +++- cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll | 4 +++- .../code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index 58bd5b0a8cb..ba5e562a71e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -269,7 +269,9 @@ module IRCfg implements BB::CfgSig { class SuccessorType = EdgeKind; - class BasicBlock extends IRBlock { + final private class FinalIRBlock = IRBlock; + + class BasicBlock extends FinalIRBlock { ControlFlowNode getNode(int i) { result = this.getInstruction(i) } ControlFlowNode getLastNode() { result = super.getLastInstruction() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index 58bd5b0a8cb..ba5e562a71e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -269,7 +269,9 @@ module IRCfg implements BB::CfgSig { class SuccessorType = EdgeKind; - class BasicBlock extends IRBlock { + final private class FinalIRBlock = IRBlock; + + class BasicBlock extends FinalIRBlock { ControlFlowNode getNode(int i) { result = this.getInstruction(i) } ControlFlowNode getLastNode() { result = super.getLastInstruction() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index 58bd5b0a8cb..ba5e562a71e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -269,7 +269,9 @@ module IRCfg implements BB::CfgSig { class SuccessorType = EdgeKind; - class BasicBlock extends IRBlock { + final private class FinalIRBlock = IRBlock; + + class BasicBlock extends FinalIRBlock { ControlFlowNode getNode(int i) { result = this.getInstruction(i) } ControlFlowNode getLastNode() { result = super.getLastInstruction() } From e2eb6dbbf26932d03aadb9f3b3229d5888d07ece Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 29 Aug 2025 13:58:28 +0200 Subject: [PATCH 8/9] Ruby: Fix query compilation. --- ruby/ql/lib/utils/test/InlineFlowTest.qll | 2 +- ruby/ql/src/queries/performance/DatabaseQueryInLoop.ql | 9 ++++----- .../test/library-tests/controlflow/graph/BasicBlocks.ql | 1 - .../dataflow/barrier-guards/barrier-flow.ql | 3 +-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/ruby/ql/lib/utils/test/InlineFlowTest.qll b/ruby/ql/lib/utils/test/InlineFlowTest.qll index 2444b04fb13..7ef20938003 100644 --- a/ruby/ql/lib/utils/test/InlineFlowTest.qll +++ b/ruby/ql/lib/utils/test/InlineFlowTest.qll @@ -3,7 +3,7 @@ * See `shared/util/codeql/dataflow/test/InlineFlowTest.qll` */ -import ruby +private import ruby private import codeql.Locations private import codeql.dataflow.test.InlineFlowTest private import codeql.ruby.dataflow.internal.DataFlowImplSpecific diff --git a/ruby/ql/src/queries/performance/DatabaseQueryInLoop.ql b/ruby/ql/src/queries/performance/DatabaseQueryInLoop.ql index b34ee080fcd..c81cdd6b2f6 100644 --- a/ruby/ql/src/queries/performance/DatabaseQueryInLoop.ql +++ b/ruby/ql/src/queries/performance/DatabaseQueryInLoop.ql @@ -16,7 +16,6 @@ import codeql.ruby.ast.internal.Constant import codeql.ruby.Concepts import codeql.ruby.frameworks.ActiveRecord private import codeql.ruby.TaintTracking -private import codeql.ruby.CFG private import codeql.ruby.controlflow.internal.Guards as Guards /** Gets the name of a built-in method that involves a loop operation. */ @@ -42,7 +41,7 @@ class LoopingCall extends DataFlow::CallNode { /** Holds if `ar` influences a guard that may control the execution of a loop. */ predicate usedInLoopControlGuard(ActiveRecordInstance ar) { - exists(DataFlow::Node insideGuard, CfgNodes::ExprCfgNode guard | + exists(DataFlow::Node insideGuard, Cfg::CfgNodes::ExprCfgNode guard | // For a guard like `cond && ar`, the whole guard will not be tainted // so we need to look at the taint of the individual parts. insideGuard.asExpr().getExpr() = guard.getExpr().getAChild*() @@ -53,12 +52,12 @@ predicate usedInLoopControlGuard(ActiveRecordInstance ar) { } /** Holds if `guard` controls `break` and `break` would break out of a loop. */ -predicate guardForLoopControl(CfgNodes::ExprCfgNode guard, CfgNodes::AstCfgNode break) { +predicate guardForLoopControl(Cfg::CfgNodes::ExprCfgNode guard, Cfg::CfgNodes::AstCfgNode break) { Guards::guardControlsBlock(guard, break.getBasicBlock(), _) and ( - break.(CfgNodes::ExprNodes::MethodCallCfgNode).getMethodName() = "raise" + break.(Cfg::CfgNodes::ExprNodes::MethodCallCfgNode).getMethodName() = "raise" or - break instanceof CfgNodes::ReturningCfgNode + break instanceof Cfg::CfgNodes::ReturningCfgNode ) } diff --git a/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.ql b/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.ql index e7879fa600a..8db21dd4496 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.ql +++ b/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.ql @@ -1,4 +1,3 @@ -import ruby import codeql.ruby.controlflow.ControlFlowGraph import codeql.ruby.controlflow.BasicBlocks diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-flow.ql b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-flow.ql index e485d98d361..3828ea6b3ae 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-flow.ql +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-flow.ql @@ -2,8 +2,7 @@ * @kind path-problem */ -import codeql.ruby.AST -import codeql.ruby.CFG +import ruby import utils.test.InlineFlowTest import codeql.ruby.dataflow.BarrierGuards import PathGraph From 09b2c5abf011b8f16f79d36712828eb73dbc2f80 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 1 Sep 2025 11:47:33 +0200 Subject: [PATCH 9/9] BasicBlock: Replace entryBlock predicate with subclass. --- .../ir/implementation/aliased_ssa/IRBlock.qll | 6 ++++-- .../cpp/ir/implementation/raw/IRBlock.qll | 6 ++++-- .../implementation/unaliased_ssa/IRBlock.qll | 6 ++++-- .../code/csharp/controlflow/BasicBlocks.qll | 6 ++++-- .../controlflow/internal/PreBasicBlocks.qll | 6 ++++-- .../code/java/controlflow/BasicBlocks.qll | 4 ++-- .../internal/BasicBlockInternal.qll | 6 ++++-- python/ql/lib/semmle/python/Flow.qll | 6 ++++-- .../codeql/ruby/controlflow/BasicBlocks.qll | 6 ++++-- .../codeql/rust/controlflow/BasicBlocks.qll | 4 ++-- .../codeql/controlflow/BasicBlock.qll | 19 +++++++++++++------ .../codeql/dataflow/VariableCapture.qll | 2 +- .../codeql/swift/controlflow/BasicBlocks.qll | 6 ++++-- 13 files changed, 54 insertions(+), 29 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index ba5e562a71e..89efaa8e15a 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -295,14 +295,16 @@ module IRCfg implements BB::CfgSig { predicate postDominates(BasicBlock bb) { super.postDominates(bb) } } + class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { isEntryBlock(this) } + } + pragma[nomagic] predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { bb1.getASuccessor() = bb2 and bb1 = bb2.getImmediateDominator() and forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) } - - predicate entryBlock(BasicBlock bb) { isEntryBlock(bb) } } cached diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index ba5e562a71e..89efaa8e15a 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -295,14 +295,16 @@ module IRCfg implements BB::CfgSig { predicate postDominates(BasicBlock bb) { super.postDominates(bb) } } + class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { isEntryBlock(this) } + } + pragma[nomagic] predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { bb1.getASuccessor() = bb2 and bb1 = bb2.getImmediateDominator() and forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) } - - predicate entryBlock(BasicBlock bb) { isEntryBlock(bb) } } cached diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index ba5e562a71e..89efaa8e15a 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -295,14 +295,16 @@ module IRCfg implements BB::CfgSig { predicate postDominates(BasicBlock bb) { super.postDominates(bb) } } + class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { isEntryBlock(this) } + } + pragma[nomagic] predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { bb1.getASuccessor() = bb2 and bb1 = bb2.getImmediateDominator() and forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) } - - predicate entryBlock(BasicBlock bb) { isEntryBlock(bb) } } cached diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll index 8271b0ac484..c3bf2545230 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -341,6 +341,8 @@ final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBl private class BasicBlockAlias = BasicBlock; +private class EntryBasicBlockAlias = EntryBasicBlock; + module Cfg implements BB::CfgSig { class ControlFlowNode = ControlFlow::Node; @@ -348,9 +350,9 @@ module Cfg implements BB::CfgSig { class BasicBlock = BasicBlockAlias; + class EntryBasicBlock = EntryBasicBlockAlias; + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BasicBlocksImpl::dominatingEdge(bb1, bb2) } - - predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll index 774aa9958c8..b3cdf3335e6 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll @@ -167,9 +167,11 @@ module PreCfg implements BB::CfgSig { class BasicBlock = PreBasicBlock; + class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { entryBB(this) } + } + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { conditionBlockImmediatelyControls(bb1, bb2, _) } - - predicate entryBlock(BasicBlock bb) { entryBB(bb) } } diff --git a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll index 9054debb04e..1657a81816f 100644 --- a/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll +++ b/java/ql/lib/semmle/code/java/controlflow/BasicBlocks.qll @@ -165,7 +165,7 @@ module Cfg implements BB::CfgSig { class BasicBlock = BasicBlockAlias; - predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BbImpl::dominatingEdge(bb1, bb2) } + class EntryBasicBlock extends BasicBlock instanceof BbImpl::EntryBasicBlock { } - predicate entryBlock(BasicBlock bb) { BbImpl::entryBlock(bb) } + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BbImpl::dominatingEdge(bb1, bb2) } } diff --git a/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll b/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll index 7b9c0b4f841..6742429b15e 100644 --- a/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll +++ b/javascript/ql/lib/semmle/javascript/internal/BasicBlockInternal.qll @@ -402,13 +402,15 @@ module Public { predicate postDominates(BasicBlock bb) { this.(ReachableBasicBlock).postDominates(bb) } } + class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { entryBB(this) } + } + pragma[nomagic] predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { bb1.getASuccessor() = bb2 and bb1 = bb2.getImmediateDominator() and forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) } - - predicate entryBlock(BasicBlock bb) { entryBB(bb) } } } diff --git a/python/ql/lib/semmle/python/Flow.qll b/python/ql/lib/semmle/python/Flow.qll index 572bbebf1e1..c91a492e269 100644 --- a/python/ql/lib/semmle/python/Flow.qll +++ b/python/ql/lib/semmle/python/Flow.qll @@ -1295,12 +1295,14 @@ module Cfg implements BB::CfgSig { } } + class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { this.getNode(0).isEntryNode() } + } + pragma[nomagic] predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { bb1.getASuccessor() = bb2 and bb1 = bb2.getImmediateDominator() and forall(BasicBlock pred | pred = bb2.getAPredecessor() and pred != bb1 | bb2.dominates(pred)) } - - predicate entryBlock(BasicBlock bb) { bb.getNode(0).isEntryNode() } } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll index c6d4b86b882..bd13fca2875 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll @@ -300,6 +300,8 @@ final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBl private class BasicBlockAlias = BasicBlock; +private class EntryBasicBlockAlias = EntryBasicBlock; + private class SuccessorTypeAlias = SuccessorType; module Cfg implements BB::CfgSig { @@ -309,9 +311,9 @@ module Cfg implements BB::CfgSig { class BasicBlock = BasicBlockAlias; + class EntryBasicBlock = EntryBasicBlockAlias; + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BasicBlocksImpl::dominatingEdge(bb1, bb2) } - - predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } } diff --git a/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll b/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll index 03eac5e31be..daa2cb1e818 100644 --- a/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll +++ b/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll @@ -25,7 +25,7 @@ module Cfg implements BB::CfgSig { class BasicBlock = BasicBlocksImpl::BasicBlock; - predicate dominatingEdge = BasicBlocksImpl::dominatingEdge/2; + class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock; - predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } + predicate dominatingEdge = BasicBlocksImpl::dominatingEdge/2; } diff --git a/shared/controlflow/codeql/controlflow/BasicBlock.qll b/shared/controlflow/codeql/controlflow/BasicBlock.qll index 683182786be..7aa541b4630 100644 --- a/shared/controlflow/codeql/controlflow/BasicBlock.qll +++ b/shared/controlflow/codeql/controlflow/BasicBlock.qll @@ -150,6 +150,12 @@ signature module CfgSig { predicate postDominates(BasicBlock bb); } + /** + * An entry basic block, that is, a basic block whose first node is + * an entry node. + */ + class EntryBasicBlock extends BasicBlock; + /** * Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1` * and `bb2` is a dominating edge. @@ -164,9 +170,6 @@ signature module CfgSig { * means that the edge `(bb1, bb2)` dominates `bb3`. */ predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2); - - /** Holds if `bb` is an entry basic block. */ - predicate entryBlock(BasicBlock bb); } /** @@ -365,6 +368,10 @@ module Make Input> implements CfgSig Input> implements CfgSig Input> implements CfgSig { class BasicBlock = BasicBlockAlias; + class EntryBasicBlock = EntryBasicBlockAlias; + predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BasicBlocksImpl::dominatingEdge(bb1, bb2) } - - predicate entryBlock(BasicBlock bb) { bb instanceof EntryBasicBlock } }