Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust: Count number of CFG inconsistencies #17763

Merged
merged 8 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
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 | Consistency::nonUniqueSetRepresentation(ss, _) | ss)
or
type = "Splitting invariant 2" and
result = count(AstNode n | Consistency::breakInvariant2(n, _, _, _, _, _) | n)
or
type = "Splitting invariant 3" and
result = count(AstNode n | Consistency::breakInvariant3(n, _, _, _, _, _) | n)
or
type = "Splitting invariant 4" and
result = count(AstNode n | Consistency::breakInvariant4(n, _, _, _, _, _) | n)
or
type = "Splitting invariant 5" and
result = count(AstNode n | Consistency::breakInvariant5(n, _, _, _, _, _) | n)
or
type = "Multiple successors of the same type" and
result = count(CfgNode n | Consistency::multipleSuccessors(n, _, _) | n)
or
type = "Simple and normal successors" and
result = count(CfgNode n | Consistency::simpleAndNormalSuccessors(n, _, _, _, _) | n)
or
type = "Dead end" and
result = count(CfgNode n | Consistency::deadEnd(n) | n)
paldepind marked this conversation as resolved.
Show resolved Hide resolved
or
type = "Non-unique split kind" and
result = count(CfgImpl::SplitImpl si | Consistency::nonUniqueSplitKind(si, _) | si)
or
type = "Non-unique list order" and
result = count(CfgImpl::SplitKind sk | Consistency::nonUniqueListOrder(sk, _) | sk)
or
type = "Multiple toStrings" and
result = count(CfgNode n | Consistency::multipleToString(n, _) | n)
or
type = "CFG scope lacks initial AST node" and
result = count(CfgScope s | Consistency::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
12 changes: 12 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,21 @@
*/

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(CfgConsistency::getCfgInconsistencyCounts(_)) }
Fixed Show fixed Hide fixed
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 |
Loading