Skip to content

Commit d29fa6f

Browse files
committed
Python: Add some CFG-validation queries
These use the annotated, self-verifying test files to check various consistency requirements. Some of these may be expressing the same thing in different ways, but it's fairly cheap to keep them around, so I have not attempted to produce a minimal set of queries for this.
1 parent 21c0d3d commit d29fa6f

File tree

10 files changed

+121
-0
lines changed

10 files changed

+121
-0
lines changed

python/ql/test/library-tests/ControlFlow/evaluation-order/BasicBlockAnnotationGap.expected

Whitespace-only changes.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Checks that within a basic block, if a node is annotated then its
3+
* successor is also annotated (or excluded). A gap in annotations
4+
* within a basic block indicates a missing annotation, since there
5+
* are no branches to justify the gap.
6+
*
7+
* Nodes with exceptional successors are excluded, as the exception
8+
* edge leaves the basic block and the normal successor may be dead.
9+
*/
10+
11+
import python
12+
import TimerUtils
13+
14+
from TimerCfgNode a, ControlFlowNode succ
15+
where
16+
exists(BasicBlock bb, int i |
17+
a = bb.getNode(i) and
18+
succ = bb.getNode(i + 1)
19+
) and
20+
not succ instanceof TimerCfgNode and
21+
not isUnannotatable(succ.getNode()) and
22+
not isTimerMechanism(succ.getNode(), a.getTestFunction()) and
23+
not exists(a.getAnExceptionalSuccessor()) and
24+
succ.getNode() instanceof Expr
25+
select a, "Annotated node followed by unannotated $@ in the same basic block", succ,
26+
succ.getNode().toString()

python/ql/test/library-tests/ControlFlow/evaluation-order/ContiguousTimestamps.expected

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Checks that timestamps form a contiguous sequence {0, 1, ..., max}
3+
* within each test function. Every integer in the range must appear
4+
* in at least one annotation (live or dead).
5+
*/
6+
7+
import python
8+
import TimerUtils
9+
10+
from TestFunction f, int missing, int maxTs, TimerAnnotation maxAnn
11+
where
12+
maxTs = max(TimerAnnotation a | a.getTestFunction() = f | a.getATimestamp()) and
13+
maxAnn.getTestFunction() = f and
14+
maxAnn.getATimestamp() = maxTs and
15+
missing = [0 .. maxTs] and
16+
not exists(TimerAnnotation a | a.getTestFunction() = f and a.getATimestamp() = missing)
17+
select f,
18+
"Missing timestamp " + missing + " (max is $@)",
19+
maxAnn.getTimestampExpr(maxTs), maxTs.toString()

python/ql/test/library-tests/ControlFlow/evaluation-order/NoBackwardFlow.expected

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Checks that time never flows backward between consecutive timer annotations
3+
* in the CFG. For each pair of consecutive annotated nodes (A -> B), there must
4+
* exist timestamps a in A and b in B with a < b.
5+
*/
6+
7+
import python
8+
import TimerUtils
9+
10+
from TimerCfgNode a, TimerCfgNode b, int minA, int maxB
11+
where
12+
nextTimerAnnotation(a, b) and
13+
not a.isDead() and
14+
not b.isDead() and
15+
minA = min(a.getATimestamp()) and
16+
maxB = max(b.getATimestamp()) and
17+
minA >= maxB
18+
select a,
19+
"Backward flow: $@ flows to $@ (max timestamp $@)",
20+
a.getTimestampExpr(minA), minA.toString(),
21+
b, b.getNode().toString(),
22+
b.getTimestampExpr(maxB), maxB.toString()

python/ql/test/library-tests/ControlFlow/evaluation-order/NoSharedReachable.expected

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Checks that two annotations sharing a timestamp value are on
3+
* mutually exclusive CFG paths (neither can reach the other).
4+
*/
5+
6+
import python
7+
import TimerUtils
8+
9+
from TimerCfgNode a, TimerCfgNode b, int ts
10+
where
11+
a != b and
12+
not a.isDead() and
13+
not b.isDead() and
14+
a.getTestFunction() = b.getTestFunction() and
15+
ts = a.getATimestamp() and
16+
ts = b.getATimestamp() and
17+
(
18+
a.getBasicBlock().strictlyReaches(b.getBasicBlock())
19+
or
20+
exists(BasicBlock bb, int i, int j |
21+
a = bb.getNode(i) and b = bb.getNode(j) and i < j
22+
)
23+
)
24+
select a,
25+
"Shared timestamp $@ but this node reaches $@",
26+
a.getTimestampExpr(ts), ts.toString(),
27+
b, b.getNode().toString()

python/ql/test/library-tests/ControlFlow/evaluation-order/StrictForward.expected

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Stronger version of NoBackwardFlow: for consecutive annotated nodes
3+
* A -> B that both have a single timestamp (non-loop code) and B does
4+
* NOT dominate A (forward edge), requires max(A) < min(B).
5+
*/
6+
7+
import python
8+
import TimerUtils
9+
10+
from TimerCfgNode a, TimerCfgNode b, int maxA, int minB
11+
where
12+
nextTimerAnnotation(a, b) and
13+
not a.isDead() and
14+
not b.isDead() and
15+
// Only apply to non-loop code (single timestamps on both sides)
16+
strictcount(a.getATimestamp()) = 1 and
17+
strictcount(b.getATimestamp()) = 1 and
18+
// Forward edge: B does not strictly dominate A (excludes loop back-edges
19+
// but still checks same-basic-block pairs)
20+
not b.getBasicBlock().strictlyDominates(a.getBasicBlock()) and
21+
maxA = max(a.getATimestamp()) and
22+
minB = min(b.getATimestamp()) and
23+
maxA >= minB
24+
select a,
25+
"Strict forward violation: $@ flows to $@",
26+
a.getTimestampExpr(maxA), "timestamp " + maxA,
27+
b.getTimestampExpr(minB), "timestamp " + minB

0 commit comments

Comments
 (0)