Skip to content

Commit 3677765

Browse files
committed
C++: Don't use dbtypes in ControlFlowNode etc.
Many classes have been declared with `extends @cfgnode` because they should be implemented internally as a control-flow node but should not expose the member predicates of `ControlFlowNode` to their users. After the transition in a1e4404 it became mandatory to convert explicitly between the `Element`-derived `ControlFlowNode` and the raw dbtype `@cfgnode`, and that commit inserted numerous such conversions as a result of having all those classes that did not derive from `Element` in the standard library. It was also confusing and error-prone that the libraries implementing `ControlFlowNode` referred to `ControlFlowNode`. This seemingly cyclic reference worked out because the libraries did not call the predicates on `ControlFlowNode` whose implementation they were part of. Both these problems are now solved by adding a new class `ControlFlowNodeBase extends Element` that should be used in preference to `@cfgnode` everywhere. This class is for exactly those use cases where `@cfgnode` should be seen as an `Element` without having too many member predicates on it. The classes that move from extending `@cfgnode` to extending `ControlFlowNodeBase` are: `BasicBlock`, `AdditionalControlFlowEdge`, `DefOrUse`, `SsaDefinition`, `SubBasicBlock` and `RangeSsaDefinition`. These previously had to define their own `toString` rootdef, which typically had some dummy string as result (like `"BasicBlock"`), but now their `toString` is part of the `Element` rootdef and should not be overridden otherwise `Element.toString` will sometimes have multiple results. Removing these dummy `toString` predicates had some effects on the tests that are included in this commit. The `getLocation` family of predicates is affected like `toString`, but the situation is slightly different. Some of these classes had genuinely useful alternative definitions of locations. Fortunately, they all used `hasLocationInfo`, which is preferred over `getLocation` by the QL engine. Because `Element` does not define `getLocationInfo`, each class can create its own rootdef of this predicate like before.
1 parent 1ed4a48 commit 3677765

27 files changed

Lines changed: 2952 additions & 2923 deletions

cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ predicate flowsToDefImpl(
9898
or
9999
// `x++`
100100
exists (CrementOperation crem
101-
| mkElement(def) = crem and
101+
| def = crem and
102102
crem.getOperand() = v.getAnAccess() and
103103
flowsToExpr(source, crem.getOperand(), pathMightOverflow))
104104
or

cpp/ql/src/Security/CWE/CWE-764/LockFlow.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ predicate tryLockCondition(VariableAccess access,
2727
(cond = call.getParent*() and
2828
cond.isCondition() and
2929
failNode = cond.getASuccessor() and
30-
unresolveElement(failNode) instanceof BasicBlockWithReturn))
30+
failNode instanceof BasicBlockWithReturn))
3131
}
3232

3333
/**

cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ predicate failedLock(MutexType t, BasicBlock lockblock, BasicBlock failblock) {
2929
exists (ControlFlowNode lock |
3030
lock = lockblock.getEnd() and
3131
lock = t.getLockAccess() and
32-
lock.getAFalseSuccessor() = mkElement(failblock)
32+
lock.getAFalseSuccessor() = failblock
3333
)
3434
}
3535

cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private cached module Cached {
6060
private predicate non_primitive_basic_block_entry_node(ControlFlowNode node) {
6161
not primitive_basic_block_entry_node(node) and
6262
not exists(node.getAPredecessor()) and
63-
successors_extended(unresolveElement(node), _)
63+
successors_extended(node, _)
6464
}
6565

6666
/**
@@ -80,7 +80,7 @@ private cached module Cached {
8080
* reuse predicates already computed for `PrimitiveBasicBlocks`.
8181
*/
8282
private predicate equalsPrimitiveBasicBlock(BasicBlock bb) {
83-
primitive_basic_block_entry_node(mkElement(bb))
83+
primitive_basic_block_entry_node(bb)
8484
and
8585
not exists(int i |
8686
i > 0 and
@@ -96,11 +96,11 @@ private cached module Cached {
9696
}
9797

9898
private predicate non_primitive_basic_block_member(ControlFlowNode node, BasicBlock bb, int pos) {
99-
(not equalsPrimitiveBasicBlock(bb) and node = mkElement(bb) and pos = 0)
99+
(not equalsPrimitiveBasicBlock(bb) and node = bb and pos = 0)
100100
or
101-
(not (unresolveElement(node) instanceof BasicBlock) and
101+
(not (node instanceof BasicBlock) and
102102
exists (ControlFlowNode pred
103-
| successors_extended(unresolveElement(pred),unresolveElement(node))
103+
| successors_extended(pred, node)
104104
| non_primitive_basic_block_member(pred, bb, pos - 1)))
105105
}
106106

@@ -117,7 +117,7 @@ private cached module Cached {
117117
predicate bb_successor_cached(BasicBlock pred, BasicBlock succ) {
118118
exists(ControlFlowNode last |
119119
basic_block_member(last, pred, bb_length(pred)-1) and
120-
last.getASuccessor() = mkElement(succ)
120+
last.getASuccessor() = succ
121121
)
122122
}
123123
}
@@ -143,15 +143,10 @@ predicate bb_successor = bb_successor_cached/2;
143143
* A - B < C - D AB is a basic block and CD is a basic block (B has two outgoing edges)
144144
* ```
145145
*/
146-
class BasicBlock extends @cfgnode {
146+
class BasicBlock extends ControlFlowNodeBase {
147147

148148
BasicBlock() {
149-
basic_block_entry_node(mkElement(this))
150-
}
151-
152-
/** Gets a textual representation of this element. */
153-
string toString() {
154-
result = "BasicBlock"
149+
basic_block_entry_node(this)
155150
}
156151

157152
predicate contains(ControlFlowNode node) {
@@ -187,7 +182,7 @@ class BasicBlock extends @cfgnode {
187182
}
188183

189184
ControlFlowNode getStart() {
190-
result = mkElement(this)
185+
result = this
191186
}
192187

193188
/** Gets the number of `ControlFlowNode`s in this basic block. */
@@ -248,9 +243,9 @@ class BasicBlock extends @cfgnode {
248243
* point or a `catch` clause of a reachable `try` statement.
249244
*/
250245
predicate isReachable() {
251-
exists(Function f | f.getBlock() = mkElement(this))
246+
exists(Function f | f.getBlock() = this)
252247
or
253-
exists(TryStmt t, BasicBlock tryblock | mkElement(this) = t.getACatchClause() and tryblock.isReachable() and tryblock.contains(t))
248+
exists(TryStmt t, BasicBlock tryblock | this = t.getACatchClause() and tryblock.isReachable() and tryblock.contains(t))
254249
or
255250
exists(BasicBlock pred | pred.getASuccessor() = this and pred.isReachable())
256251
}
@@ -272,7 +267,7 @@ predicate unreachable(ControlFlowNode n) {
272267
*/
273268
class EntryBasicBlock extends BasicBlock {
274269
EntryBasicBlock() {
275-
exists (Function f | mkElement(this) = f.getEntryPoint())
270+
exists (Function f | this = f.getEntryPoint())
276271
}
277272
}
278273

cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ private import semmle.code.cpp.controlflow.internal.ConstantExprs
2727
* function, or to the exit point of the function if there is no such
2828
* `Handler`. There are no edges from function calls to `Handler`s.
2929
*/
30-
class ControlFlowNode extends Locatable, @cfgnode {
31-
32-
ControlFlowNode getASuccessor() { successors_adapted(underlyingElement(this),unresolveElement(result)) }
30+
class ControlFlowNode extends Locatable, ControlFlowNodeBase {
31+
ControlFlowNode getASuccessor() { successors_adapted(this, result) }
3332

3433
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
3534

@@ -58,15 +57,17 @@ class ControlFlowNode extends Locatable, @cfgnode {
5857
* taken when this expression is true.
5958
*/
6059
ControlFlowNode getATrueSuccessor() {
61-
truecond(underlyingElement(this),unresolveElement(result)) and result = getASuccessor()
60+
truecond_base(this, result) and
61+
result = getASuccessor()
6262
}
6363

6464
/**
6565
* Gets a node such that the control-flow edge `(this, result)` may be
6666
* taken when this expression is false.
6767
*/
6868
ControlFlowNode getAFalseSuccessor() {
69-
falsecond(underlyingElement(this),unresolveElement(result)) and result = getASuccessor()
69+
falsecond_base(this,result) and
70+
result = getASuccessor()
7071
}
7172

7273
BasicBlock getBasicBlock() {
@@ -90,7 +91,7 @@ private cached module Cached {
9091
exists(Function f | f.getEntryPoint() = n)
9192
or
9293
// Okay to use successors_extended directly here
93-
(not successors_extended(_,unresolveElement(n)) and not successors_extended(unresolveElement(n),_))
94+
(not successors_extended(_,n) and not successors_extended(n,_))
9495
or
9596
reachable(n.getAPredecessor())
9697
or
@@ -142,6 +143,25 @@ private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condit
142143
)
143144
}
144145

146+
/**
147+
* An element that is convertible to `ControlFlowNode`. This class is similar
148+
* to `ControlFlowNode` except that is has no member predicates apart from
149+
* those inherited from `Locatable`.
150+
*
151+
* This class can be used as base class for classes that want to inherit the
152+
* extent of `ControlFlowNode` without inheriting its public member predicates.
153+
*/
154+
class ControlFlowNodeBase extends Locatable, @cfgnode {
155+
}
156+
157+
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
158+
truecond(unresolveElement(n1), unresolveElement(n2))
159+
}
160+
161+
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
162+
falsecond(unresolveElement(n1), unresolveElement(n2))
163+
}
164+
145165
/**
146166
* An abstract class that can be extended to add additional edges to the
147167
* control-flow graph. Instances of this class correspond to the source nodes
@@ -158,20 +178,19 @@ private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condit
158178
* appear to be unreachable. See the documentation on `ControlFlowNode` for
159179
* more information about the control-flow graph.
160180
*/
161-
abstract class AdditionalControlFlowEdge extends @cfgnode {
181+
abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
162182
/** Gets a target node of this edge, where the source node is `this`. */
163-
abstract ControlFlowNode getAnEdgeTarget();
164-
165-
string toString() { result = mkElement(this).(ControlFlowNode).toString() }
183+
abstract ControlFlowNodeBase getAnEdgeTarget();
166184
}
167185

168186
/**
169187
* Holds if there is a control-flow edge from `source` to `target` in either
170188
* the extractor-generated control-flow graph or in a subclass of
171189
* `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
172190
*/
173-
predicate successors_extended(@cfgnode source, @cfgnode target) {
174-
successors(source, target)
191+
predicate successors_extended(
192+
ControlFlowNodeBase source, ControlFlowNodeBase target) {
193+
successors(unresolveElement(source), unresolveElement(target))
175194
or
176-
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = mkElement(target)
195+
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
177196
}

cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ private import semmle.code.cpp.dataflow.EscapesTree
1111
*/
1212
predicate definitionUsePair(SemanticStackVariable var, Expr def, Expr use) {
1313
exists(Use u |
14-
mkElement(u) = use
14+
u = use
1515
and
16-
unresolveElement(def).(Def).reaches(true, var, u)
16+
def.(Def).reaches(true, var, u)
1717
and
1818
u.getVariable(false) = var
1919
)
@@ -24,7 +24,7 @@ predicate definitionUsePair(SemanticStackVariable var, Expr def, Expr use) {
2424
* is a definition or use, without crossing definitions of the same variable.
2525
*/
2626
predicate definitionReaches(Expr def, Expr node) {
27-
unresolveElement(def).(Def).reaches(true, _, (DefOrUse)unresolveElement(node))
27+
def.(Def).reaches(true, _, (DefOrUse)node)
2828
}
2929

3030
private predicate hasAddressOfAccess(SemanticStackVariable var) {
@@ -61,9 +61,9 @@ predicate useUsePair(SemanticStackVariable var, Expr first, Expr second) {
6161
not definition(var, first)
6262
and
6363
exists(Use u |
64-
mkElement(u) = second
64+
u = second
6565
and
66-
unresolveElement(first).(Use).reaches(false, var, u)
66+
first.(Use).reaches(false, var, u)
6767
and
6868
u.getVariable(false) = var
6969
)
@@ -76,19 +76,19 @@ predicate useUsePair(SemanticStackVariable var, Expr first, Expr second) {
7676
predicate parameterUsePair(Parameter p, VariableAccess va) {
7777
not parameterIsOverwritten(_, p) and va.getTarget() = p
7878
or
79-
exists(ParameterDef pd | pd.reaches(true, p, (Use)unresolveElement(va)))
79+
exists(ParameterDef pd | pd.reaches(true, p, (Use)va))
8080
}
8181

8282
/**
8383
* Utility class: A definition or use of a stack variable.
8484
*/
8585
library
86-
class DefOrUse extends @cfgnode {
86+
class DefOrUse extends ControlFlowNodeBase {
8787
DefOrUse() {
8888
// Uninstantiated templates are purely syntax, and only on instantiation
8989
// will they be complete with information about types, conversions, call
9090
// targets, etc.
91-
not mkElement(this).isFromUninstantiatedTemplate(_)
91+
not this.isFromUninstantiatedTemplate(_)
9292
}
9393

9494
/**
@@ -104,7 +104,7 @@ class DefOrUse extends @cfgnode {
104104
pragma[noinline]
105105
private predicate reaches_helper(boolean isDef, SemanticStackVariable v, BasicBlock bb, int i) {
106106
getVariable(isDef) = v and
107-
bb.getNode(i) = mkElement(this)
107+
bb.getNode(i) = this
108108
}
109109

110110
/**
@@ -140,24 +140,21 @@ class DefOrUse extends @cfgnode {
140140
exists(BasicBlock bb, int i |
141141
getVariable(isDef) = v
142142
and
143-
bb.getNode(i) = mkElement(this)
143+
bb.getNode(i) = this
144144
and
145145
j = min(int k | bbBarrierAt(bb, k, v, _) and k > i)
146146
)
147147
}
148-
149-
/** Gets a textual representation of this element. */
150-
string toString() { result = "DefOrUse" }
151148
}
152149

153150
library
154151
class Def extends DefOrUse {
155152
Def() {
156-
definition(_, mkElement(this))
153+
definition(_, this)
157154
}
158155

159156
override SemanticStackVariable getVariable(boolean isDef) {
160-
definition(result, mkElement(this)) and isDef = true
157+
definition(result, this) and isDef = true
161158
}
162159
}
163160

@@ -172,11 +169,11 @@ class ParameterDef extends DefOrUse {
172169
ParameterDef() {
173170
// Optimization: parameters that are not overwritten do not require
174171
// reachability analysis
175-
exists(Function f | parameterIsOverwritten(f, _) | mkElement(this) = f.getEntryPoint())
172+
exists(Function f | parameterIsOverwritten(f, _) | this = f.getEntryPoint())
176173
}
177174

178175
override SemanticStackVariable getVariable(boolean isDef) {
179-
exists(Function f | parameterIsOverwritten(f, result) | mkElement(this) = f.getEntryPoint())
176+
exists(Function f | parameterIsOverwritten(f, result) | this = f.getEntryPoint())
180177
and
181178
isDef = true
182179
}
@@ -185,22 +182,22 @@ class ParameterDef extends DefOrUse {
185182
library
186183
class Use extends DefOrUse {
187184
Use() {
188-
useOfVar(_, mkElement(this))
185+
useOfVar(_, this)
189186
}
190187

191188
override SemanticStackVariable getVariable(boolean isDef) {
192-
useOfVar(result, mkElement(this)) and isDef = false
189+
useOfVar(result, this) and isDef = false
193190
}
194191
}
195192

196193
private predicate bbUseAt(BasicBlock bb, int i, SemanticStackVariable v, Use use) {
197-
bb.getNode(i) = mkElement(use)
194+
bb.getNode(i) = use
198195
and
199196
use.getVariable(false) = v
200197
}
201198

202199
private predicate bbDefAt(BasicBlock bb, int i, SemanticStackVariable v, Def def) {
203-
bb.getNode(i) = mkElement(def)
200+
bb.getNode(i) = def
204201
and
205202
def.getVariable(true) = v
206203
}
@@ -403,8 +400,8 @@ predicate useOfVarActual(SemanticStackVariable v, VariableAccess use) {
403400
private predicate excludeReachesFunction(Function f) {
404401
exists(int defOrUses |
405402
defOrUses =
406-
count(Def def | mkElement(def).(ControlFlowNode).getControlFlowScope() = f) +
407-
count(Use use | mkElement(use).(ControlFlowNode).getControlFlowScope() = f) and
403+
count(Def def | def.(ControlFlowNode).getControlFlowScope() = f) +
404+
count(Use use | use.(ControlFlowNode).getControlFlowScope() = f) and
408405
defOrUses >= 13000
409406
)
410407
}

cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ predicate functionEntry(ControlFlowNode entry) {
2828
and not hasMultiScopeNode(function))
2929
}
3030

31-
/** Holds if `entry` is the entry point of a function. */
32-
predicate functionEntryBB(@cfgnode entry) {
33-
functionEntry(mkElement(entry))
34-
}
35-
3631
/**
3732
* Holds if `dest` is an immediate successor of `src` in the control-flow graph.
3833
*/
@@ -70,7 +65,7 @@ predicate dominates(ControlFlowNode dominator, ControlFlowNode node) {
7065
* Holds if `dominator` is an immediate dominator of `node` in the control-flow
7166
* graph of basic blocks.
7267
*/
73-
predicate bbIDominates(BasicBlock dom, BasicBlock node) = idominance(functionEntryBB/1, bb_successor/2)(_, dom, node)
68+
predicate bbIDominates(BasicBlock dom, BasicBlock node) = idominance(functionEntry/1, bb_successor/2)(_, dom, node)
7469

7570
/**
7671
* Holds if `dominator` is a strict dominator of `node` in the control-flow

cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ class GuardCondition extends Expr {
9393
exists(BasicBlock thisblock
9494
| thisblock.contains(this)
9595
| exists(BasicBlock succ
96-
| testIsTrue = true and mkElement(succ) = this.getATrueSuccessor() or
97-
testIsTrue = false and mkElement(succ) = this.getAFalseSuccessor()
96+
| testIsTrue = true and succ = this.getATrueSuccessor() or
97+
testIsTrue = false and succ = this.getAFalseSuccessor()
9898
| bbDominates(succ, controlled) and
9999
forall(BasicBlock pred
100100
| pred.getASuccessor() = succ
101-
| pred = thisblock or bbDominates(succ, pred) or not reachable(mkElement(pred)))))
101+
| pred = thisblock or bbDominates(succ, pred) or not reachable(pred))))
102102
}
103103
}
104104

0 commit comments

Comments
 (0)