Skip to content

Commit 91d5171

Browse files
committed
Add base setup for control flow graph construction
1 parent 4f90f5f commit 91d5171

File tree

12 files changed

+614
-0
lines changed

12 files changed

+614
-0
lines changed
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
private import rust
2+
private import ControlFlowGraph
3+
private import internal.SuccessorType
4+
private import SuccessorTypes
5+
private import internal.ControlFlowGraphImpl as Impl
6+
private import codeql.rust.generated.Raw
7+
private import codeql.rust.generated.Synth
8+
9+
/**
10+
* A basic block, that is, a maximal straight-line sequence of control flow nodes
11+
* without branches or joins.
12+
*/
13+
class BasicBlock extends TBasicBlockStart {
14+
/** Gets the scope of this basic block. */
15+
CfgScope getScope() { result = this.getAPredecessor().getScope() }
16+
17+
/** Gets an immediate successor of this basic block, if any. */
18+
BasicBlock getASuccessor() { result = this.getASuccessor(_) }
19+
20+
/** Gets an immediate successor of this basic block of a given type, if any. */
21+
BasicBlock getASuccessor(SuccessorType t) {
22+
result.getFirstNode() = this.getLastNode().getASuccessor(t)
23+
}
24+
25+
/** Gets an immediate predecessor of this basic block, if any. */
26+
BasicBlock getAPredecessor() { result.getASuccessor() = this }
27+
28+
/** Gets an immediate predecessor of this basic block of a given type, if any. */
29+
BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
30+
31+
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
32+
CfgNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) }
33+
34+
/** Gets a control flow node in this basic block. */
35+
CfgNode getANode() { result = this.getNode(_) }
36+
37+
/** Gets the first control flow node in this basic block. */
38+
CfgNode getFirstNode() { this = TBasicBlockStart(result) }
39+
40+
/** Gets the last control flow node in this basic block. */
41+
CfgNode getLastNode() { result = this.getNode(this.length() - 1) }
42+
43+
/** Gets the length of this basic block. */
44+
int length() { result = strictcount(this.getANode()) }
45+
46+
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
47+
48+
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
49+
50+
predicate dominates(BasicBlock bb) {
51+
bb = this or
52+
this.strictlyDominates(bb)
53+
}
54+
55+
predicate inDominanceFrontier(BasicBlock df) {
56+
this.dominatesPredecessor(df) and
57+
not this.strictlyDominates(df)
58+
}
59+
60+
/**
61+
* Holds if this basic block dominates a predecessor of `df`.
62+
*/
63+
private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) }
64+
65+
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
66+
67+
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
68+
69+
predicate postDominates(BasicBlock bb) {
70+
this.strictlyPostDominates(bb) or
71+
this = bb
72+
}
73+
74+
/** Holds if this basic block is in a loop in the control flow graph. */
75+
predicate inLoop() { this.getASuccessor+() = this }
76+
77+
/** Gets a textual representation of this basic block. */
78+
string toString() { result = this.getFirstNode().toString() }
79+
80+
/** Gets the location of this basic block. */
81+
Location getLocation() { result = this.getFirstNode().getLocation() }
82+
}
83+
84+
cached
85+
private module Cached {
86+
/** Internal representation of basic blocks. */
87+
cached
88+
newtype TBasicBlock = TBasicBlockStart(CfgNode cfn) { startsBB(cfn) }
89+
90+
/** Holds if `cfn` starts a new basic block. */
91+
private predicate startsBB(CfgNode cfn) {
92+
not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor())
93+
or
94+
cfn.isJoin()
95+
or
96+
cfn.getAPredecessor().isBranch()
97+
}
98+
99+
/**
100+
* Holds if `succ` is a control flow successor of `pred` within
101+
* the same basic block.
102+
*/
103+
private predicate intraBBSucc(CfgNode pred, CfgNode succ) {
104+
succ = pred.getASuccessor() and
105+
not startsBB(succ)
106+
}
107+
108+
/**
109+
* Holds if `cfn` is the `i`th node in basic block `bb`.
110+
*
111+
* In other words, `i` is the shortest distance from a node `bb`
112+
* that starts a basic block to `cfn` along the `intraBBSucc` relation.
113+
*/
114+
cached
115+
predicate bbIndex(CfgNode bbStart, CfgNode cfn, int i) =
116+
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
117+
118+
/**
119+
* Holds if the first node of basic block `succ` is a control flow
120+
* successor of the last node of basic block `pred`.
121+
*/
122+
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
123+
124+
/** Holds if `dom` is an immediate dominator of `bb`. */
125+
cached
126+
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
127+
idominance(entryBB/1, succBB/2)(_, dom, bb)
128+
129+
/** Holds if `pred` is a basic block predecessor of `succ`. */
130+
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
131+
132+
/** Holds if `bb` is an exit basic block that represents normal exit. */
133+
private predicate normalExitBB(BasicBlock bb) {
134+
bb.getANode().(Impl::AnnotatedExitNode).isNormal()
135+
}
136+
137+
/** Holds if `dom` is an immediate post-dominator of `bb`. */
138+
cached
139+
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
140+
idominance(normalExitBB/1, predBB/2)(_, dom, bb)
141+
142+
/**
143+
* Gets the `i`th predecessor of join block `jb`, with respect to some
144+
* arbitrary order.
145+
*/
146+
cached
147+
JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) {
148+
result =
149+
rank[i + 1](JoinBlockPredecessor jbp |
150+
jbp = jb.getAPredecessor()
151+
|
152+
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
153+
)
154+
}
155+
}
156+
157+
private import Cached
158+
159+
/** Holds if `bb` is an entry basic block. */
160+
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof Impl::EntryNode }
161+
162+
/**
163+
* An entry basic block, that is, a basic block whose first node is
164+
* an entry node.
165+
*/
166+
class EntryBasicBlock extends BasicBlock {
167+
EntryBasicBlock() { entryBB(this) }
168+
169+
override CfgScope getScope() {
170+
this.getFirstNode() = any(Impl::EntryNode node | node.getScope() = result)
171+
}
172+
}
173+
174+
/**
175+
* An annotated exit basic block, that is, a basic block whose last node is
176+
* an annotated exit node.
177+
*/
178+
class AnnotatedExitBasicBlock extends BasicBlock {
179+
private boolean normal;
180+
181+
AnnotatedExitBasicBlock() {
182+
exists(Impl::AnnotatedExitNode n |
183+
n = this.getANode() and
184+
if n.isNormal() then normal = true else normal = false
185+
)
186+
}
187+
188+
/** Holds if this block represent a normal exit. */
189+
final predicate isNormal() { normal = true }
190+
}
191+
192+
/**
193+
* An exit basic block, that is, a basic block whose last node is
194+
* an exit node.
195+
*/
196+
class ExitBasicBlock extends BasicBlock {
197+
ExitBasicBlock() { this.getLastNode() instanceof Impl::ExitNode }
198+
}
199+
200+
private module JoinBlockPredecessors {
201+
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
202+
203+
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
204+
205+
// TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block
206+
private predicate idOf(AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) }
207+
208+
int getId(JoinBlockPredecessor jbp) {
209+
idOf(jbp.getFirstNode().(Impl::AstCfgNode).getAstNode(), result)
210+
or
211+
idOf(jbp.(EntryBasicBlock).getScope(), result)
212+
}
213+
214+
string getSplitString(JoinBlockPredecessor jbp) {
215+
result = jbp.getFirstNode().(Impl::AstCfgNode).getSplitsString()
216+
or
217+
not exists(jbp.getFirstNode().(Impl::AstCfgNode).getSplitsString()) and
218+
result = ""
219+
}
220+
}
221+
222+
/** A basic block with more than one predecessor. */
223+
class JoinBlock extends BasicBlock {
224+
JoinBlock() { this.getFirstNode().isJoin() }
225+
226+
/**
227+
* Gets the `i`th predecessor of this join block, with respect to some
228+
* arbitrary order.
229+
*/
230+
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) }
231+
}
232+
233+
/** A basic block that is an immediate predecessor of a join block. */
234+
class JoinBlockPredecessor extends BasicBlock {
235+
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
236+
}
237+
238+
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
239+
class ConditionBlock extends BasicBlock {
240+
ConditionBlock() { this.getLastNode().isCondition() }
241+
242+
/**
243+
* Holds if basic block `succ` is immediately controlled by this basic
244+
* block with conditional value `s`. That is, `succ` is an immediate
245+
* successor of this block, and `succ` can only be reached from
246+
* the callable entry point by going via the `s` edge out of this basic block.
247+
*/
248+
pragma[nomagic]
249+
predicate immediatelyControls(BasicBlock succ, SuccessorType s) {
250+
succ = this.getASuccessor(s) and
251+
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
252+
}
253+
254+
/**
255+
* Holds if basic block `controlled` is controlled by this basic block with
256+
* conditional value `s`. That is, `controlled` can only be reached from
257+
* the callable entry point by going via the `s` edge out of this basic block.
258+
*/
259+
predicate controls(BasicBlock controlled, BooleanSuccessor s) {
260+
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
261+
}
262+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/** Provides classes representing the control flow graph. */
2+
3+
private import rust
4+
private import internal.ControlFlowGraphImpl
5+
private import internal.Completion
6+
private import internal.SuccessorType
7+
private import codeql.rust.controlflow.BasicBlocks
8+
import internal.Scope
9+
10+
/**
11+
* A control flow node.
12+
*
13+
* A control flow node is a node in the control flow graph (CFG). There is a
14+
* many-to-one relationship between CFG nodes and AST nodes.
15+
*
16+
* Only nodes that can be reached from an entry point are included in the CFG.
17+
*/
18+
class CfgNode extends Node {
19+
/** Gets the file of this control flow node. */
20+
final File getFile() { result = this.getLocation().getFile() }
21+
22+
/** Gets a successor node of a given type, if any. */
23+
final CfgNode getASuccessor(SuccessorType t) { result = super.getASuccessor(t) }
24+
25+
/** Gets an immediate successor, if any. */
26+
final CfgNode getASuccessor() { result = this.getASuccessor(_) }
27+
28+
/** Gets an immediate predecessor node of a given flow type, if any. */
29+
final CfgNode getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
30+
31+
/** Gets an immediate predecessor, if any. */
32+
final CfgNode getAPredecessor() { result = this.getAPredecessor(_) }
33+
34+
/** Gets the basic block that this control flow node belongs to. */
35+
BasicBlock getBasicBlock() { result.getANode() = this }
36+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
private import rust
2+
private import codeql.rust.controlflow.ControlFlowGraph
3+
private import SuccessorType
4+
private import SuccessorTypes
5+
6+
private newtype TCompletion =
7+
TSimpleCompletion() or
8+
TBooleanCompletion(boolean b) { b in [false, true] } or
9+
TReturnCompletion()
10+
11+
/** A completion of a statement or an expression. */
12+
abstract class Completion extends TCompletion {
13+
/** Gets a textual representation of this completion. */
14+
abstract string toString();
15+
16+
predicate isValidForSpecific(AstNode e) { none() }
17+
18+
predicate isValidFor(AstNode e) { this.isValidForSpecific(e) }
19+
20+
/** Gets a successor type that matches this completion. */
21+
abstract SuccessorType getAMatchingSuccessorType();
22+
}
23+
24+
/**
25+
* A completion that represents normal evaluation of a statement or an
26+
* expression.
27+
*/
28+
abstract class NormalCompletion extends Completion { }
29+
30+
/** A simple (normal) completion. */
31+
class SimpleCompletion extends NormalCompletion, TSimpleCompletion {
32+
override NormalSuccessor getAMatchingSuccessorType() { any() }
33+
34+
// `SimpleCompletion` is the "default" completion type, thus it is valid for
35+
// any node where there isn't another more specific completion type.
36+
override predicate isValidFor(AstNode e) { not any(Completion c).isValidForSpecific(e) }
37+
38+
override string toString() { result = "simple" }
39+
}
40+
41+
/**
42+
* A completion that represents evaluation of an expression, whose value
43+
* determines the successor.
44+
*/
45+
abstract class ConditionalCompletion extends NormalCompletion {
46+
boolean value;
47+
48+
bindingset[value]
49+
ConditionalCompletion() { any() }
50+
51+
/** Gets the Boolean value of this conditional completion. */
52+
final boolean getValue() { result = value }
53+
54+
/** Gets the dual completion. */
55+
abstract ConditionalCompletion getDual();
56+
}
57+
58+
/**
59+
* A completion that represents evaluation of an expression
60+
* with a Boolean value.
61+
*/
62+
class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
63+
BooleanCompletion() { this = TBooleanCompletion(value) }
64+
65+
override predicate isValidForSpecific(AstNode e) { e = any(If c).getCondition() }
66+
67+
/** Gets the dual Boolean completion. */
68+
override BooleanCompletion getDual() { result = TBooleanCompletion(value.booleanNot()) }
69+
70+
override BooleanSuccessor getAMatchingSuccessorType() { result.getValue() = value }
71+
72+
override string toString() { result = "boolean(" + value + ")" }
73+
}
74+
75+
/**
76+
* A completion that represents a return.
77+
*/
78+
class ReturnCompletion extends TReturnCompletion, Completion {
79+
override ReturnSuccessor getAMatchingSuccessorType() { any() }
80+
81+
override predicate isValidForSpecific(AstNode e) { e instanceof Return }
82+
83+
override string toString() { result = "return" }
84+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import ControlFlowGraphImplSpecific::CfgImpl

0 commit comments

Comments
 (0)