Merge pull request #17763 from geoffw0/cfgcount3

Rust: Count number of CFG inconsistencies
This commit is contained in:
Geoffrey White
2024-10-15 15:43:39 +01:00
committed by GitHub
8 changed files with 145 additions and 34 deletions

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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 |

View File

@@ -0,0 +1 @@
queries/diagnostics/CfgConsistencyCounts.ql

View File

@@ -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 |