Python: Fully remove points-to from Flow.qll

Gets rid of a bunch of predicates relating to reachability (which
depended on the modelling of exceptions, which uses points-to), moving
them to `LegacyPointsTo`. In the process, we gained a new class
`BasicBlockWithPointsTo`.
This commit is contained in:
Taus
2025-10-31 14:05:33 +00:00
parent 7176898503
commit 21e74a3f01
4 changed files with 62 additions and 57 deletions

View File

@@ -106,6 +106,24 @@ class ControlFlowNodeWithPointsTo extends ControlFlowNode {
// for that variable.
exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v))
}
/** Whether it is unlikely that this ControlFlowNode can be reached */
predicate unlikelyReachable() {
not start_bb_likely_reachable(this.getBasicBlock())
or
exists(BasicBlock b |
start_bb_likely_reachable(b) and
not end_bb_likely_reachable(b) and
// If there is an unlikely successor edge earlier in the BB
// than this node, then this node must be unreachable.
exists(ControlFlowNode p, int i, int j |
p.(RaisingNode).unlikelySuccessor(_) and
p = b.getNode(i) and
this = b.getNode(j) and
i < j
)
)
}
}
/**
@@ -134,6 +152,45 @@ private predicate varHasCompletePointsToSet(SsaVariable var) {
)
}
private predicate start_bb_likely_reachable(BasicBlock b) {
exists(Scope s | s.getEntryNode() = b.getNode(_))
or
exists(BasicBlock pred |
pred = b.getAPredecessor() and
end_bb_likely_reachable(pred) and
not pred.getLastNode().(RaisingNode).unlikelySuccessor(b)
)
}
private predicate end_bb_likely_reachable(BasicBlock b) {
start_bb_likely_reachable(b) and
not exists(ControlFlowNode p, ControlFlowNode s |
p.(RaisingNode).unlikelySuccessor(s) and
p = b.getNode(_) and
s = b.getNode(_) and
not p = b.getLastNode()
)
}
/**
* An extension of `BasicBlock` that provides points-to related methods.
*/
class BasicBlockWithPointsTo extends BasicBlock {
/**
* Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ.
*/
predicate unlikelySuccessor(BasicBlockWithPointsTo succ) {
this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode())
or
not end_bb_likely_reachable(this) and succ = this.getASuccessor()
}
/**
* Whether (as inferred by type inference) this basic block is likely to be reachable.
*/
predicate likelyReachable() { start_bb_likely_reachable(this) }
}
/**
* An extension of `Expr` that provides points-to predicates.
*/

View File

@@ -1,7 +1,6 @@
import python
private import semmle.python.internal.CachedStages
private import codeql.controlflow.BasicBlock as BB
private import LegacyPointsTo
/*
* Note about matching parent and child nodes and CFG splitting:
@@ -191,24 +190,6 @@ class ControlFlowNode extends @py_flow_node {
/** Whether this node is a normal (non-exceptional) exit */
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
/** Whether it is unlikely that this ControlFlowNode can be reached */
predicate unlikelyReachable() {
not start_bb_likely_reachable(this.getBasicBlock())
or
exists(BasicBlock b |
start_bb_likely_reachable(b) and
not end_bb_likely_reachable(b) and
// If there is an unlikely successor edge earlier in the BB
// than this node, then this node must be unreachable.
exists(ControlFlowNode p, int i, int j |
p.(RaisingNode).unlikelySuccessor(_) and
p = b.getNode(i) and
this = b.getNode(j) and
i < j
)
)
}
/** Whether this strictly dominates other. */
pragma[inline]
predicate strictlyDominates(ControlFlowNode other) {
@@ -1005,7 +986,8 @@ class BasicBlock extends @py_flow_node {
)
}
private ControlFlowNode firstNode() { result = this }
/** Gets the first node in this basic block */
ControlFlowNode firstNode() { result = this }
/** Gets the last node in this basic block */
ControlFlowNode getLastNode() {
@@ -1094,15 +1076,6 @@ class BasicBlock extends @py_flow_node {
)
}
/**
* Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ.
*/
predicate unlikelySuccessor(BasicBlock succ) {
this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode())
or
not end_bb_likely_reachable(this) and succ = this.getASuccessor()
}
/** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */
cached
predicate strictlyReaches(BasicBlock other) {
@@ -1113,11 +1086,6 @@ class BasicBlock extends @py_flow_node {
/** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
/**
* Whether (as inferred by type inference) this basic block is likely to be reachable.
*/
predicate likelyReachable() { start_bb_likely_reachable(this) }
/**
* Gets the `ConditionBlock`, if any, that controls this block and
* does not control any other `ConditionBlock`s that control this block.
@@ -1145,26 +1113,6 @@ class BasicBlock extends @py_flow_node {
}
}
private predicate start_bb_likely_reachable(BasicBlock b) {
exists(Scope s | s.getEntryNode() = b.getNode(_))
or
exists(BasicBlock pred |
pred = b.getAPredecessor() and
end_bb_likely_reachable(pred) and
not pred.getLastNode().(RaisingNode).unlikelySuccessor(b)
)
}
private predicate end_bb_likely_reachable(BasicBlock b) {
start_bb_likely_reachable(b) and
not exists(ControlFlowNode p, ControlFlowNode s |
p.(RaisingNode).unlikelySuccessor(s) and
p = b.getNode(_) and
s = b.getNode(_) and
not p = b.getLastNode()
)
}
private class ControlFlowNodeAlias = ControlFlowNode;
final private class FinalBasicBlock = BasicBlock;

View File

@@ -29,9 +29,9 @@ class FunctionMetrics extends Function {
*/
int getCyclomaticComplexity() {
exists(int e, int n |
n = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and
n = count(BasicBlockWithPointsTo b | b = this.getABasicBlock() and b.likelyReachable()) and
e =
count(BasicBlock b1, BasicBlock b2 |
count(BasicBlockWithPointsTo b1, BasicBlockWithPointsTo b2 |
b1 = this.getABasicBlock() and
b1.likelyReachable() and
b2 = this.getABasicBlock() and

View File

@@ -92,7 +92,7 @@ class SsaVariable extends @py_ssa_var {
}
/** Gets the incoming edges for a Phi node, pruned of unlikely edges. */
private BasicBlock getAPrunedPredecessorBlockForPhi() {
private BasicBlockWithPointsTo getAPrunedPredecessorBlockForPhi() {
result = this.getAPredecessorBlockForPhi() and
not result.unlikelySuccessor(this.getDefinition().getBasicBlock())
}