Skip to content

Commit 1c9e7cc

Browse files
committed
Add shared basic block library
1 parent 5bbd244 commit 1c9e7cc

File tree

12 files changed

+380
-1022
lines changed

12 files changed

+380
-1022
lines changed

csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll

+27-254
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,18 @@
33
*/
44

55
import csharp
6+
private import codeql.basicblock.BasicBlock as BB
67
private import ControlFlow::SuccessorTypes
8+
private import ControlFlow::SuccessorTypes as ST
9+
private import ControlFlow as CFG
10+
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as CfgImpl
11+
import CfgImpl::BasicBlock as BasicBlockImpl
712

8-
/**
9-
* A basic block, that is, a maximal straight-line sequence of control flow nodes
10-
* without branches or joins.
11-
*/
12-
class BasicBlock extends TBasicBlockStart {
13-
/** Gets an immediate successor of this basic block, if any. */
14-
BasicBlock getASuccessor() { result.getFirstNode() = this.getLastNode().getASuccessor() }
15-
16-
/** Gets an immediate successor of this basic block of a given type, if any. */
17-
BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) {
18-
result.getFirstNode() = this.getLastNode().getASuccessorByType(t)
19-
}
20-
21-
/** Gets an immediate predecessor of this basic block, if any. */
22-
BasicBlock getAPredecessor() { result.getASuccessor() = this }
13+
final class BasicBlock extends BasicBlockImpl::BasicBlock {
14+
BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) { result = this.getASuccessor(t) }
2315

24-
/** Gets an immediate predecessor of this basic block of a given type, if any. */
2516
BasicBlock getAPredecessorByType(ControlFlow::SuccessorType t) {
26-
result.getASuccessorByType(t) = this
17+
result = this.getAPredecessor(t)
2718
}
2819

2920
/**
@@ -65,23 +56,20 @@ class BasicBlock extends TBasicBlockStart {
6556
}
6657

6758
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
68-
ControlFlow::Node getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) }
59+
ControlFlow::Node getNode(int pos) { result = super.getNode(pos) }
6960

7061
/** Gets a control flow node in this basic block. */
71-
ControlFlow::Node getANode() { result = this.getNode(_) }
62+
ControlFlow::Node getANode() { result = super.getANode() }
7263

7364
/** Gets the first control flow node in this basic block. */
74-
ControlFlow::Node getFirstNode() { this = TBasicBlockStart(result) }
65+
ControlFlow::Node getFirstNode() { result = super.getFirstNode() }
7566

7667
/** Gets the last control flow node in this basic block. */
77-
ControlFlow::Node getLastNode() { result = this.getNode(this.length() - 1) }
68+
ControlFlow::Node getLastNode() { result = super.getLastNode() }
7869

7970
/** Gets the callable that this basic block belongs to. */
8071
final Callable getCallable() { result = this.getFirstNode().getEnclosingCallable() }
8172

82-
/** Gets the length of this basic block. */
83-
int length() { result = strictcount(this.getANode()) }
84-
8573
/**
8674
* Holds if this basic block immediately dominates basic block `bb`.
8775
*
@@ -103,7 +91,7 @@ class BasicBlock extends TBasicBlockStart {
10391
* basic block on line 4 (all paths from the entry point of `M`
10492
* to `return s.Length;` must go through the null check).
10593
*/
106-
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
94+
predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) }
10795

10896
/**
10997
* Holds if this basic block strictly dominates basic block `bb`.
@@ -126,7 +114,7 @@ class BasicBlock extends TBasicBlockStart {
126114
* basic block on line 4 (all paths from the entry point of `M`
127115
* to `return s.Length;` must go through the null check).
128116
*/
129-
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
117+
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
130118

131119
/**
132120
* Holds if this basic block dominates basic block `bb`.
@@ -178,15 +166,7 @@ class BasicBlock extends TBasicBlockStart {
178166
* `Console.Write(x);`. Also, the basic block starting on line 2
179167
* does not dominate the basic block on line 6.
180168
*/
181-
predicate inDominanceFrontier(BasicBlock df) {
182-
this.dominatesPredecessor(df) and
183-
not this.strictlyDominates(df)
184-
}
185-
186-
/**
187-
* Holds if this basic block dominates a predecessor of `df`.
188-
*/
189-
private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) }
169+
predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
190170

191171
/**
192172
* Gets the basic block that immediately dominates this basic block, if any.
@@ -208,7 +188,7 @@ class BasicBlock extends TBasicBlockStart {
208188
* the basic block online 4 (all paths from the entry point of `M`
209189
* to `return s.Length;` must go through the null check.
210190
*/
211-
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
191+
BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
212192

213193
/**
214194
* Holds if this basic block strictly post-dominates basic block `bb`.
@@ -234,7 +214,7 @@ class BasicBlock extends TBasicBlockStart {
234214
* line 3 (all paths to the exit point of `M` from `return s.Length;`
235215
* must go through the `WriteLine` call).
236216
*/
237-
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
217+
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
238218

239219
/**
240220
* Holds if this basic block post-dominates basic block `bb`.
@@ -262,10 +242,7 @@ class BasicBlock extends TBasicBlockStart {
262242
* This predicate is *reflexive*, so for example `Console.WriteLine("M");`
263243
* post-dominates itself.
264244
*/
265-
predicate postDominates(BasicBlock bb) {
266-
this.strictlyPostDominates(bb) or
267-
this = bb
268-
}
245+
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
269246

270247
/**
271248
* Holds if this basic block is in a loop in the control flow graph. This
@@ -274,230 +251,26 @@ class BasicBlock extends TBasicBlockStart {
274251
* necessary back edges are unreachable.
275252
*/
276253
predicate inLoop() { this.getASuccessor+() = this }
277-
278-
/** Gets a textual representation of this basic block. */
279-
string toString() { result = this.getFirstNode().toString() }
280-
281-
/** Gets the location of this basic block. */
282-
Location getLocation() { result = this.getFirstNode().getLocation() }
283-
}
284-
285-
/**
286-
* Internal implementation details.
287-
*/
288-
cached
289-
private module Internal {
290-
/** Internal representation of basic blocks. */
291-
cached
292-
newtype TBasicBlock = TBasicBlockStart(ControlFlow::Node cfn) { startsBB(cfn) }
293-
294-
/** Holds if `cfn` starts a new basic block. */
295-
private predicate startsBB(ControlFlow::Node cfn) {
296-
not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor())
297-
or
298-
cfn.isJoin()
299-
or
300-
cfn.getAPredecessor().isBranch()
301-
or
302-
/*
303-
* In cases such as
304-
* ```csharp
305-
* if (b)
306-
* M()
307-
* ```
308-
* where the `false` edge out of `b` is not present (because we can prove it
309-
* impossible), we still split up the basic block in two, in order to generate
310-
* a `ConditionBlock` which can be used by the guards library.
311-
*/
312-
313-
exists(cfn.getAPredecessorByType(any(ControlFlow::SuccessorTypes::ConditionalSuccessor s)))
314-
}
315-
316-
/**
317-
* Holds if `succ` is a control flow successor of `pred` within
318-
* the same basic block.
319-
*/
320-
private predicate intraBBSucc(ControlFlow::Node pred, ControlFlow::Node succ) {
321-
succ = pred.getASuccessor() and
322-
not startsBB(succ)
323-
}
324-
325-
/**
326-
* Holds if `cfn` is the `i`th node in basic block `bb`.
327-
*
328-
* In other words, `i` is the shortest distance from a node `bb`
329-
* that starts a basic block to `cfn` along the `intraBBSucc` relation.
330-
*/
331-
cached
332-
predicate bbIndex(ControlFlow::Node bbStart, ControlFlow::Node cfn, int i) =
333-
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
334-
335-
/**
336-
* Holds if the first node of basic block `succ` is a control flow
337-
* successor of the last node of basic block `pred`.
338-
*/
339-
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
340-
341-
/** Holds if `dom` is an immediate dominator of `bb`. */
342-
cached
343-
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
344-
idominance(entryBB/1, succBB/2)(_, dom, bb)
345-
346-
/** Holds if `pred` is a basic block predecessor of `succ`. */
347-
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
348-
349-
/** Holds if `bb` is an exit basic block that represents normal exit. */
350-
private predicate normalExitBB(BasicBlock bb) {
351-
bb.getANode().(ControlFlow::Nodes::AnnotatedExitNode).isNormal()
352-
}
353-
354-
/** Holds if `dom` is an immediate post-dominator of `bb`. */
355-
cached
356-
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
357-
idominance(normalExitBB/1, predBB/2)(_, dom, bb)
358-
}
359-
360-
private import Internal
361-
362-
/**
363-
* An entry basic block, that is, a basic block whose first node is
364-
* the entry node of a callable.
365-
*/
366-
class EntryBasicBlock extends BasicBlock {
367-
EntryBasicBlock() { entryBB(this) }
368-
}
369-
370-
/** Holds if `bb` is an entry basic block. */
371-
private predicate entryBB(BasicBlock bb) {
372-
bb.getFirstNode() instanceof ControlFlow::Nodes::EntryNode
373-
}
374-
375-
/**
376-
* An annotated exit basic block, that is, a basic block that contains
377-
* an annotated exit node.
378-
*/
379-
class AnnotatedExitBasicBlock extends BasicBlock {
380-
private boolean isNormal;
381-
382-
AnnotatedExitBasicBlock() {
383-
this.getANode() =
384-
any(ControlFlow::Nodes::AnnotatedExitNode n |
385-
if n.isNormal() then isNormal = true else isNormal = false
386-
)
387-
}
388-
389-
/** Holds if this block represents a normal exit. */
390-
predicate isNormal() { isNormal = true }
391-
}
392-
393-
/**
394-
* An exit basic block, that is, a basic block whose last node is
395-
* the exit node of a callable.
396-
*/
397-
class ExitBasicBlock extends BasicBlock {
398-
ExitBasicBlock() { this.getLastNode() instanceof ControlFlow::Nodes::ExitNode }
399254
}
400255

401-
private module JoinBlockPredecessors {
402-
private import ControlFlow::Nodes
403-
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as Impl
256+
final class EntryBasicBlock extends BasicBlock, BasicBlockImpl::EntryBasicBlock { }
404257

405-
int getId(JoinBlockPredecessor jbp) {
406-
exists(Impl::AstNode n | result = n.getId() |
407-
n = jbp.getFirstNode().getAstNode()
408-
or
409-
n = jbp.(EntryBasicBlock).getCallable()
410-
)
411-
}
258+
final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlockImpl::AnnotatedExitBasicBlock { }
412259

413-
string getSplitString(JoinBlockPredecessor jbp) {
414-
result = jbp.getFirstNode().(ElementNode).getSplitsString()
415-
or
416-
not exists(jbp.getFirstNode().(ElementNode).getSplitsString()) and
417-
result = ""
418-
}
419-
}
420-
421-
/** A basic block with more than one predecessor. */
422-
class JoinBlock extends BasicBlock {
423-
JoinBlock() { this.getFirstNode().isJoin() }
260+
final class ExitBasicBlock extends BasicBlock, BasicBlockImpl::ExitBasicBlock { }
424261

425-
/**
426-
* Gets the `i`th predecessor of this join block, with respect to some
427-
* arbitrary order.
428-
*/
429-
cached
430-
JoinBlockPredecessor getJoinBlockPredecessor(int i) {
431-
result =
432-
rank[i + 1](JoinBlockPredecessor jbp |
433-
jbp = this.getAPredecessor()
434-
|
435-
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
436-
)
437-
}
438-
}
439-
440-
/** A basic block that is an immediate predecessor of a join block. */
441-
class JoinBlockPredecessor extends BasicBlock {
442-
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
262+
final class JoinBlock extends BasicBlock, BasicBlockImpl::JoinBasicBlock {
263+
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) }
443264
}
444265

445-
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
446-
class ConditionBlock extends BasicBlock {
447-
ConditionBlock() { this.getLastNode().isCondition() }
266+
final class JoinBlockPredecessor extends BasicBlock, BasicBlockImpl::JoinPredecessorBasicBlock { }
448267

449-
/**
450-
* Holds if basic block `succ` is immediately controlled by this basic
451-
* block with conditional value `s`. That is, `succ` is an immediate
452-
* successor of this block, and `succ` can only be reached from
453-
* the callable entry point by going via the `s` edge out of this basic block.
454-
*/
455-
pragma[nomagic]
268+
final class ConditionBlock extends BasicBlock, BasicBlockImpl::ConditionBasicBlock {
456269
predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
457-
succ = this.getASuccessorByType(s) and
458-
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
270+
super.immediatelyControls(succ, s)
459271
}
460272

461-
/**
462-
* Holds if basic block `controlled` is controlled by this basic block with
463-
* conditional value `s`. That is, `controlled` can only be reached from
464-
* the callable entry point by going via the `s` edge out of this basic block.
465-
*/
466273
predicate controls(BasicBlock controlled, ConditionalSuccessor s) {
467-
/*
468-
* For this block to control the block `controlled` with `testIsTrue` the following must be true:
469-
* Execution must have passed through the test i.e. `this` must strictly dominate `controlled`.
470-
* Execution must have passed through the `testIsTrue` edge leaving `this`.
471-
*
472-
* Although "passed through the true edge" implies that `this.getATrueSuccessor()` dominates `controlled`,
473-
* the reverse is not true, as flow may have passed through another edge to get to `this.getATrueSuccessor()`
474-
* so we need to assert that `this.getATrueSuccessor()` dominates `controlled` *and* that
475-
* all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`.
476-
*
477-
* For example, in the following C# snippet:
478-
* ```csharp
479-
* if (x)
480-
* controlled;
481-
* false_successor;
482-
* uncontrolled;
483-
* ```
484-
* `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`)
485-
* or dominated by itself. Whereas in the following code:
486-
* ```csharp
487-
* if (x)
488-
* while (controlled)
489-
* also_controlled;
490-
* false_successor;
491-
* uncontrolled;
492-
* ```
493-
* the block `while controlled` is controlled because all of its predecessors are `this` (`if (x)`)
494-
* or (in the case of `also_controlled`) dominated by itself.
495-
*
496-
* The additional constraint on the predecessors of the test successor implies
497-
* that `this` strictly dominates `controlled` so that isn't necessary to check
498-
* directly.
499-
*/
500-
501-
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
274+
super.controls(controlled, s)
502275
}
503276
}

csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ private module CfgInput implements CfgShared::InputSig<Location> {
9595
t instanceof ST::SuccessorTypes::ExceptionSuccessor or
9696
t instanceof ST::SuccessorTypes::ExitSuccessor
9797
}
98+
99+
predicate idOfAstNode(AstNode node, int id) { node.getId() = id }
100+
101+
predicate idOfCfgScope(CfgScope node, int id) { idOfAstNode(node, id) }
98102
}
99103

100104
private module CfgSplittingInput implements CfgShared::SplittingInputSig<Location, CfgInput> {

0 commit comments

Comments
 (0)