Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 54 additions & 12 deletions rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll
Original file line number Diff line number Diff line change
@@ -1,29 +1,71 @@
private import codeql.controlflow.BasicBlock as BB
private import codeql.Locations
private import codeql.rust.elements.internal.generated.Raw
private import codeql.rust.elements.internal.generated.Synth
private import codeql.rust.controlflow.ControlFlowGraph as ControlFlowGraph
private import internal.ControlFlowGraphImpl as CfgImpl
private import CfgImpl::BasicBlocks as BasicBlocksImpl

class BasicBlock = BasicBlocksImpl::BasicBlock;
class BasicBlock = CfgImpl::Cfg::BasicBlock;

final class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock;
/** A basic block containing the entry node of a callable. */
final class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { this.getFirstNode() instanceof CfgImpl::ControlFlow::EntryNode }
}

/** A basic block containing the final exit node of a callable. */
final class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() { this.getLastNode() instanceof CfgImpl::ControlFlow::ExitNode }
}

/** A basic block containing an annotated normal or exceptional exit node. */
final class AnnotatedExitBasicBlock extends BasicBlock {
AnnotatedExitBasicBlock() { this.getANode() instanceof CfgImpl::ControlFlow::AnnotatedExitNode }

/** Holds if this block represents normal exit. */
predicate isNormal() { this.getANode() instanceof CfgImpl::ControlFlow::NormalExitNode }
}

final class ExitBasicBlock = BasicBlocksImpl::ExitBasicBlock;
/** A basic block whose last node is a condition. */
final class ConditionBasicBlock extends BasicBlock {
ConditionBasicBlock() { this.getLastNode().(ControlFlowGraph::CfgNode).isCondition() }
}

/** A basic block whose first node joins multiple predecessors. */
final class JoinBasicBlock extends BasicBlock {
JoinBasicBlock() { this.getFirstNode().(ControlFlowGraph::CfgNode).isJoin() }

/** Gets the `i`th predecessor of this join block in a stable arbitrary order. */
JoinPredecessorBasicBlock getJoinBlockPredecessor(int i) {
result =
rank[i + 1](JoinPredecessorBasicBlock pred, int id, string tag |
pred = this.getAPredecessor() and
id = getNodeId(pred.getFirstNode()) and
tag = pred.getFirstNode().getIdTag()
|
pred order by id, tag
)
}
}

final class AnnotatedExitBasicBlock = BasicBlocksImpl::AnnotatedExitBasicBlock;
/** A predecessor basic block of a join block. */
final class JoinPredecessorBasicBlock extends BasicBlock {
JoinPredecessorBasicBlock() { this.getASuccessor() instanceof JoinBasicBlock }
}

final class ConditionBasicBlock = BasicBlocksImpl::ConditionBasicBlock;
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }

final class JoinBasicBlock = BasicBlocksImpl::JoinBasicBlock;
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)

final class JoinPredecessorBasicBlock = BasicBlocksImpl::JoinPredecessorBasicBlock;
private int getNodeId(CfgImpl::ControlFlowNode node) {
idOfDbAstNode(Synth::convertAstNodeToRaw(node.getAstNode()), result)
}

module Cfg implements BB::CfgSig<Location> {
class ControlFlowNode = ControlFlowGraph::CfgNode;
class ControlFlowNode = CfgImpl::Cfg::ControlFlowNode;

class BasicBlock = BasicBlocksImpl::BasicBlock;
class BasicBlock = CfgImpl::Cfg::BasicBlock;

class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock;
class EntryBasicBlock = CfgImpl::Cfg::EntryBasicBlock;

predicate dominatingEdge = BasicBlocksImpl::dominatingEdge/2;
predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
}
17 changes: 13 additions & 4 deletions rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@

private import rust
private import ControlFlowGraph
private import internal.ControlFlowGraphImpl as CfgImpl
private import internal.CfgNodes
import Nodes

class AstCfgNode = CfgImpl::AstCfgNode;
/**
* A CFG node that uniquely represents an AST node.
*/
class AstCfgNode extends CfgNode {
AstCfgNode() { this.injects(_) }

/** Gets the represented AST node. */
AstNode getAstNode() { this.injects(result) }
}

class ExitCfgNode = CfgImpl::ExitNode;
/** A CFG node representing the final exit from a callable. */
class ExitCfgNode extends CfgNode instanceof ControlFlow::ExitNode { }

class AnnotatedExitCfgNode = CfgImpl::AnnotatedExitNode;
/** A CFG node representing a normal or exceptional exit from a callable. */
class AnnotatedExitCfgNode extends CfgNode instanceof ControlFlow::AnnotatedExitNode { }

/** A variable access. */
final class VariableAccessCfgNode extends PathExprBaseCfgNode {
Expand Down
24 changes: 20 additions & 4 deletions rust/ql/lib/codeql/rust/controlflow/ControlFlowGraph.qll
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
/** Provides classes representing the control flow graph. */

private import internal.ControlFlowGraphImpl
private import internal.Scope as Scope
private import internal.ControlFlowGraphImpl as CfgImpl
import codeql.controlflow.SuccessorType

final class CfgScope = Scope::CfgScope;
final class CfgScope = CfgImpl::CfgScope;

final class CfgNode = Node;
/**
* A node in the control flow graph.
*/
class CfgNode extends CfgImpl::ControlFlowNode {
/** Gets the CFG scope containing this node. */
CfgScope getScope() { result = this.getEnclosingCallable() }

/** Holds if this node has conditional successors. */
predicate isCondition() { exists(this.getASuccessor(any(ConditionalSuccessor t))) }

/** Holds if this node has more than one predecessor. */
predicate isJoin() { strictcount(this.getAPredecessor()) > 1 }

/** Holds if this node has more than one successor. */
predicate isBranch() { strictcount(this.getASuccessor()) > 1 }
}

module ControlFlow = CfgImpl::ControlFlow;
112 changes: 9 additions & 103 deletions rust/ql/lib/codeql/rust/controlflow/internal/CfgConsistency.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,124 +3,30 @@
*/

private import rust
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl::Consistency as Consistency
import Consistency
private import codeql.rust.controlflow.ControlFlowGraph
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
private import codeql.rust.controlflow.internal.Completion
private import codeql.rust.controlflow.internal.CfgNodes::Consistency as CfgNodes
private import codeql.rust.Diagnostics

private predicate nonPostOrderExpr(Expr e) {
not e instanceof LetExpr and
not e instanceof ParenExpr and
exists(AstNode last, Completion c |
CfgImpl::last(e, last, c) and
last != e and
c instanceof NormalCompletion
)
}

/**
* All `Expr` nodes are `PostOrderTree`s
*/
query predicate nonPostOrderExpr(Expr e, string cls) {
nonPostOrderExpr(e) and
cls = e.getPrimaryQlClasses()
}

/**
* Holds if CFG scope `scope` lacks an initial AST node. Overrides shared consistency predicate.
*/
query predicate scopeNoFirst(CfgScope scope) {
Consistency::scopeNoFirst(scope) and
not scope =
[
any(AstNode f | not f.(Callable).hasBody()),
any(AsyncBlockExpr b | not b.hasStmtList())
]
}

/** Holds if `be` is the `else` branch of a `let` statement that results in a panic. */
private predicate letElsePanic(BlockExpr be) {
be = any(LetStmt let).getLetElse().getBlockExpr() and
exists(Completion c | CfgImpl::last(be, _, c) | completionIsNormal(c))
}

/**
* Holds if `node` is lacking a successor. Overrides shared consistency predicate.
*/
query predicate deadEnd(CfgImpl::Node node) {
Consistency::deadEnd(node) and
successfullyExtractedFile(node.getLocation().getFile()) and
not letElsePanic(node.getAstNode())
}
import CfgImpl::ControlFlow::Consistency

pragma[nomagic]
private predicate successfullyExtractedFile(File f) {
not exists(ExtractionWarning ee | ee.getLocation().getFile() = f)
not exists(ExtractionWarning warning | warning.getLocation().getFile() = f)
}

query predicate missingCfgChild(CfgNode parent, string pred, int i, AstNode child) {
query predicate missingCfgChild(CfgImpl::ControlFlowNode parent, string pred, int i, AstNode child) {
CfgNodes::missingCfgChild(parent, pred, i, child) and
successfullyExtractedFile(child.getLocation().getFile()) and
not exists(AstNode last, CfgImpl::Completion c | CfgImpl::last(child, last, c) |
// In for example `if (a && true) ...`, there is no edge from the CFG node
// for `true` into the `[false] a && true` node.
strictcount(ConditionalSuccessor cs | exists(last.getACfgNode().getASuccessor(cs)) | cs) = 1
or
// In for example `x && return`, there is no edge from the node for
// `return` into the `&&` node.
not c instanceof CfgImpl::NormalCompletion
)
successfullyExtractedFile(child.getLocation().getFile())
}

/**
* Gets counts of control flow graph inconsistencies of each type.
*/
int getCfgInconsistencyCounts(string type) {
// total results from all the CFG consistency query predicates in:
// - `codeql.rust.controlflow.internal.CfgConsistency` (this file)
// - `shared.controlflow.codeql.controlflow.Cfg`
type = "Non-unique set representation" and
result = count(CfgImpl::Splits ss | nonUniqueSetRepresentation(ss, _) | ss)
or
type = "Splitting invariant 2" and
result = count(AstNode n | breakInvariant2(n, _, _, _, _, _) | n)
or
type = "Splitting invariant 3" and
result = count(AstNode n | breakInvariant3(n, _, _, _, _, _) | n)
or
type = "Splitting invariant 4" and
result = count(AstNode n | breakInvariant4(n, _, _, _, _, _) | n)
or
type = "Splitting invariant 5" and
result = count(AstNode n | breakInvariant5(n, _, _, _, _, _) | n)
or
type = "Multiple successors of the same type" and
result = count(CfgNode n | multipleSuccessors(n, _, _) | n)
or
type = "Simple and normal successors" and
result = count(CfgNode n | simpleAndNormalSuccessors(n, _, _, _, _) | n)
or
type = "Dead end" and
result = count(CfgNode n | deadEnd(n) | n)
or
type = "Non-unique split kind" and
result = count(CfgImpl::SplitImpl si | nonUniqueSplitKind(si, _) | si)
or
type = "Non-unique list order" and
result = count(CfgImpl::SplitKind sk | nonUniqueListOrder(sk, _) | sk)
or
type = "Multiple toStrings" and
result = count(CfgNode n | multipleToString(n) | n)
or
type = "CFG scope lacks initial AST node" and
result = count(CfgScope s | scopeNoFirst(s) | s)
or
type = "Non-PostOrderTree Expr node" and
result = count(Expr e | nonPostOrderExpr(e) | e)
CfgImpl::ControlFlow::Consistency::consistencyOverview(type, result)
or
type = "Missing CFG child" and
result = count(CfgNode parent, string pred, int child | missingCfgChild(parent, pred, child, _))
result =
count(CfgImpl::ControlFlowNode parent, string pred, int child |
missingCfgChild(parent, pred, child, _)
)
}
Loading
Loading