mirror of
https://github.com/github/codeql.git
synced 2026-04-10 17:44:03 +02:00
269 lines
9.6 KiB
Plaintext
269 lines
9.6 KiB
Plaintext
/**
|
|
* Provides a library for reasoning about control flow at the granularity of basic blocks.
|
|
* This is usually much more efficient than reasoning directly at the level of `ControlFlowNode`s.
|
|
*/
|
|
|
|
import cpp
|
|
private import internal.PrimitiveBasicBlocks
|
|
private import internal.ConstantExprs
|
|
/*
|
|
* `BasicBlock`s are refinements of `PrimitiveBasicBlock`s, taking
|
|
* impossible CFG edges into account (using the `successors_adapted`
|
|
* relation). The refinement manifests itself in two changes:
|
|
*
|
|
* - The successor relation on `BasicBlock`s uses `successors_adapted`
|
|
* (instead of `successors_extended` used by `PrimitiveBasicBlock`s). Consequently,
|
|
* some edges between `BasicBlock`s may be removed. Example:
|
|
* ```
|
|
* x = 1; // s1
|
|
* if (true) { // s2
|
|
* x = 2; // s3
|
|
* } else {
|
|
* x = 3; // s4
|
|
* }
|
|
* ```
|
|
* The `BasicBlock` successor edge from the basic block containing `s1`
|
|
* and `s2` to the basic block containing `s4` is removed.
|
|
*
|
|
* - `PrimitiveBasicBlock`s may be split up into two or more
|
|
* `BasicBlock`s: Internal nodes of `PrimitiveBasicBlock`s whose
|
|
* predecessor edges have been removed (unreachable code) will be entry
|
|
* points of new `BasicBlock`s. Consequently, each entry point of a
|
|
* `PrimitiveBasicBlock` will also be an entry point of a `BasicBlock`,
|
|
* but the converse does not necessarily hold. Example:
|
|
* ```
|
|
* x = 1; // s5
|
|
* abort(); // s6
|
|
* x = 2; // s7
|
|
* ```
|
|
* `s5`-`s7` belong to the same `PrimitiveBasicBlock`, but the CFG edge
|
|
* from `s6` to `s7` is impossible, so `s7` will be the entry point of
|
|
* its own (unreachable) `BasicBlock`.
|
|
*
|
|
* Note that, although possible, two or more `PrimitiveBasicBlock`s are
|
|
* never merged to one `BasicBlock`: Consider the first example above;
|
|
* it would be possible to define a single `BasicBlock` consisting of
|
|
* `s1`-`s3`, however, the result would be counter-intuitive.
|
|
*/
|
|
|
|
private import Cached
|
|
|
|
cached
|
|
private module Cached {
|
|
/**
|
|
* Any node that is the entry point of a primitive basic block is
|
|
* also the entry point of a basic block. In addition, all nodes
|
|
* with a primitive successor, where the predecessor has been pruned
|
|
* (that is, `getAPredecessor()` does not exist while a predecessor
|
|
* using the primitive `successors_extended` relation does exist), is also
|
|
* considered a basic block entry node.
|
|
*/
|
|
cached
|
|
predicate basic_block_entry_node(ControlFlowNode node) {
|
|
primitive_basic_block_entry_node(node) or
|
|
non_primitive_basic_block_entry_node(node)
|
|
}
|
|
|
|
private predicate non_primitive_basic_block_entry_node(ControlFlowNode node) {
|
|
not primitive_basic_block_entry_node(node) and
|
|
not exists(node.getAPredecessor()) and
|
|
successors_extended(node, _)
|
|
}
|
|
|
|
/**
|
|
* Holds if basic block `bb` equals a primitive basic block.
|
|
*
|
|
* There are two situations in which this is *not* the case:
|
|
*
|
|
* - Either the entry node of `bb` does not correspond to an
|
|
* entry node of a primitive basic block, or
|
|
* - The primitive basic block with the same entry node contains
|
|
* a (non-entry) node which is the entry node of a non-primitive
|
|
* basic block (that is, the primitive basic block has been split
|
|
* up).
|
|
*
|
|
* This predicate is used for performance optimization only:
|
|
* Whenever a `BasicBlock` equals a `PrimitiveBasicBlock`, we can
|
|
* reuse predicates already computed for `PrimitiveBasicBlocks`.
|
|
*/
|
|
private predicate equalsPrimitiveBasicBlock(BasicBlock bb) {
|
|
primitive_basic_block_entry_node(bb) and
|
|
not exists(int i |
|
|
i > 0 and
|
|
non_primitive_basic_block_entry_node(bb.(PrimitiveBasicBlock).getNode(i))
|
|
)
|
|
}
|
|
|
|
/** Holds if `node` is the `pos`th control-flow node in basic block `bb`. */
|
|
cached
|
|
predicate basic_block_member(ControlFlowNode node, BasicBlock bb, int pos) {
|
|
equalsPrimitiveBasicBlock(bb) and primitive_basic_block_member(node, bb, pos) // reuse already computed relation
|
|
or
|
|
non_primitive_basic_block_member(node, bb, pos)
|
|
}
|
|
|
|
private predicate non_primitive_basic_block_member(ControlFlowNode node, BasicBlock bb, int pos) {
|
|
not equalsPrimitiveBasicBlock(bb) and node = bb and pos = 0
|
|
or
|
|
not node instanceof BasicBlock and
|
|
exists(ControlFlowNode pred | successors_extended(pred, node) |
|
|
non_primitive_basic_block_member(pred, bb, pos - 1)
|
|
)
|
|
}
|
|
|
|
/** Gets the number of control-flow nodes in the basic block `bb`. */
|
|
cached
|
|
int bb_length(BasicBlock bb) {
|
|
if equalsPrimitiveBasicBlock(bb)
|
|
then result = bb.(PrimitiveBasicBlock).length() // reuse already computed relation
|
|
else result = strictcount(ControlFlowNode node | basic_block_member(node, bb, _))
|
|
}
|
|
|
|
/** Successor relation for basic blocks. */
|
|
cached
|
|
predicate bb_successor_cached(BasicBlock pred, BasicBlock succ) {
|
|
exists(ControlFlowNode last |
|
|
basic_block_member(last, pred, bb_length(pred) - 1) and
|
|
last.getASuccessor() = succ
|
|
)
|
|
}
|
|
}
|
|
|
|
predicate bb_successor = bb_successor_cached/2;
|
|
|
|
/**
|
|
* A basic block in the C/C++ control-flow graph.
|
|
*
|
|
* A basic block is a simple sequence of control-flow nodes,
|
|
* connected to each other and nothing else:
|
|
*
|
|
* ```
|
|
* A - B - C - D ABCD is a basic block
|
|
* ```
|
|
*
|
|
* Any incoming or outgoing edges break the block into two:
|
|
*
|
|
* ```
|
|
* A - B > C - D AB is a basic block and CD is a basic block (C has two incoming edges)
|
|
*
|
|
*
|
|
* A - B < C - D AB is a basic block and CD is a basic block (B has two outgoing edges)
|
|
* ```
|
|
*/
|
|
class BasicBlock extends ControlFlowNodeBase {
|
|
BasicBlock() { basic_block_entry_node(this) }
|
|
|
|
/** Holds if this basic block contains `node`. */
|
|
predicate contains(ControlFlowNode node) { basic_block_member(node, this, _) }
|
|
|
|
/** Gets the `ControlFlowNode` at position `pos` in this basic block. */
|
|
ControlFlowNode getNode(int pos) { basic_block_member(result, this, pos) }
|
|
|
|
/** Gets a `ControlFlowNode` in this basic block. */
|
|
ControlFlowNode getANode() { basic_block_member(result, this, _) }
|
|
|
|
/** Gets a `BasicBlock` that is a direct successor of this basic block. */
|
|
BasicBlock getASuccessor() { bb_successor(this, result) }
|
|
|
|
/** Gets a `BasicBlock` that is a direct predecessor of this basic block. */
|
|
BasicBlock getAPredecessor() { bb_successor(result, this) }
|
|
|
|
/**
|
|
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
|
|
* when the outgoing edge of this basic block is an expression that is true.
|
|
*/
|
|
BasicBlock getATrueSuccessor() { result.getStart() = this.getEnd().getATrueSuccessor() }
|
|
|
|
/**
|
|
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
|
|
* when the outgoing edge of this basic block is an expression that is false.
|
|
*/
|
|
BasicBlock getAFalseSuccessor() { result.getStart() = this.getEnd().getAFalseSuccessor() }
|
|
|
|
/** Gets the final `ControlFlowNode` of this basic block. */
|
|
ControlFlowNode getEnd() { basic_block_member(result, this, bb_length(this) - 1) }
|
|
|
|
/** Gets the first `ControlFlowNode` of this basic block. */
|
|
ControlFlowNode getStart() { result = this }
|
|
|
|
/** Gets the number of `ControlFlowNode`s in this basic block. */
|
|
int length() { result = bb_length(this) }
|
|
|
|
/**
|
|
* Holds if this element is at the specified location.
|
|
* The location spans column `startcolumn` of line `startline` to
|
|
* column `endcolumn` of line `endline` in file `filepath`.
|
|
* For more information, see
|
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
|
*
|
|
* Yields no result if this basic block spans multiple source files.
|
|
*/
|
|
predicate hasLocationInfo(
|
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
|
) {
|
|
this.hasLocationInfoInternal(filepath, startline, startcolumn, filepath, endline, endcolumn)
|
|
}
|
|
|
|
pragma[noinline]
|
|
private predicate hasLocationInfoInternal(
|
|
string file, int line, int col, string endf, int endl, int endc
|
|
) {
|
|
this.getStart().getLocation().hasLocationInfo(file, line, col, _, _) and
|
|
this.getEnd().getLocation().hasLocationInfo(endf, _, _, endl, endc)
|
|
}
|
|
|
|
/** Gets the function containing this basic block. */
|
|
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
|
|
|
|
/**
|
|
* Holds if this basic block is in a loop of the control-flow graph. This
|
|
* includes loops created by `goto` statements. This predicate may not hold
|
|
* even if this basic block is syntactically inside a `while` loop if the
|
|
* necessary back edges are unreachable.
|
|
*/
|
|
predicate inLoop() { this.getASuccessor+() = this }
|
|
|
|
/**
|
|
* Holds if control flow may reach this basic block from a function entry
|
|
* point or any handler of a reachable `try` statement.
|
|
*/
|
|
predicate isReachable() {
|
|
exists(Function f | f.getBlock() = this)
|
|
or
|
|
exists(TryStmt t, BasicBlock tryblock |
|
|
// a `Handler` precedes the `CatchBlock`, and is always the beginning
|
|
// of a new `BasicBlock` (see `primitive_basic_block_entry_node`).
|
|
this.(Handler).getTryStmt() = t and
|
|
tryblock.isReachable() and
|
|
tryblock.contains(t)
|
|
)
|
|
or
|
|
exists(BasicBlock pred | pred.getASuccessor() = this and pred.isReachable())
|
|
}
|
|
|
|
/** Means `not isReachable()`. */
|
|
predicate isUnreachable() { not this.isReachable() }
|
|
}
|
|
|
|
/** Correct relation for reachability of ControlFlowNodes. */
|
|
predicate unreachable(ControlFlowNode n) {
|
|
exists(BasicBlock bb | bb.contains(n) and bb.isUnreachable())
|
|
}
|
|
|
|
/**
|
|
* An entry point of a function.
|
|
*/
|
|
class EntryBasicBlock extends BasicBlock {
|
|
EntryBasicBlock() { exists(Function f | this = f.getEntryPoint()) }
|
|
}
|
|
|
|
/**
|
|
* A basic block whose last node is the exit point of a function.
|
|
*/
|
|
class ExitBasicBlock extends BasicBlock {
|
|
ExitBasicBlock() {
|
|
this.getEnd() instanceof Function or
|
|
aborting(this.getEnd())
|
|
}
|
|
}
|