Skip to content

Commit

Permalink
Merge pull request #17763 from geoffw0/cfgcount3
Browse files Browse the repository at this point in the history
Rust: Count number of CFG inconsistencies
  • Loading branch information
geoffw0 authored Oct 15, 2024
2 parents 196baa9 + 79c5adf commit f9eca42
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 34 deletions.
39 changes: 5 additions & 34 deletions rust/ql/consistency-queries/CfgConsistency.ql
Original file line number Diff line number Diff line change
@@ -1,37 +1,8 @@
import rust
import codeql.rust.controlflow.internal.ControlFlowGraphImpl::Consistency as Consistency
import Consistency
import codeql.rust.controlflow.ControlFlowGraph
import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
import codeql.rust.controlflow.internal.Completion

/**
* All `Expr` nodes are `PostOrderTree`s
* @name Control flow graph inconsistencies
* @description Lists the control flow graph inconsistencies in the database. This query is intended for internal use.
* @kind table
* @id rust/diagnostics/cfg-consistency
*/
query predicate nonPostOrderExpr(Expr e, string cls) {
cls = e.getPrimaryQlClasses() and
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
)
}

query predicate scopeNoFirst(CfgScope scope) {
Consistency::scopeNoFirst(scope) and
not scope = any(Function f | not exists(f.getBody())) and
not scope = any(ClosureExpr c | not exists(c.getBody()))
}

/** 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))
}

query predicate deadEnd(CfgImpl::Node node) {
Consistency::deadEnd(node) and
not letElsePanic(node.getAstNode())
}
import codeql.rust.controlflow.internal.CfgConsistency
94 changes: 94 additions & 0 deletions rust/ql/lib/codeql/rust/controlflow/internal/CfgConsistency.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Provides classes for recognizing control flow graph inconsistencies.
*/

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

/**
* All `Expr` nodes are `PostOrderTree`s
*/
query predicate nonPostOrderExpr(Expr e, string cls) {
cls = e.getPrimaryQlClasses() and
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
)
}

/**
* 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(Function f | not exists(f.getBody())) and
not scope = any(ClosureExpr c | not exists(c.getBody()))
}

/** 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
not letElsePanic(node.getAstNode())
}

/**
* 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)
}
15 changes: 15 additions & 0 deletions rust/ql/src/queries/diagnostics/CfgConsistencyCounts.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @name Control flow graph inconsistency counts
* @description Counts the number of control flow graph inconsistencies of each type. This query is intended for internal use.
* @kind diagnostic
* @id rust/diagnostics/cfg-consistency-counts
*/

import rust
import codeql.rust.controlflow.internal.CfgConsistency as Consistency

// see also `rust/diagnostics/cfg-consistency`, which lists the
// individual inconsistency results.
from string type, int num
where num = Consistency::getCfgInconsistencyCounts(type)
select type, num
14 changes: 14 additions & 0 deletions rust/ql/src/queries/summary/Stats.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@
*/

import rust
private import codeql.rust.controlflow.internal.CfgConsistency as CfgConsistency

/**
* Gets a count of the total number of lines of code in the database.
*/
int getLinesOfCode() { result = sum(File f | | f.getNumberOfLinesOfCode()) }

/**
* Gets a count of the total number of lines of code from the source code directory in the database.
*/
int getLinesOfUserCode() {
result = sum(File f | exists(f.getRelativePath()) | f.getNumberOfLinesOfCode())
}

/**
* Gets a count of the total number of control flow graph inconsistencies in the database.
*/
int getTotalCfgInconsistencies() {
result = sum(string type | | CfgConsistency::getCfgInconsistencyCounts(type))
}
2 changes: 2 additions & 0 deletions rust/ql/src/queries/summary/SummaryStats.ql
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ where
key = "Lines of code extracted" and value = getLinesOfCode().toString()
or
key = "Lines of user code extracted" and value = getLinesOfUserCode().toString()
or
key = "Inconsistencies - CFG" and value = getTotalCfgInconsistencies().toString()
select key, value
13 changes: 13 additions & 0 deletions rust/ql/test/query-tests/diagnostics/CfgConsistencyCounts.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
| CFG scope lacks initial AST node | 0 |
| Dead end | 0 |
| Multiple successors of the same type | 0 |
| Multiple toStrings | 0 |
| Non-PostOrderTree Expr node | 0 |
| Non-unique list order | 0 |
| Non-unique set representation | 0 |
| Non-unique split kind | 0 |
| Simple and normal successors | 0 |
| Splitting invariant 2 | 0 |
| Splitting invariant 3 | 0 |
| Splitting invariant 4 | 0 |
| Splitting invariant 5 | 0 |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
queries/diagnostics/CfgConsistencyCounts.ql
1 change: 1 addition & 0 deletions rust/ql/test/query-tests/diagnostics/SummaryStats.expected
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
| Files extracted - total | 7 |
| Files extracted - with errors | 2 |
| Files extracted - without errors | 5 |
| Inconsistencies - CFG | 0 |
| Lines of code extracted | 59 |
| Lines of user code extracted | 59 |

0 comments on commit f9eca42

Please sign in to comment.