mirror of
https://github.com/github/codeql.git
synced 2026-04-20 22:44:52 +02:00
Merge pull request #18497 from paldepind/shared-basic-block-library
Add shared basic block library
This commit is contained in:
@@ -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>;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
6530
ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.expected
Normal file
6530
ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.expected
Normal file
File diff suppressed because it is too large
Load Diff
23
ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.ql
Normal file
23
ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.ql
Normal 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
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
|
||||
1900
rust/ql/test/library-tests/controlflow/BasicBlocks.expected
Normal file
1900
rust/ql/test/library-tests/controlflow/BasicBlocks.expected
Normal file
File diff suppressed because it is too large
Load Diff
23
rust/ql/test/library-tests/controlflow/BasicBlocks.ql
Normal file
23
rust/ql/test/library-tests/controlflow/BasicBlocks.ql
Normal 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
|
||||
}
|
||||
@@ -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`.
|
||||
370
shared/controlflow/codeql/controlflow/BasicBlock.qll
Normal file
370
shared/controlflow/codeql/controlflow/BasicBlock.qll
Normal 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()) }
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user