Skip to content

Commit e00390d

Browse files
authored
Merge pull request #21224 from owen-mc/go/use-shared-basic-block-lib
Go: Use shared basic block lib
2 parents e712e62 + 2f29c90 commit e00390d

File tree

7 files changed

+55
-181
lines changed

7 files changed

+55
-181
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: breaking
3+
---
4+
* The `BasicBlock` class is now defined using the shared basic blocks library. `BasicBlock.getRoot` has been replaced by `BasicBlock.getScope`. `BasicBlock.getAPredecessor` and `BasicBlock.getASuccessor` now take a `SuccessorType` argument. `ReachableJoinBlock.inDominanceFrontierOf` has been removed, so use `BasicBlock.inDominanceFrontier` instead, swapping the receiver and the argument.

go/ql/lib/qlpack.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ library: true
77
upgrades: upgrades
88
dependencies:
99
codeql/concepts: ${workspace}
10+
codeql/controlflow: ${workspace}
1011
codeql/dataflow: ${workspace}
1112
codeql/mad: ${workspace}
1213
codeql/threat-models: ${workspace}

go/ql/lib/semmle/go/controlflow/BasicBlocks.qll

Lines changed: 36 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -4,201 +4,65 @@
44

55
import go
66
private import ControlFlowGraphImpl
7+
private import codeql.controlflow.BasicBlock as BB
8+
private import codeql.controlflow.SuccessorType
9+
10+
private module Input implements BB::InputSig<Location> {
11+
/** A delineated part of the AST with its own CFG. */
12+
class CfgScope = ControlFlow::Root;
13+
14+
/** The class of control flow nodes. */
15+
class Node = ControlFlowNode;
16+
17+
/** Gets the CFG scope in which this node occurs. */
18+
CfgScope nodeGetCfgScope(Node node) { node.getRoot() = result }
19+
20+
/** Gets an immediate successor of this node. */
21+
Node nodeGetASuccessor(Node node, SuccessorType t) {
22+
result = node.getASuccessor() and
23+
(
24+
not result instanceof ControlFlow::ConditionGuardNode and t instanceof DirectSuccessor
25+
or
26+
t.(BooleanSuccessor).getValue() = result.(ControlFlow::ConditionGuardNode).getOutcome()
27+
)
28+
}
729

8-
/**
9-
* Holds if `nd` starts a new basic block.
10-
*/
11-
private predicate startsBB(ControlFlow::Node nd) {
12-
count(nd.getAPredecessor()) != 1
13-
or
14-
nd.getAPredecessor().isBranch()
15-
}
16-
17-
/**
18-
* Holds if the first node of basic block `succ` is a control flow
19-
* successor of the last node of basic block `bb`.
20-
*/
21-
private predicate succBB(BasicBlock bb, BasicBlock succ) { succ = bb.getLastNode().getASuccessor() }
22-
23-
/**
24-
* Holds if the first node of basic block `bb` is a control flow
25-
* successor of the last node of basic block `pre`.
26-
*/
27-
private predicate predBB(BasicBlock bb, BasicBlock pre) { succBB(pre, bb) }
28-
29-
/** Holds if `bb` is an entry basic block. */
30-
private predicate entryBB(BasicBlock bb) { bb.getFirstNode().isEntryNode() }
31-
32-
/** Holds if `bb` is an exit basic block. */
33-
private predicate exitBB(BasicBlock bb) { bb.getLastNode().isExitNode() }
34-
35-
cached
36-
private module Internal {
3730
/**
38-
* Holds if `succ` is a control flow successor of `nd` within the same basic block.
31+
* Holds if `node` represents an entry node to be used when calculating
32+
* dominance.
3933
*/
40-
private predicate intraBBSucc(ControlFlow::Node nd, ControlFlow::Node succ) {
41-
succ = nd.getASuccessor() and
42-
not startsBB(succ)
43-
}
34+
predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode }
4435

4536
/**
46-
* Holds if `nd` is the `i`th node in basic block `bb`.
47-
*
48-
* In other words, `i` is the shortest distance from a node `bb`
49-
* that starts a basic block to `nd` along the `intraBBSucc` relation.
37+
* Holds if `node` represents an exit node to be used when calculating
38+
* post dominance.
5039
*/
51-
cached
52-
predicate bbIndex(BasicBlock bb, ControlFlow::Node nd, int i) =
53-
shortestDistances(startsBB/1, intraBBSucc/2)(bb, nd, i)
54-
55-
cached
56-
int bbLength(BasicBlock bb) { result = strictcount(ControlFlow::Node nd | bbIndex(bb, nd, _)) }
57-
58-
cached
59-
predicate reachableBB(BasicBlock bb) {
60-
entryBB(bb)
61-
or
62-
exists(BasicBlock predBB | succBB(predBB, bb) | reachableBB(predBB))
63-
}
40+
predicate nodeIsPostDominanceExit(Node node) { node instanceof ExitNode }
6441
}
6542

66-
private import Internal
67-
68-
/** Holds if `dom` is an immediate dominator of `bb`. */
69-
cached
70-
private predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
71-
idominance(entryBB/1, succBB/2)(_, dom, bb)
72-
73-
/** Holds if `dom` is an immediate post-dominator of `bb`. */
74-
cached
75-
private predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
76-
idominance(exitBB/1, predBB/2)(_, dom, bb)
77-
78-
/**
79-
* A basic block, that is, a maximal straight-line sequence of control flow nodes
80-
* without branches or joins.
81-
*
82-
* At the database level, a basic block is represented by its first control flow node.
83-
*/
84-
class BasicBlock extends TControlFlowNode {
85-
BasicBlock() { startsBB(this) }
86-
87-
/** Gets a basic block succeeding this one. */
88-
BasicBlock getASuccessor() { succBB(this, result) }
89-
90-
/** Gets a basic block preceding this one. */
91-
BasicBlock getAPredecessor() { result.getASuccessor() = this }
92-
93-
/** Gets a node in this block. */
94-
ControlFlow::Node getANode() { result = this.getNode(_) }
95-
96-
/** Gets the node at the given position in this block. */
97-
ControlFlow::Node getNode(int pos) { bbIndex(this, result, pos) }
98-
99-
/** Gets the first node in this block. */
100-
ControlFlow::Node getFirstNode() { result = this }
43+
private module BbImpl = BB::Make<Location, Input>;
10144

102-
/** Gets the last node in this block. */
103-
ControlFlow::Node getLastNode() { result = this.getNode(this.length() - 1) }
45+
class BasicBlock = BbImpl::BasicBlock;
10446

105-
/** Gets the length of this block. */
106-
int length() { result = bbLength(this) }
47+
class EntryBasicBlock = BbImpl::EntryBasicBlock;
10748

108-
/** Gets the basic block that immediately dominates this basic block. */
109-
ReachableBasicBlock getImmediateDominator() { bbIDominates(result, this) }
110-
111-
/** Gets the innermost function or file to which this basic block belongs. */
112-
ControlFlow::Root getRoot() { result = this.getFirstNode().getRoot() }
113-
114-
/** Gets a textual representation of this basic block. */
115-
string toString() { result = "basic block" }
116-
117-
/** Gets the source location for this element. */
118-
Location getLocation() { result = this.getFirstNode().getLocation() }
119-
120-
/**
121-
* DEPRECATED: Use `getLocation()` instead.
122-
*
123-
* Holds if this basic block is at the specified location.
124-
* The location spans column `startcolumn` of line `startline` to
125-
* column `endcolumn` of line `endline` in file `filepath`.
126-
* For more information, see
127-
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
128-
*/
129-
deprecated predicate hasLocationInfo(
130-
string filepath, int startline, int startcolumn, int endline, int endcolumn
131-
) {
132-
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
133-
}
134-
}
135-
136-
/**
137-
* An entry basic block, that is, a basic block whose first node is an entry node.
138-
*/
139-
class EntryBasicBlock extends BasicBlock {
140-
EntryBasicBlock() { entryBB(this) }
49+
cached
50+
private predicate reachableBB(BasicBlock bb) {
51+
bb instanceof EntryBasicBlock
52+
or
53+
exists(BasicBlock predBB | predBB.getASuccessor(_) = bb | reachableBB(predBB))
14154
}
14255

14356
/**
14457
* A basic block that is reachable from an entry basic block.
14558
*/
14659
class ReachableBasicBlock extends BasicBlock {
14760
ReachableBasicBlock() { reachableBB(this) }
148-
149-
/**
150-
* Holds if this basic block strictly dominates `bb`.
151-
*/
152-
cached
153-
predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) }
154-
155-
/**
156-
* Holds if this basic block dominates `bb`.
157-
*
158-
* This predicate is reflexive: each reachable basic block dominates itself.
159-
*/
160-
predicate dominates(ReachableBasicBlock bb) {
161-
bb = this or
162-
this.strictlyDominates(bb)
163-
}
164-
165-
/**
166-
* Holds if this basic block strictly post-dominates `bb`.
167-
*/
168-
cached
169-
predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) }
170-
171-
/**
172-
* Holds if this basic block post-dominates `bb`.
173-
*
174-
* This predicate is reflexive: each reachable basic block post-dominates itself.
175-
*/
176-
predicate postDominates(ReachableBasicBlock bb) {
177-
bb = this or
178-
this.strictlyPostDominates(bb)
179-
}
18061
}
18162

18263
/**
18364
* A reachable basic block with more than one predecessor.
18465
*/
18566
class ReachableJoinBlock extends ReachableBasicBlock {
18667
ReachableJoinBlock() { this.getFirstNode().isJoin() }
187-
188-
/**
189-
* Holds if this basic block belongs to the dominance frontier of `b`, that is
190-
* `b` dominates a predecessor of this block, but not this block itself.
191-
*
192-
* Algorithm from Cooper et al., "A Simple, Fast Dominance Algorithm" (Figure 5),
193-
* who in turn attribute it to Ferrante et al., "The program dependence graph and
194-
* its use in optimization".
195-
*/
196-
predicate inDominanceFrontierOf(ReachableBasicBlock b) {
197-
b = this.getAPredecessor() and not b = this.getImmediateDominator()
198-
or
199-
exists(ReachableBasicBlock prev | this.inDominanceFrontierOf(prev) |
200-
b = prev.getImmediateDominator() and
201-
not b = this.getImmediateDominator()
202-
)
203-
}
20468
}

go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ module ControlFlow {
313313
*/
314314
Expr getCondition() { result = cond }
315315

316+
/** Gets the value of the condition that this node corresponds to. */
317+
boolean getOutcome() { result = outcome }
318+
316319
override Root getRoot() { result.isRootOf(cond) }
317320

318321
override string toString() { result = cond + " is " + outcome }
@@ -350,4 +353,6 @@ module ControlFlow {
350353
}
351354
}
352355

356+
class ControlFlowNode = ControlFlow::Node;
357+
353358
class Write = ControlFlow::WriteNode;

go/ql/lib/semmle/go/dataflow/SSA.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class SsaDefinition extends TSsaDefinition {
144144
abstract string prettyPrintRef();
145145

146146
/** Gets the innermost function or file to which this SSA definition belongs. */
147-
ControlFlow::Root getRoot() { result = this.getBasicBlock().getRoot() }
147+
ControlFlow::Root getRoot() { result = this.getBasicBlock().getScope() }
148148

149149
/** Gets a textual representation of this element. */
150150
string toString() { result = this.prettyPrintDef() }
@@ -285,7 +285,7 @@ abstract class SsaPseudoDefinition extends SsaImplicitDefinition {
285285
*/
286286
class SsaPhiNode extends SsaPseudoDefinition, TPhi {
287287
override SsaVariable getAnInput() {
288-
result = getDefReachingEndOf(this.getBasicBlock().getAPredecessor(), this.getSourceVariable())
288+
result = getDefReachingEndOf(this.getBasicBlock().getAPredecessor(_), this.getSourceVariable())
289289
}
290290

291291
override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {

go/ql/lib/semmle/go/dataflow/SsaImpl.qll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ private module Internal {
7171
private predicate inDefDominanceFrontier(ReachableJoinBlock bb, SsaSourceVariable v) {
7272
exists(ReachableBasicBlock defbb, SsaDefinition def |
7373
def.definesAt(defbb, _, v) and
74-
bb.inDominanceFrontierOf(defbb)
74+
defbb.inDominanceFrontier(bb)
7575
)
7676
}
7777

@@ -86,15 +86,15 @@ private module Internal {
8686

8787
/** Holds if the `i`th node of `bb` in function `f` is an entry node. */
8888
private predicate entryNode(FuncDef f, ReachableBasicBlock bb, int i) {
89-
f = bb.getRoot() and
89+
f = bb.getScope() and
9090
bb.getNode(i).isEntryNode()
9191
}
9292

9393
/**
9494
* Holds if the `i`th node of `bb` in function `f` is a function call.
9595
*/
9696
private predicate callNode(FuncDef f, ReachableBasicBlock bb, int i) {
97-
f = bb.getRoot() and
97+
f = bb.getScope() and
9898
bb.getNode(i).(IR::EvalInstruction).getExpr() instanceof CallExpr
9999
}
100100

@@ -186,7 +186,7 @@ private module Internal {
186186
* Holds if `v` is live at the beginning of any successor of basic block `bb`.
187187
*/
188188
private predicate liveAtSuccEntry(ReachableBasicBlock bb, SsaSourceVariable v) {
189-
liveAtEntry(bb.getASuccessor(), v)
189+
liveAtEntry(bb.getASuccessor(_), v)
190190
}
191191

192192
/**
@@ -317,7 +317,7 @@ private module Internal {
317317
SsaSourceVariable v, ReachableBasicBlock b1, ReachableBasicBlock b2
318318
) {
319319
varOccursInBlock(v, b1) and
320-
b2 = b1.getASuccessor()
320+
b2 = b1.getASuccessor(_)
321321
}
322322

323323
/**
@@ -335,7 +335,7 @@ private module Internal {
335335
) {
336336
varBlockReaches(v, b1, mid) and
337337
not varOccursInBlock(v, mid) and
338-
b2 = mid.getASuccessor()
338+
b2 = mid.getASuccessor(_)
339339
}
340340

341341
/**

go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ predicate writesHttpError(ReachableBasicBlock b) {
4545
predicate onlyErrors(BasicBlock block) {
4646
writesHttpError(block)
4747
or
48-
forex(ReachableBasicBlock pred | pred = block.getAPredecessor() | onlyErrors(pred))
48+
forex(ReachableBasicBlock pred | pred = block.getAPredecessor(_) | onlyErrors(pred))
4949
}
5050

5151
/** Gets a node that refers to a handler that is considered to return an HTTP error. */

0 commit comments

Comments
 (0)