mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Shared: Add shared basic block library
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
predicate idOfAstNode(AstNode node, int id) { idOf(projectToAst(node), id) }
|
||||
|
||||
predicate idOfCfgScope(CfgScope node, int id) { idOf(node, id) }
|
||||
}
|
||||
|
||||
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