Merge pull request #18497 from paldepind/shared-basic-block-library

Add shared basic block library
This commit is contained in:
Simon Friis Vindum
2025-01-24 10:49:33 +01:00
committed by GitHub
20 changed files with 9201 additions and 980 deletions

View File

@@ -134,6 +134,10 @@ private module Implementation implements CfgShared::InputSig<Location> {
SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() }
predicate isAbnormalExitType(SuccessorType t) { none() }
int idOfAstNode(AstNode node) { none() }
int idOfCfgScope(CfgScope scope) { none() }
}
module CfgImpl = CfgShared::Make<Location, Implementation>;

View File

@@ -4,26 +4,20 @@
import csharp
private import ControlFlow::SuccessorTypes
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as CfgImpl
private import CfgImpl::BasicBlocks as BasicBlocksImpl
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock extends TBasicBlockStart {
/** Gets an immediate successor of this basic block, if any. */
BasicBlock getASuccessor() { result.getFirstNode() = this.getLastNode().getASuccessor() }
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.getFirstNode() = this.getLastNode().getASuccessorByType(t)
}
/** Gets an immediate predecessor of this basic block, if any. */
BasicBlock getAPredecessor() { result.getASuccessor() = this }
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) {
result.getASuccessorByType(t) = this
result = this.getAPredecessor(t)
}
/**
@@ -65,29 +59,30 @@ class BasicBlock extends TBasicBlockStart {
}
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
ControlFlow::Node getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) }
ControlFlow::Node getNode(int pos) { result = super.getNode(pos) }
/** Gets a control flow node in this basic block. */
ControlFlow::Node getANode() { result = this.getNode(_) }
ControlFlow::Node getANode() { result = super.getANode() }
/** Gets the first control flow node in this basic block. */
ControlFlow::Node getFirstNode() { this = TBasicBlockStart(result) }
ControlFlow::Node getFirstNode() { result = super.getFirstNode() }
/** Gets the last control flow node in this basic block. */
ControlFlow::Node getLastNode() { result = this.getNode(this.length() - 1) }
ControlFlow::Node getLastNode() { result = super.getLastNode() }
/** Gets the callable that this basic block belongs to. */
final Callable getCallable() { result = this.getFirstNode().getEnclosingCallable() }
/** Gets the length of this basic block. */
int length() { result = strictcount(this.getANode()) }
/**
* Holds if this basic block immediately dominates basic block `bb`.
*
* That is, all paths reaching basic block `bb` from some entry point
* basic block must go through this basic block (which is an immediate
* predecessor of `bb`).
* That is, this basic block is the unique basic block satisfying:
* 1. This basic block strictly dominates `bb`
* 2. There exists no other basic block that is strictly dominated by this
* basic block and which strictly dominates `bb`.
*
* All basic blocks, except entry basic blocks, have a unique immediate
* dominator.
*
* Example:
*
@@ -103,7 +98,7 @@ class BasicBlock extends TBasicBlockStart {
* basic block on line 4 (all paths from the entry point of `M`
* to `return s.Length;` must go through the null check).
*/
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) }
/**
* Holds if this basic block strictly dominates basic block `bb`.
@@ -126,7 +121,7 @@ class BasicBlock extends TBasicBlockStart {
* basic block on line 4 (all paths from the entry point of `M`
* to `return s.Length;` must go through the null check).
*/
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
/**
* Holds if this basic block dominates basic block `bb`.
@@ -178,21 +173,18 @@ class BasicBlock extends TBasicBlockStart {
* `Console.Write(x);`. Also, the basic block starting on line 2
* does not dominate the basic block on line 6.
*/
predicate inDominanceFrontier(BasicBlock df) {
this.dominatesPredecessor(df) and
not this.strictlyDominates(df)
}
/**
* Holds if this basic block dominates a predecessor of `df`.
*/
private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) }
predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
/**
* Gets the basic block that immediately dominates this basic block, if any.
*
* That is, all paths reaching this basic block from some entry point
* basic block must go through the result.
* 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.
*
* Example:
*
@@ -208,7 +200,7 @@ class BasicBlock extends TBasicBlockStart {
* the basic block online 4 (all paths from the entry point of `M`
* to `return s.Length;` must go through the null check.
*/
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
/**
* Holds if this basic block strictly post-dominates basic block `bb`.
@@ -234,7 +226,7 @@ class BasicBlock extends TBasicBlockStart {
* line 3 (all paths to the exit point of `M` from `return s.Length;`
* must go through the `WriteLine` call).
*/
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
/**
* Holds if this basic block post-dominates basic block `bb`.
@@ -262,10 +254,7 @@ class BasicBlock extends TBasicBlockStart {
* This predicate is *reflexive*, so for example `Console.WriteLine("M");`
* post-dominates itself.
*/
predicate postDominates(BasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
/**
* Holds if this basic block is in a loop in the control flow graph. This
@@ -274,230 +263,44 @@ class BasicBlock extends TBasicBlockStart {
* necessary back edges are unreachable.
*/
predicate inLoop() { this.getASuccessor+() = this }
/** Gets a textual representation of this basic block. */
string toString() { result = this.getFirstNode().toString() }
/** Gets the location of this basic block. */
Location getLocation() { result = this.getFirstNode().getLocation() }
}
/**
* Internal implementation details.
*/
cached
private module Internal {
/** Internal representation of basic blocks. */
cached
newtype TBasicBlock = TBasicBlockStart(ControlFlow::Node cfn) { startsBB(cfn) }
/** Holds if `cfn` starts a new basic block. */
private predicate startsBB(ControlFlow::Node cfn) {
not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor())
or
cfn.isJoin()
or
cfn.getAPredecessor().isBranch()
or
/*
* In cases such as
* ```csharp
* if (b)
* M()
* ```
* where the `false` edge out of `b` is not present (because we can prove it
* impossible), we still split up the basic block in two, in order to generate
* a `ConditionBlock` which can be used by the guards library.
*/
exists(cfn.getAPredecessorByType(any(ControlFlow::SuccessorTypes::ConditionalSuccessor s)))
}
/**
* Holds if `succ` is a control flow successor of `pred` within
* the same basic block.
*/
private predicate intraBBSucc(ControlFlow::Node pred, ControlFlow::Node succ) {
succ = pred.getASuccessor() and
not startsBB(succ)
}
/**
* Holds if `cfn` is the `i`th node in basic block `bb`.
*
* In other words, `i` is the shortest distance from a node `bb`
* that starts a basic block to `cfn` along the `intraBBSucc` relation.
*/
cached
predicate bbIndex(ControlFlow::Node bbStart, ControlFlow::Node cfn, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `pred`.
*/
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
/** Holds if `pred` is a basic block predecessor of `succ`. */
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
/** Holds if `bb` is an exit basic block that represents normal exit. */
private predicate normalExitBB(BasicBlock bb) {
bb.getANode().(ControlFlow::Nodes::AnnotatedExitNode).isNormal()
}
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
idominance(normalExitBB/1, predBB/2)(_, dom, bb)
}
private import Internal
/**
* An entry basic block, that is, a basic block whose first node is
* the entry node of a callable.
* an entry node.
*/
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
}
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) {
bb.getFirstNode() instanceof ControlFlow::Nodes::EntryNode
}
final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { }
/**
* An annotated exit basic block, that is, a basic block that contains
* an annotated exit node.
* An annotated exit basic block, that is, a basic block that contains an
* annotated exit node.
*/
class AnnotatedExitBasicBlock extends BasicBlock {
private boolean isNormal;
AnnotatedExitBasicBlock() {
this.getANode() =
any(ControlFlow::Nodes::AnnotatedExitNode n |
if n.isNormal() then isNormal = true else isNormal = false
)
}
/** Holds if this block represents a normal exit. */
predicate isNormal() { isNormal = true }
}
final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { }
/**
* An exit basic block, that is, a basic block whose last node is
* the exit node of a callable.
* an exit node.
*/
class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() { this.getLastNode() instanceof ControlFlow::Nodes::ExitNode }
}
private module JoinBlockPredecessors {
private import ControlFlow::Nodes
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as Impl
int getId(JoinBlockPredecessor jbp) {
exists(Impl::AstNode n | result = n.getId() |
n = jbp.getFirstNode().getAstNode()
or
n = jbp.(EntryBasicBlock).getCallable()
)
}
string getSplitString(JoinBlockPredecessor jbp) {
result = jbp.getFirstNode().(ElementNode).getSplitsString()
or
not exists(jbp.getFirstNode().(ElementNode).getSplitsString()) and
result = ""
}
}
final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { }
/** A basic block with more than one predecessor. */
class JoinBlock extends BasicBlock {
JoinBlock() { this.getFirstNode().isJoin() }
/**
* Gets the `i`th predecessor of this join block, with respect to some
* arbitrary order.
*/
cached
JoinBlockPredecessor getJoinBlockPredecessor(int i) {
result =
rank[i + 1](JoinBlockPredecessor jbp |
jbp = this.getAPredecessor()
|
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
)
}
final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock {
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) }
}
/** A basic block that is an immediate predecessor of a join block. */
class JoinBlockPredecessor extends BasicBlock {
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
}
final class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { }
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
class ConditionBlock extends BasicBlock {
ConditionBlock() { this.getLastNode().isCondition() }
/**
* Holds if basic block `succ` is immediately controlled by this basic
* block with conditional value `s`. That is, `succ` is an immediate
* successor of this block, and `succ` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
pragma[nomagic]
/**
* A basic block that terminates in a condition, splitting the subsequent
* control flow.
*/
final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock {
predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
succ = this.getASuccessorByType(s) and
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
super.immediatelyControls(succ, s)
}
/**
* Holds if basic block `controlled` is controlled by this basic block with
* conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate controls(BasicBlock controlled, ConditionalSuccessor s) {
/*
* For this block to control the block `controlled` with `testIsTrue` the following must be true:
* Execution must have passed through the test i.e. `this` must strictly dominate `controlled`.
* Execution must have passed through the `testIsTrue` edge leaving `this`.
*
* Although "passed through the true edge" implies that `this.getATrueSuccessor()` dominates `controlled`,
* the reverse is not true, as flow may have passed through another edge to get to `this.getATrueSuccessor()`
* so we need to assert that `this.getATrueSuccessor()` dominates `controlled` *and* that
* all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`.
*
* For example, in the following C# snippet:
* ```csharp
* if (x)
* controlled;
* false_successor;
* uncontrolled;
* ```
* `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`)
* or dominated by itself. Whereas in the following code:
* ```csharp
* if (x)
* while (controlled)
* also_controlled;
* false_successor;
* uncontrolled;
* ```
* the block `while controlled` is controlled because all of its predecessors are `this` (`if (x)`)
* or (in the case of `also_controlled`) dominated by itself.
*
* The additional constraint on the predecessors of the test successor implies
* that `this` strictly dominates `controlled` so that isn't necessary to check
* directly.
*/
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
super.controls(controlled, s)
}
}

View File

@@ -95,6 +95,10 @@ private module CfgInput implements CfgShared::InputSig<Location> {
t instanceof ST::SuccessorTypes::ExceptionSuccessor or
t instanceof ST::SuccessorTypes::ExitSuccessor
}
int idOfAstNode(AstNode node) { result = node.getId() }
int idOfCfgScope(CfgScope node) { result = idOfAstNode(node) }
}
private module CfgSplittingInput implements CfgShared::SplittingInputSig<Location, CfgInput> {

View File

@@ -4,52 +4,49 @@ private import codeql.ruby.AST
private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.TreeSitter
private import codeql.ruby.controlflow.ControlFlowGraph
private import internal.ControlFlowGraphImpl as CfgImpl
private import CfgNodes
private import SuccessorTypes
private import CfgImpl::BasicBlocks as BasicBlocksImpl
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock extends TBasicBlockStart {
/** Gets the scope of this basic block. */
final CfgScope getScope() { result = this.getFirstNode().getScope() }
final class BasicBlock extends BasicBlocksImpl::BasicBlock {
/** Gets an immediate successor of this basic block, if any. */
BasicBlock getASuccessor() { result = this.getASuccessor(_) }
BasicBlock getASuccessor() { result = super.getASuccessor() }
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessor(SuccessorType t) {
result.getFirstNode() = this.getLastNode().getASuccessor(t)
}
BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor(t) }
/** Gets an immediate predecessor of this basic block, if any. */
BasicBlock getAPredecessor() { result.getASuccessor() = this }
BasicBlock getAPredecessor() { result = super.getAPredecessor() }
/** Gets an immediate predecessor of this basic block of a given type, if any. */
BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
BasicBlock getAPredecessor(SuccessorType t) { result = super.getAPredecessor(t) }
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
CfgNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) }
// The overrides below are to use `CfgNode` instead of `CfgImpl::Node`
CfgNode getNode(int pos) { result = super.getNode(pos) }
/** Gets a control flow node in this basic block. */
CfgNode getANode() { result = this.getNode(_) }
CfgNode getANode() { result = super.getANode() }
/** Gets the first control flow node in this basic block. */
CfgNode getFirstNode() { this = TBasicBlockStart(result) }
CfgNode getFirstNode() { result = super.getFirstNode() }
/** Gets the last control flow node in this basic block. */
CfgNode getLastNode() { result = this.getNode(this.length() - 1) }
/** Gets the length of this basic block. */
int length() { result = strictcount(this.getANode()) }
CfgNode getLastNode() { result = super.getLastNode() }
/**
* Holds if this basic block immediately dominates basic block `bb`.
*
* That is, all paths reaching basic block `bb` from some entry point
* basic block must go through this basic block (which is an immediate
* predecessor of `bb`).
* That is, this basic block is the unique basic block satisfying:
* 1. This basic block strictly dominates `bb`
* 2. There exists no other basic block that is strictly dominated by this
* basic block and which strictly dominates `bb`.
*
* All basic blocks, except entry basic blocks, have a unique immediate
* dominator.
*
* Example:
*
@@ -66,7 +63,7 @@ class BasicBlock extends TBasicBlockStart {
* basic block on line 5 (all paths from the entry point of `m`
* to `return 1` must go through the `if` block).
*/
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) }
/**
* Holds if this basic block strictly dominates basic block `bb`.
@@ -90,7 +87,7 @@ class BasicBlock extends TBasicBlockStart {
* basic block on line 5 (all paths from the entry point of `m`
* to `return 1` must go through the `if` block).
*/
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
/**
* Holds if this basic block dominates basic block `bb`.
@@ -113,10 +110,7 @@ class BasicBlock extends TBasicBlockStart {
* basic block on line 5 (all paths from the entry point of `m`
* to `return 1` must go through the `if` block).
*/
predicate dominates(BasicBlock bb) {
bb = this or
this.strictlyDominates(bb)
}
predicate dominates(BasicBlock bb) { super.dominates(bb) }
/**
* Holds if `df` is in the dominance frontier of this basic block.
@@ -143,22 +137,18 @@ class BasicBlock extends TBasicBlockStart {
* `puts x`. Also, the basic block starting on line 3 does not
* dominate the basic block on line 8.
*/
predicate inDominanceFrontier(BasicBlock df) {
this.dominatesPredecessor(df) and
not this.strictlyDominates(df)
}
/**
* Holds if this basic block dominates a predecessor of `df`.
*/
private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) }
predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
/**
* Gets the basic block that immediately dominates this basic block, if any.
*
* That is, all paths reaching this basic block from some entry point
* basic block must go through the result, which is an immediate basic block
* predecessor of this basic block.
* 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.
*
* Example:
*
@@ -176,7 +166,7 @@ class BasicBlock extends TBasicBlockStart {
* to `return 1` must go through the `if` block, and the `if` block
* is an immediate predecessor of `return 1`).
*/
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
/**
* Holds if this basic block strictly post-dominates basic block `bb`.
@@ -200,7 +190,7 @@ class BasicBlock extends TBasicBlockStart {
* line 3 (all paths to the exit point of `m` from `puts "b"` must go
* through `puts "m"`).
*/
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
/**
* Holds if this basic block post-dominates basic block `bb`.
@@ -223,205 +213,40 @@ class BasicBlock extends TBasicBlockStart {
* (all paths to the exit point of `m` from `puts "b"` must go through
* `puts "m"`).
*/
predicate postDominates(BasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
/** Holds if this basic block is in a loop in the control flow graph. */
predicate inLoop() { this.getASuccessor+() = this }
/** Gets a textual representation of this basic block. */
string toString() { result = this.getFirstNode().toString() }
/** Gets the location of this basic block. */
Location getLocation() { result = this.getFirstNode().getLocation() }
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
}
cached
private module Cached {
/** Internal representation of basic blocks. */
cached
newtype TBasicBlock = TBasicBlockStart(CfgNode cfn) { startsBB(cfn) }
/** Holds if `cfn` starts a new basic block. */
private predicate startsBB(CfgNode cfn) {
not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor())
or
cfn.isJoin()
or
cfn.getAPredecessor().isBranch()
or
/*
* In cases such as
*
* ```rb
* if x or y
* foo
* else
* bar
* ```
*
* we have a CFG that looks like
*
* x --false--> [false] x or y --false--> bar
* \ |
* --true--> y --false--
* \
* --true--> [true] x or y --true--> foo
*
* and we want to ensure that both `foo` and `bar` start a new basic block,
* in order to get a `ConditionalBlock` out of the disjunction.
*/
exists(cfn.getAPredecessor(any(SuccessorTypes::ConditionalSuccessor s)))
}
/**
* Holds if `succ` is a control flow successor of `pred` within
* the same basic block.
*/
private predicate intraBBSucc(CfgNode pred, CfgNode succ) {
succ = pred.getASuccessor() and
not startsBB(succ)
}
/**
* Holds if `cfn` is the `i`th node in basic block `bb`.
*
* In other words, `i` is the shortest distance from a node `bb`
* that starts a basic block to `cfn` along the `intraBBSucc` relation.
*/
cached
predicate bbIndex(CfgNode bbStart, CfgNode cfn, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `pred`.
*/
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
/** Holds if `pred` is a basic block predecessor of `succ`. */
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
/** Holds if `bb` is an exit basic block that represents normal exit. */
private predicate normalExitBB(BasicBlock bb) { bb.getANode().(AnnotatedExitNode).isNormal() }
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
idominance(normalExitBB/1, predBB/2)(_, dom, bb)
/**
* Gets the `i`th predecessor of join block `jb`, with respect to some
* arbitrary order.
*/
cached
JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) {
result =
rank[i + 1](JoinBlockPredecessor jbp |
jbp = jb.getAPredecessor()
|
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
)
}
cached
predicate immediatelyControls(ConditionBlock cb, BasicBlock succ, ConditionalSuccessor s) {
succ = cb.getASuccessor(s) and
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != cb | succ.dominates(pred))
}
cached
predicate controls(ConditionBlock cb, BasicBlock controlled, ConditionalSuccessor s) {
exists(BasicBlock succ | cb.immediatelyControls(succ, s) | succ.dominates(controlled))
}
}
private import Cached
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode }
/**
* An entry basic block, that is, a basic block whose first node is
* an entry node.
*/
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
}
final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { }
/**
* An annotated exit basic block, that is, a basic block whose last node is
* an annotated exit node.
* An annotated exit basic block, that is, a basic block that contains an
* annotated exit node.
*/
class AnnotatedExitBasicBlock extends BasicBlock {
private boolean normal;
AnnotatedExitBasicBlock() {
exists(AnnotatedExitNode n |
n = this.getANode() and
if n.isNormal() then normal = true else normal = false
)
}
/** Holds if this block represent a normal exit. */
final predicate isNormal() { normal = true }
}
final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { }
/**
* An exit basic block, that is, a basic block whose last node is
* an exit node.
*/
class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() { this.getLastNode() instanceof ExitNode }
}
private module JoinBlockPredecessors {
private predicate id(Ruby::AstNode x, Ruby::AstNode y) { x = y }
private predicate idOf(Ruby::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
int getId(JoinBlockPredecessor jbp) {
idOf(toGeneratedInclSynth(jbp.getFirstNode().(AstCfgNode).getAstNode()), result)
or
idOf(toGeneratedInclSynth(jbp.(EntryBasicBlock).getScope()), result)
}
string getSplitString(JoinBlockPredecessor jbp) {
result = jbp.getFirstNode().(AstCfgNode).getSplitsString()
or
not exists(jbp.getFirstNode().(AstCfgNode).getSplitsString()) and
result = ""
}
}
final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { }
/** A basic block with more than one predecessor. */
class JoinBlock extends BasicBlock {
JoinBlock() { this.getFirstNode().isJoin() }
/**
* Gets the `i`th predecessor of this join block, with respect to some
* arbitrary order.
*/
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) }
final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock {
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) }
}
/** A basic block that is an immediate predecessor of a join block. */
class JoinBlockPredecessor extends BasicBlock {
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
}
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
class ConditionBlock extends BasicBlock {
ConditionBlock() { this.getLastNode().isCondition() }
final class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { }
/**
* A basic block that terminates in a condition, splitting the subsequent
* control flow.
*/
final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock {
/**
* Holds if basic block `succ` is immediately controlled by this basic
* block with conditional value `s`. That is, `succ` is an immediate
@@ -429,15 +254,15 @@ class ConditionBlock extends BasicBlock {
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
immediatelyControls(this, succ, s)
super.immediatelyControls(succ, s)
}
/**
* Holds if basic block `controlled` is controlled by this basic block with
* conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
* conditional value `s`. That is, `controlled` can only be reached from the
* callable entry point by going via the `s` edge out of this basic block.
*/
predicate controls(BasicBlock controlled, ConditionalSuccessor s) {
controls(this, controlled, s)
super.controls(controlled, s)
}
}

View File

@@ -60,6 +60,14 @@ private module CfgInput implements CfgShared::InputSig<Location> {
t instanceof Cfg::SuccessorTypes::RaiseSuccessor or
t instanceof Cfg::SuccessorTypes::ExitSuccessor
}
private predicate id(Ruby::AstNode node1, Ruby::AstNode node2) { node1 = node2 }
private predicate idOf(Ruby::AstNode node, int id) = equivalenceRelation(id/2)(node, id)
int idOfAstNode(AstNode node) { idOf(AstInternal::toGeneratedInclSynth(node), result) }
int idOfCfgScope(CfgScope node) { result = idOfAstNode(node) }
}
private module CfgSplittingInput implements CfgShared::SplittingInputSig<Location, CfgInput> {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
import ruby
import codeql.ruby.controlflow.ControlFlowGraph
import codeql.ruby.controlflow.BasicBlocks
query predicate dominates(BasicBlock bb1, BasicBlock bb2) { bb1.dominates(bb2) }
query predicate postDominance(BasicBlock bb1, BasicBlock bb2) { bb1.postDominates(bb2) }
query predicate immediateDominator(BasicBlock bb1, BasicBlock bb2) {
bb1.getImmediateDominator() = bb2
}
query predicate controls(ConditionBlock bb1, BasicBlock bb2, SuccessorType t) {
bb1.controls(bb2, t)
}
query predicate successor(ConditionBlock bb1, BasicBlock bb2, SuccessorType t) {
bb1.getASuccessor(t) = bb2
}
query predicate joinBlockPredecessor(JoinBlock bb1, BasicBlock bb2, int i) {
bb1.getJoinBlockPredecessor(i) = bb2
}

View File

@@ -1,263 +1,16 @@
private import rust
private import ControlFlowGraph
private import internal.SuccessorType
private import internal.ControlFlowGraphImpl as Impl
private import codeql.rust.elements.internal.generated.Raw
private import codeql.rust.elements.internal.generated.Synth
private import internal.ControlFlowGraphImpl as CfgImpl
private import CfgImpl::BasicBlocks as BasicBlocksImpl
final class BasicBlock = BasicBlockImpl;
final class BasicBlock = BasicBlocksImpl::BasicBlock;
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
private class BasicBlockImpl extends TBasicBlockStart {
/** Gets the CFG scope of this basic block. */
CfgScope getScope() { result = this.getAPredecessor().getScope() }
final class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock;
/** Gets an immediate successor of this basic block, if any. */
BasicBlock getASuccessor() { result = this.getASuccessor(_) }
final class ExitBasicBlock = BasicBlocksImpl::ExitBasicBlock;
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessor(SuccessorType t) {
result.getFirstNode() = this.getLastNode().getASuccessor(t)
}
final class AnnotatedExitBasicBlock = BasicBlocksImpl::AnnotatedExitBasicBlock;
/** Gets an immediate predecessor of this basic block, if any. */
BasicBlock getAPredecessor() { result.getASuccessor() = this }
final class ConditionBasicBlock = BasicBlocksImpl::ConditionBasicBlock;
/** Gets an immediate predecessor of this basic block of a given type, if any. */
BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
final class JoinBasicBlock = BasicBlocksImpl::JoinBasicBlock;
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
CfgNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) }
/** Gets a control flow node in this basic block. */
CfgNode getANode() { result = this.getNode(_) }
/** Gets the first control flow node in this basic block. */
CfgNode getFirstNode() { this = TBasicBlockStart(result) }
/** Gets the last control flow node in this basic block. */
CfgNode getLastNode() { result = this.getNode(this.length() - 1) }
/** Gets the length of this basic block. */
int length() { result = strictcount(this.getANode()) }
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
predicate dominates(BasicBlock bb) {
bb = this or
this.strictlyDominates(bb)
}
predicate inDominanceFrontier(BasicBlock df) {
this.dominatesPredecessor(df) and
not this.strictlyDominates(df)
}
/**
* Holds if this basic block dominates a predecessor of `df`.
*/
private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) }
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
predicate postDominates(BasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
/** Holds if this basic block is in a loop in the control flow graph. */
predicate inLoop() { this.getASuccessor+() = this }
/** Gets a textual representation of this basic block. */
string toString() { result = this.getFirstNode().toString() }
/** Gets the location of this basic block. */
Location getLocation() { result = this.getFirstNode().getLocation() }
}
cached
private module Cached {
/** Internal representation of basic blocks. */
cached
newtype TBasicBlock = TBasicBlockStart(CfgNode cfn) { startsBB(cfn) }
/** Holds if `cfn` starts a new basic block. */
private predicate startsBB(CfgNode cfn) {
not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor())
or
cfn.isJoin()
or
cfn.getAPredecessor().isBranch()
}
/**
* Holds if `succ` is a control flow successor of `pred` within
* the same basic block.
*/
private predicate intraBBSucc(CfgNode pred, CfgNode succ) {
succ = pred.getASuccessor() and
not startsBB(succ)
}
/**
* Holds if `cfn` is the `i`th node in basic block `bb`.
*
* In other words, `i` is the shortest distance from a node `bb`
* that starts a basic block to `cfn` along the `intraBBSucc` relation.
*/
cached
predicate bbIndex(CfgNode bbStart, CfgNode cfn, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `pred`.
*/
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
/** Holds if `pred` is a basic block predecessor of `succ`. */
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
/** Holds if `bb` is an exit basic block that represents normal exit. */
private predicate normalExitBB(BasicBlock bb) {
bb.getANode().(Impl::AnnotatedExitNode).isNormal()
}
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
idominance(normalExitBB/1, predBB/2)(_, dom, bb)
/**
* Gets the `i`th predecessor of join block `jb`, with respect to some
* arbitrary order.
*/
cached
JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) {
result =
rank[i + 1](JoinBlockPredecessor jbp |
jbp = jb.getAPredecessor()
|
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
)
}
}
private import Cached
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof Impl::EntryNode }
/**
* An entry basic block, that is, a basic block whose first node is
* an entry node.
*/
class EntryBasicBlock extends BasicBlockImpl {
EntryBasicBlock() { entryBB(this) }
override CfgScope getScope() {
this.getFirstNode() = any(Impl::EntryNode node | node.getScope() = result)
}
}
/**
* An annotated exit basic block, that is, a basic block whose last node is
* an annotated exit node.
*/
class AnnotatedExitBasicBlock extends BasicBlockImpl {
private boolean normal;
AnnotatedExitBasicBlock() {
exists(Impl::AnnotatedExitNode n |
n = this.getANode() and
if n.isNormal() then normal = true else normal = false
)
}
/** Holds if this block represent a normal exit. */
final predicate isNormal() { normal = true }
}
/**
* An exit basic block, that is, a basic block whose last node is
* an exit node.
*/
class ExitBasicBlock extends BasicBlockImpl {
ExitBasicBlock() { this.getLastNode() instanceof Impl::ExitNode }
}
private module JoinBlockPredecessors {
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
// TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block
private predicate idOf(AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) }
int getId(JoinBlockPredecessor jbp) {
idOf(jbp.getFirstNode().(Impl::AstCfgNode).getAstNode(), result)
or
idOf(jbp.(EntryBasicBlock).getScope(), result)
}
string getSplitString(JoinBlockPredecessor jbp) {
result = jbp.getFirstNode().(Impl::AstCfgNode).getSplitsString()
or
not exists(jbp.getFirstNode().(Impl::AstCfgNode).getSplitsString()) and
result = ""
}
}
/** A basic block with more than one predecessor. */
class JoinBlock extends BasicBlockImpl {
JoinBlock() { this.getFirstNode().isJoin() }
/**
* Gets the `i`th predecessor of this join block, with respect to some
* arbitrary order.
*/
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) }
}
/** A basic block that is an immediate predecessor of a join block. */
class JoinBlockPredecessor extends BasicBlockImpl {
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
}
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
class ConditionBlock extends BasicBlockImpl {
ConditionBlock() { this.getLastNode().isCondition() }
/**
* Holds if basic block `succ` is immediately controlled by this basic
* block with conditional value `s`. That is, `succ` is an immediate
* successor of this block, and `succ` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
pragma[nomagic]
predicate immediatelyControls(BasicBlock succ, SuccessorType s) {
succ = this.getASuccessor(s) and
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
}
/**
* Holds if basic block `controlled` is controlled by this basic block with
* conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate controls(BasicBlock controlled, BooleanSuccessor s) {
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
}
}
final class JoinPredecessorBasicBlock = BasicBlocksImpl::JoinPredecessorBasicBlock;

View File

@@ -1,11 +1,8 @@
/** Provides classes representing the control flow graph. */
private import rust
private import internal.ControlFlowGraphImpl
private import internal.Completion
private import internal.SuccessorType
private import internal.Scope as Scope
private import BasicBlocks
final class CfgScope = Scope::CfgScope;
@@ -25,30 +22,4 @@ final class ContinueSuccessor = ContinueSuccessorImpl;
final class ReturnSuccessor = ReturnSuccessorImpl;
/**
* A control flow node.
*
* A control flow node is a node in the control flow graph (CFG). There is a
* many-to-one relationship between CFG nodes and AST nodes.
*
* Only nodes that can be reached from an entry point are included in the CFG.
*/
final class CfgNode extends Node {
/** Gets the file of this control flow node. */
File getFile() { result = this.getLocation().getFile() }
/** Gets a successor node of a given type, if any. */
CfgNode getASuccessor(SuccessorType t) { result = super.getASuccessor(t) }
/** Gets an immediate successor, if any. */
CfgNode getASuccessor() { result = this.getASuccessor(_) }
/** Gets an immediate predecessor node of a given flow type, if any. */
CfgNode getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
/** Gets an immediate predecessor, if any. */
CfgNode getAPredecessor() { result = this.getAPredecessor(_) }
/** Gets the basic block that this control flow node belongs to. */
BasicBlock getBasicBlock() { result.getANode() = this }
}
final class CfgNode = Node;

View File

@@ -3,6 +3,8 @@ import codeql.controlflow.Cfg
import Completion
private import Scope as Scope
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
private import codeql.rust.elements.internal.generated.Raw
private import codeql.rust.elements.internal.generated.Synth
private module CfgInput implements InputSig<Location> {
private import codeql.rust.internal.CachedStages
@@ -48,6 +50,15 @@ private module CfgInput implements InputSig<Location> {
/** Holds if `scope` is exited when `last` finishes with completion `c`. */
predicate scopeLast(CfgScope scope, AstNode last, Completion c) { scope.scopeLast(last, c) }
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
// TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block
int idOfAstNode(AstNode node) { idOfDbAstNode(Synth::convertAstNodeToRaw(node), result) }
int idOfCfgScope(CfgScope node) { result = idOfAstNode(node) }
}
private module CfgSplittingInput implements SplittingInputSig<Location, CfgInput> {

View File

@@ -488,7 +488,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
/** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */
predicate guardControlsBlock(Guard guard, SsaInput::BasicBlock bb, boolean branch) {
exists(ConditionBlock conditionBlock, ConditionalSuccessor s |
exists(ConditionBasicBlock conditionBlock, ConditionalSuccessor s |
guard = conditionBlock.getLastNode() and
s.getValue() = branch and
conditionBlock.controls(bb, s)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
import rust
import codeql.rust.controlflow.ControlFlowGraph
import codeql.rust.controlflow.BasicBlocks
query predicate dominates(BasicBlock bb1, BasicBlock bb2) { bb1.dominates(bb2) }
query predicate postDominance(BasicBlock bb1, BasicBlock bb2) { bb1.postDominates(bb2) }
query predicate immediateDominator(BasicBlock bb1, BasicBlock bb2) {
bb1.getImmediateDominator() = bb2
}
query predicate controls(ConditionBasicBlock bb1, BasicBlock bb2, SuccessorType t) {
bb1.controls(bb2, t)
}
query predicate successor(ConditionBasicBlock bb1, BasicBlock bb2, SuccessorType t) {
bb1.getASuccessor(t) = bb2
}
query predicate joinBlockPredecessor(JoinBasicBlock bb1, BasicBlock bb2, int i) {
bb1.getJoinBlockPredecessor(i) = bb2
}

View File

@@ -0,0 +1,7 @@
---
category: breaking
---
* Added a basic block construction as part of the library. This is currently
considered an internal unstable API. The input signature to the control flow
graph now requires two additional predicates: `idOfAstNode` and
`idOfCfgScope`.

View File

@@ -0,0 +1,370 @@
/**
* This modules provides an implementation of a basic block class based on a
* control flow graph implementation.
*
* INTERNAL use only. This is an experimental API subject to change without
* notice.
*/
private import codeql.util.Location
/** Provides the language-specific input specification. */
signature module InputSig<LocationSig Location> {
class SuccessorType;
/** Hold if `t` represents a conditional successor type. */
predicate successorTypeIsCondition(SuccessorType t);
/** A delineated part of the AST with its own CFG. */
class CfgScope;
/** The class of control flow nodes. */
class Node {
string toString();
/** Gets the location of this control flow node. */
Location getLocation();
}
/** Gets the CFG scope in which this node occurs. */
CfgScope nodeGetCfgScope(Node node);
/** Gets an immediate successor of this node. */
Node nodeGetASuccessor(Node node, SuccessorType t);
/**
* Holds if `node` represents an entry node to be used when calculating
* dominance.
*/
predicate nodeIsDominanceEntry(Node node);
/**
* Holds if `node` represents an exit node to be used when calculating
* post dominance.
*/
predicate nodeIsPostDominanceExit(Node node);
}
/**
* Provides a basic block construction on top of a control flow graph.
*/
module Make<LocationSig Location, InputSig<Location> Input> {
private import Input
final class BasicBlock = BasicBlockImpl;
private Node nodeGetAPredecessor(Node node, SuccessorType s) {
nodeGetASuccessor(result, s) = node
}
/** Holds if this node has more than one predecessor. */
private predicate nodeIsJoin(Node node) { strictcount(nodeGetAPredecessor(node, _)) > 1 }
/** Holds if this node has more than one successor. */
private predicate nodeIsBranch(Node node) { strictcount(nodeGetASuccessor(node, _)) > 1 }
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
private class BasicBlockImpl extends TBasicBlockStart {
/** Gets the CFG scope of this basic block. */
CfgScope getScope() { result = nodeGetCfgScope(this.getFirstNode()) }
/** Gets the location of this basic block. */
Location getLocation() { result = this.getFirstNode().getLocation() }
/** Gets an immediate successor of this basic block, if any. */
BasicBlock getASuccessor() { result = this.getASuccessor(_) }
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessor(SuccessorType t) {
result.getFirstNode() = nodeGetASuccessor(this.getLastNode(), t)
}
/** Gets an immediate predecessor of this basic block, if any. */
BasicBlock getAPredecessor() { result.getASuccessor(_) = this }
/** Gets an immediate predecessor of this basic block of a given type, if any. */
BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
Node getNode(int pos) { result = getNode(this, pos) }
/** Gets a control flow node in this basic block. */
Node getANode() { result = this.getNode(_) }
/** Gets the first control flow node in this basic block. */
Node getFirstNode() { this = TBasicBlockStart(result) }
/** Gets the last control flow node in this basic block. */
Node getLastNode() { result = this.getNode(this.length() - 1) }
/** Gets the length of this basic block. */
int length() { result = strictcount(this.getANode()) }
/**
* Holds if this basic block immediately dominates basic block `bb`.
*
* That is, this basic block is the unique basic block satisfying:
* 1. This basic block strictly dominates `bb`
* 2. There exists no other basic block that is strictly dominated by this
* basic block and which strictly dominates `bb`.
*
* All basic blocks, except entry basic blocks, have a unique immediate
* dominator.
*/
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
/**
* 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) { bbIDominates+(this, 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) {
bb = this or
this.strictlyDominates(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) {
// Algorithm from Cooper et al., "A Simple, Fast Dominance Algorithm" (Figure 5),
// who in turn attribute it to Ferrante et al., "The program dependence graph and
// its use in optimization".
this = df.getAPredecessor() and not bbIDominates(this, df)
or
exists(BasicBlock prev | prev.inDominanceFrontier(df) |
bbIDominates(this, prev) and
not bbIDominates(this, 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() { bbIDominates(result, this) }
/**
* Holds if basic block `succ` is immediately controlled by this basic
* block with successor type `s`.
*
* That is, `succ` is an immediate successor of this block, and `succ` can
* only be reached from the entry block by going via the `s` edge out of
* this basic block.
*/
pragma[nomagic]
predicate immediatelyControls(BasicBlock succ, SuccessorType s) {
succ = this.getASuccessor(s) and
bbIDominates(this, succ) and
// The above is not sufficient to ensure that `succ` can only be reached
// through `s`. To see why, consider this example corresponding to an
// `if` statement without an `else` block and whe `A` is the basic block
// following the `if` statement:
// ```
// ... --> cond --[true]--> ... --> A
// \ /
// ----[false]-----------
// ```
// Here `A` is a direct successor of `cond` along the `false` edge and it
// is immediately dominated by `cond`, but `A` is not controlled by the
// `false` edge since it is also possible to reach `A` via the `true`
// edge.
//
// Note that the first and third conjunct implies the second. But
// explicitly including the second conjunct leads to a better join order.
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this |
succ.dominates(pred)
)
}
/**
* Holds if basic block `controlled` is controlled by this basic block with
* successor type `s`.
*
* That is, all paths reaching `controlled` from the entry point basic
* block must go through the `s` edge out of this basic block.
*
* Control is similar to dominance except it concerns edges instead of
* nodes: A basic block is _dominated_ by a _basic block_ `bb` if it can
* only be reached through `bb` and _controlled_ by an _edge_ `s` if it can
* only be reached through `s`.
*
* Note that where all basic blocks (except the entry basic block) are
* strictly dominated by at least one basic block, a basic block may not be
* controlled by any edge. If an edge controls a basic block `bb`, then
* both endpoints of the edge dominates `bb`. The converse is not the case,
* as there may be multiple paths between the endpoints with none of them
* dominating.
*/
predicate controls(BasicBlock controlled, SuccessorType s) {
// For this block to control the block `controlled` with `s` the following must be true:
// 1/ Execution must have passed through the test i.e. `this` must strictly dominate `controlled`.
// 2/ Execution must have passed through the `s` edge leaving `this`.
//
// Although "passed through the `s` edge" implies that `this.getASuccessor(s)` dominates `controlled`,
// the reverse is not true, as flow may have passed through another edge to get to `this.getASuccessor(s)`
// so we need to assert that `this.getASuccessor(s)` dominates `controlled` *and* that
// all predecessors of `this.getASuccessor(s)` are either `this` or dominated by `this.getASuccessor(s)`.
//
// For example, in the following C# snippet:
// ```csharp
// if (x)
// controlled;
// false_successor;
// uncontrolled;
// ```
// `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`)
// or dominated by itself. Whereas in the following code:
// ```csharp
// if (x)
// while (controlled)
// also_controlled;
// false_successor;
// uncontrolled;
// ```
// the block `while controlled` is controlled because all of its predecessors are `this` (`if (x)`)
// or (in the case of `also_controlled`) dominated by itself.
//
// The additional constraint on the predecessors of the test successor implies
// that `this` strictly dominates `controlled` so that isn't necessary to check
// directly.
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
}
/**
* 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) { bbIPostDominates+(this, 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) {
this.strictlyPostDominates(bb) or
this = bb
}
/** Holds if this basic block is in a loop in the control flow graph. */
predicate inLoop() { this.getASuccessor+() = this }
/** Gets a textual representation of this basic block. */
string toString() { result = this.getFirstNode().toString() }
}
cached
private module Cached {
/**
* Internal representation of basic blocks. A basic block is represented
* by its first CFG node.
*/
cached
newtype TBasicBlock = TBasicBlockStart(Node cfn) { startsBB(cfn) }
/** Holds if `cfn` starts a new basic block. */
private predicate startsBB(Node cfn) {
not exists(nodeGetAPredecessor(cfn, _)) and exists(nodeGetASuccessor(cfn, _))
or
nodeIsJoin(cfn)
or
nodeIsBranch(nodeGetAPredecessor(cfn, _))
or
// In cases such as
//
// ```rb
// if x and y
// foo
// else
// bar
// ```
//
// we have a CFG that looks like
//
// x --false--> [false] x and y --false--> bar
// \ |
// --true--> y --false--
// \
// --true--> [true] x and y --true--> foo
//
// and we want to ensure that both `foo` and `bar` start a new basic block.
exists(nodeGetAPredecessor(cfn, any(SuccessorType s | successorTypeIsCondition(s))))
}
/**
* Holds if `succ` is a control flow successor of `pred` within
* the same basic block.
*/
private predicate intraBBSucc(Node pred, Node succ) {
succ = nodeGetASuccessor(pred, _) and
not startsBB(succ)
}
/**
* Holds if `bbStart` is the first node in a basic block and `cfn` is the
* `i`th node in the same basic block.
*/
pragma[nomagic]
private predicate bbIndex(Node bbStart, Node cfn, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
cached
Node getNode(BasicBlock bb, int pos) { bbIndex(bb.getFirstNode(), result, pos) }
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `pred`.
*/
private predicate succBB(BasicBlock pred, BasicBlock succ) { pred.getASuccessor(_) = succ }
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
/** Holds if `pred` is a basic block predecessor of `succ`. */
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
/** Holds if `bb` is an exit basic block that represents normal exit. */
private predicate exitBB(BasicBlock bb) { nodeIsPostDominanceExit(bb.getANode()) }
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
idominance(exitBB/1, predBB/2)(_, dom, bb)
}
private import Cached
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { nodeIsDominanceEntry(bb.getFirstNode()) }
}

View File

@@ -48,7 +48,7 @@ signature module InputSig<LocationSig Location> {
Location getLocation();
}
/** Gets the CFG scope of AST node `n`. */
/** Gets the CFG scope of the AST node `n`. */
CfgScope getCfgScope(AstNode n);
/** Holds if `first` is executed first when entering `scope`. */
@@ -77,6 +77,18 @@ signature module InputSig<LocationSig Location> {
/** Holds if `t` is an abnormal exit type out of a CFG scope. */
predicate isAbnormalExitType(SuccessorType t);
/**
* Gets an `id` of `node`. This is used to order the predecessors of a join
* basic block.
*/
int idOfAstNode(AstNode node);
/**
* Gets an `id` of `scope`. This is used to order the predecessors of a join
* basic block.
*/
int idOfCfgScope(CfgScope scope);
}
/** Provides input needed for CFG splitting. */
@@ -994,6 +1006,41 @@ module MakeWithSplitting<
)
}
private module JoinBlockPredecessors {
predicate hasIdAndKind(BasicBlocks::JoinPredecessorBasicBlock jbp, int id, int kind) {
id = idOfCfgScope(jbp.(BasicBlocks::EntryBasicBlock).getScope()) and
kind = 0
or
not jbp instanceof BasicBlocks::EntryBasicBlock and
id = idOfAstNode(jbp.getFirstNode().(AstCfgNode).getAstNode()) and
kind = 1
}
string getSplitString(BasicBlocks::JoinPredecessorBasicBlock jbp) {
result = jbp.getFirstNode().(AstCfgNode).getSplitsString()
or
not exists(jbp.getFirstNode().(AstCfgNode).getSplitsString()) and
result = ""
}
}
/**
* Gets the `i`th predecessor of join block `jb`, with respect to some
* arbitrary order.
*/
cached
BasicBlocks::JoinPredecessorBasicBlock getJoinBlockPredecessor(
BasicBlocks::JoinBasicBlock jb, int i
) {
result =
rank[i + 1](BasicBlocks::JoinPredecessorBasicBlock jbp, int id, int kind |
jbp = jb.getAPredecessor() and
JoinBlockPredecessors::hasIdAndKind(jbp, id, kind)
|
jbp order by id, kind, JoinBlockPredecessors::getSplitString(jbp)
)
}
cached
module Public {
/**
@@ -1514,6 +1561,96 @@ module MakeWithSplitting<
/** Holds if CFG scope `scope` lacks an initial AST node. */
query predicate scopeNoFirst(CfgScope scope) { not scopeFirst(scope, _) }
}
/**
* Provides a basic block construction on top of the control flow graph.
*/
module BasicBlocks {
private import codeql.controlflow.BasicBlock as BB
private class CfgScopeAlias = CfgScope;
private class NodeAlias = Node;
private module BasicBlockInputSig implements BB::InputSig<Location> {
class SuccessorType = Input::SuccessorType;
predicate successorTypeIsCondition = Input::successorTypeIsCondition/1;
class CfgScope = CfgScopeAlias;
class Node = NodeAlias;
CfgScope nodeGetCfgScope(Node node) { result = node.getScope() }
Node nodeGetASuccessor(Node node, SuccessorType t) { result = node.getASuccessor(t) }
predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode }
predicate nodeIsPostDominanceExit(Node node) { node.(AnnotatedExitNode).isNormal() }
}
private module BasicBlockImpl = BB::Make<Location, BasicBlockInputSig>;
final class BasicBlock = BasicBlockImpl::BasicBlock;
/**
* An entry basic block, that is, a basic block whose first node is
* an entry node.
*/
final class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { this.getFirstNode() instanceof EntryNode }
}
/**
* An annotated exit basic block, that is, a basic block that contains an
* annotated exit node.
*/
final class AnnotatedExitBasicBlock extends BasicBlock {
private boolean normal;
AnnotatedExitBasicBlock() {
exists(AnnotatedExitNode n |
n = this.getANode() and
if n.isNormal() then normal = true else normal = false
)
}
/** Holds if this block represent a normal exit. */
final predicate isNormal() { normal = true }
}
/**
* An exit basic block, that is, a basic block whose last node is
* an exit node.
*/
final class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() { this.getLastNode() instanceof ExitNode }
}
/** A basic block with more than one predecessor. */
final class JoinBasicBlock extends BasicBlock {
JoinBasicBlock() { this.getFirstNode().isJoin() }
/**
* Gets the `i`th predecessor of this join block, with respect to some
* arbitrary order.
*/
JoinPredecessorBasicBlock getJoinBlockPredecessor(int i) {
result = getJoinBlockPredecessor(this, i)
}
}
/** A basic block that is an immediate predecessor of a join block. */
final class JoinPredecessorBasicBlock extends BasicBlock {
JoinPredecessorBasicBlock() { this.getASuccessor() instanceof JoinBasicBlock }
}
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
final class ConditionBasicBlock extends BasicBlock {
ConditionBasicBlock() { this.getLastNode().isCondition() }
}
}
}
/** Provides a disabled `SplittingInputSig` implementation. */

View File

@@ -1,271 +1,96 @@
/** Provides classes representing basic blocks. */
private import swift
private import ControlFlowGraph
private import internal.ControlFlowGraphImpl as Impl
private import internal.ControlFlowElements
private import CfgNodes
private import internal.ControlFlowGraphImpl as CfgImpl
private import SuccessorTypes
private import codeql.swift.generated.Raw
private import codeql.swift.generated.Synth
private import CfgImpl::BasicBlocks as BasicBlocksImpl
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock extends TBasicBlockStart {
/** Gets the scope of this basic block. */
CfgScope getScope() { result = this.getAPredecessor().getScope() }
final class BasicBlock extends BasicBlocksImpl::BasicBlock {
/** Gets an immediate successor of this basic block, if any. */
BasicBlock getASuccessor() { result = this.getASuccessor(_) }
BasicBlock getASuccessor() { result = super.getASuccessor() }
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessor(SuccessorType t) {
result.getFirstNode() = this.getLastNode().getASuccessor(t)
}
BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor(t) }
/** Gets an immediate predecessor of this basic block, if any. */
BasicBlock getAPredecessor() { result.getASuccessor() = this }
BasicBlock getAPredecessor() { result = super.getAPredecessor() }
/** Gets an immediate predecessor of this basic block of a given type, if any. */
BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
BasicBlock getAPredecessor(SuccessorType t) { result = super.getAPredecessor(t) }
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
ControlFlowNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) }
ControlFlowNode getNode(int pos) { result = super.getNode(pos) }
/** Gets a control flow node in this basic block. */
ControlFlowNode getANode() { result = this.getNode(_) }
ControlFlowNode getANode() { result = super.getANode() }
/** Gets the first control flow node in this basic block. */
ControlFlowNode getFirstNode() { this = TBasicBlockStart(result) }
ControlFlowNode getFirstNode() { result = super.getFirstNode() }
/** Gets the last control flow node in this basic block. */
ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) }
ControlFlowNode getLastNode() { result = super.getLastNode() }
/** Gets the length of this basic block. */
int length() { result = strictcount(this.getANode()) }
predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) }
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
predicate dominates(BasicBlock bb) { super.dominates(bb) }
predicate dominates(BasicBlock bb) {
bb = this or
this.strictlyDominates(bb)
}
predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
predicate inDominanceFrontier(BasicBlock df) {
this.dominatesPredecessor(df) and
not this.strictlyDominates(df)
}
BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
/**
* Holds if this basic block dominates a predecessor of `df`.
*/
private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
predicate postDominates(BasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
/** Holds if this basic block is in a loop in the control flow graph. */
predicate inLoop() { this.getASuccessor+() = this }
/** Gets a textual representation of this basic block. */
string toString() { result = this.getFirstNode().toString() }
/** Gets the location of this basic block. */
Location getLocation() { result = this.getFirstNode().getLocation() }
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
}
cached
private module Cached {
/** Internal representation of basic blocks. */
cached
newtype TBasicBlock = TBasicBlockStart(ControlFlowNode cfn) { startsBB(cfn) }
/** Holds if `cfn` starts a new basic block. */
private predicate startsBB(ControlFlowNode cfn) {
not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor())
or
cfn.isJoin()
or
cfn.getAPredecessor().isBranch()
}
/**
* Holds if `succ` is a control flow successor of `pred` within
* the same basic block.
*/
private predicate intraBBSucc(ControlFlowNode pred, ControlFlowNode succ) {
succ = pred.getASuccessor() and
not startsBB(succ)
}
/**
* Holds if `cfn` is the `i`th node in basic block `bb`.
*
* In other words, `i` is the shortest distance from a node `bb`
* that starts a basic block to `cfn` along the `intraBBSucc` relation.
*/
cached
predicate bbIndex(ControlFlowNode bbStart, ControlFlowNode cfn, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `pred`.
*/
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
/** Holds if `pred` is a basic block predecessor of `succ`. */
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
/** Holds if `bb` is an exit basic block that represents normal exit. */
private predicate normalExitBB(BasicBlock bb) {
bb.getANode().(Impl::AnnotatedExitNode).isNormal()
}
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
idominance(normalExitBB/1, predBB/2)(_, dom, bb)
/**
* Gets the `i`th predecessor of join block `jb`, with respect to some
* arbitrary order.
*/
cached
JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) {
result =
rank[i + 1](JoinBlockPredecessor jbp |
jbp = jb.getAPredecessor()
|
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
)
}
}
private import Cached
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode }
/**
* An entry basic block, that is, a basic block whose first node is
* an entry node.
*/
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
override CfgScope getScope() {
this.getFirstNode() = any(EntryNode node | node.getScope() = result)
}
}
final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { }
/**
* An annotated exit basic block, that is, a basic block whose last node is
* an annotated exit node.
* An annotated exit basic block, that is, a basic block that contains an
* annotated exit node.
*/
class AnnotatedExitBasicBlock extends BasicBlock {
private boolean normal;
AnnotatedExitBasicBlock() {
exists(Impl::AnnotatedExitNode n |
n = this.getANode() and
if n.isNormal() then normal = true else normal = false
)
}
/** Holds if this block represent a normal exit. */
final predicate isNormal() { normal = true }
}
final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { }
/**
* An exit basic block, that is, a basic block whose last node is
* an exit node.
*/
class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() { this.getLastNode() instanceof ExitNode }
}
private module JoinBlockPredecessors {
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
// TODO does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block
private predicate idOf(AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) }
private AstNode projectToAst(ControlFlowElement n) {
result = n.asAstNode()
or
isPropertyGetterElement(n, _, result)
or
isPropertySetterElement(n, _, result)
or
isPropertyObserverElement(n, _, result)
or
result = n.(KeyPathElement).getAst()
or
result = n.(FuncDeclElement).getAst()
}
int getId(JoinBlockPredecessor jbp) {
idOf(projectToAst(jbp.getFirstNode().(CfgNode).getNode()), result)
or
idOf(jbp.(EntryBasicBlock).getScope(), result)
}
string getSplitString(JoinBlockPredecessor jbp) {
result = jbp.getFirstNode().(CfgNode).getSplitsString()
or
not exists(jbp.getFirstNode().(CfgNode).getSplitsString()) and
result = ""
}
}
final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { }
/** A basic block with more than one predecessor. */
class JoinBlock extends BasicBlock {
final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock {
JoinBlock() { this.getFirstNode().isJoin() }
/**
* Gets the `i`th predecessor of this join block, with respect to some
* arbitrary order.
*/
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) }
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) }
}
/** A basic block that is an immediate predecessor of a join block. */
class JoinBlockPredecessor extends BasicBlock {
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
}
class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { }
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
class ConditionBlock extends BasicBlock {
ConditionBlock() { this.getLastNode().isCondition() }
final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock {
/**
* Holds if basic block `succ` is immediately controlled by this basic
* block with conditional value `s`. That is, `succ` is an immediate
* successor of this block, and `succ` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
pragma[nomagic]
predicate immediatelyControls(BasicBlock succ, SuccessorType s) {
succ = this.getASuccessor(s) and
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
super.immediatelyControls(succ, s)
}
/**
@@ -273,7 +98,7 @@ class ConditionBlock extends BasicBlock {
* conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate controls(BasicBlock controlled, BooleanSuccessor s) {
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
predicate controls(BasicBlock controlled, ConditionalSuccessor s) {
super.controls(controlled, s)
}
}

View File

@@ -3,18 +3,18 @@
private import swift
private import BasicBlocks
private import SuccessorTypes
private import internal.ControlFlowGraphImpl
private import internal.ControlFlowGraphImpl as CfgImpl
private import internal.Completion
private import internal.Scope
private import internal.ControlFlowElements
/** An AST node with an associated control-flow graph. */
class CfgScope extends Scope instanceof CfgScope::Range_ {
class CfgScope extends Scope instanceof CfgImpl::CfgScope::Range_ {
/** Gets the CFG scope that this scope is nested under, if any. */
final CfgScope getOuterCfgScope() {
exists(ControlFlowElement parent |
parent.asAstNode() = getParentOfAst(this) and
result = getCfgScope(parent)
result = CfgImpl::getCfgScope(parent)
)
}
}
@@ -27,7 +27,7 @@ class CfgScope extends Scope instanceof CfgScope::Range_ {
*
* Only nodes that can be reached from an entry point are included in the CFG.
*/
class ControlFlowNode extends Node {
class ControlFlowNode extends CfgImpl::Node {
/** Gets the AST node that this node corresponds to, if any. */
ControlFlowElement getNode() { none() }
@@ -63,7 +63,7 @@ class ControlFlowNode extends Node {
}
/** The type of a control flow successor. */
class SuccessorType extends TSuccessorType {
class SuccessorType extends CfgImpl::TSuccessorType {
/** Gets a textual representation of successor type. */
string toString() { none() }
}
@@ -71,7 +71,7 @@ class SuccessorType extends TSuccessorType {
/** Provides different types of control flow successor types. */
module SuccessorTypes {
/** A normal control flow successor. */
class NormalSuccessor extends SuccessorType, TSuccessorSuccessor {
class NormalSuccessor extends SuccessorType, CfgImpl::TSuccessorSuccessor {
final override string toString() { result = "successor" }
}
@@ -89,24 +89,24 @@ module SuccessorTypes {
}
/** A Boolean control flow successor. */
class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor {
BooleanSuccessor() { this = TBooleanSuccessor(value) }
class BooleanSuccessor extends ConditionalSuccessor, CfgImpl::TBooleanSuccessor {
BooleanSuccessor() { this = CfgImpl::TBooleanSuccessor(value) }
}
class BreakSuccessor extends SuccessorType, TBreakSuccessor {
class BreakSuccessor extends SuccessorType, CfgImpl::TBreakSuccessor {
final override string toString() { result = "break" }
}
class ContinueSuccessor extends SuccessorType, TContinueSuccessor {
class ContinueSuccessor extends SuccessorType, CfgImpl::TContinueSuccessor {
final override string toString() { result = "continue" }
}
class ReturnSuccessor extends SuccessorType, TReturnSuccessor {
class ReturnSuccessor extends SuccessorType, CfgImpl::TReturnSuccessor {
final override string toString() { result = "return" }
}
class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor {
MatchingSuccessor() { this = TMatchingSuccessor(value) }
class MatchingSuccessor extends ConditionalSuccessor, CfgImpl::TMatchingSuccessor {
MatchingSuccessor() { this = CfgImpl::TMatchingSuccessor(value) }
/** Holds if this is a match successor. */
predicate isMatch() { value = true }
@@ -114,19 +114,19 @@ module SuccessorTypes {
override string toString() { if this.isMatch() then result = "match" else result = "no-match" }
}
class FallthroughSuccessor extends SuccessorType, TFallthroughSuccessor {
class FallthroughSuccessor extends SuccessorType, CfgImpl::TFallthroughSuccessor {
final override string toString() { result = "fallthrough" }
}
class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor {
EmptinessSuccessor() { this = TEmptinessSuccessor(value) }
class EmptinessSuccessor extends ConditionalSuccessor, CfgImpl::TEmptinessSuccessor {
EmptinessSuccessor() { this = CfgImpl::TEmptinessSuccessor(value) }
predicate isEmpty() { value = true }
override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
}
class ExceptionSuccessor extends SuccessorType, TExceptionSuccessor {
class ExceptionSuccessor extends SuccessorType, CfgImpl::TExceptionSuccessor {
override string toString() { result = "exception" }
}
}

View File

@@ -15,6 +15,8 @@ import Completion
private import codeql.swift.controlflow.ControlFlowGraph as Cfg
private import Splitting as Splitting
private import Scope
private import codeql.swift.generated.Raw
private import codeql.swift.generated.Synth
import ControlFlowElements
import AstControlFlowTrees
@@ -82,6 +84,31 @@ module CfgInput implements InputSig<Location> {
predicate scopeLast(CfgScope scope, AstNode last, Completion c) {
scope.(Impl::CfgScope::Range_).exit(last, c)
}
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
// TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block
private predicate idOf(S::AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) }
private S::AstNode projectToAst(ControlFlowElement n) {
result = n.asAstNode()
or
isPropertyGetterElement(n, _, result)
or
isPropertySetterElement(n, _, result)
or
isPropertyObserverElement(n, _, result)
or
result = n.(KeyPathElement).getAst()
or
result = n.(FuncDeclElement).getAst()
}
int idOfAstNode(AstNode node) { idOf(projectToAst(node), result) }
int idOfCfgScope(CfgScope node) { idOf(node, result) }
}
private module CfgSplittingInput implements SplittingInputSig<Location, CfgInput> {

View File

@@ -21,7 +21,7 @@ module Ssa {
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
class ExitBasicBlock extends BasicBlock, BasicBlocks::ExitBasicBlock { }
class ExitBasicBlock = BasicBlocks::ExitBasicBlock;
private newtype TSourceVariable =
TNormalSourceVariable(VarDecl v) or