Skip to content

Commit 0b6e35a

Browse files
authored
Merge pull request github#9291 from MathiasVP/swift-ipa-the-cfg
Swift: CFG for property reads and writes
2 parents 361b2aa + 80fad34 commit 0b6e35a

File tree

13 files changed

+1270
-591
lines changed

13 files changed

+1270
-591
lines changed

swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
private import swift
44
private import ControlFlowGraph
55
private import internal.ControlFlowGraphImpl
6+
private import internal.ControlFlowElements
67
private import CfgNodes
78
private import SuccessorTypes
89

@@ -198,8 +199,18 @@ private module JoinBlockPredecessors {
198199

199200
private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y)
200201

202+
private AstNode projctToAst(ControlFlowElement n) {
203+
result = n.asAstNode()
204+
or
205+
isPropertyGetterElement(n, _, result)
206+
or
207+
isPropertySetterElement(n, _, result)
208+
or
209+
isPropertyObserverElement(n, _, result)
210+
}
211+
201212
int getId(JoinBlockPredecessor jbp) {
202-
idOf(jbp.getFirstNode().(AstCfgNode).getNode(), result)
213+
idOf(projctToAst(jbp.getFirstNode().(AstCfgNode).getNode()), result)
203214
or
204215
idOf(jbp.(EntryBasicBlock).getScope(), result)
205216
}

swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import swift
44
private import BasicBlocks
55
private import ControlFlowGraph
66
private import internal.ControlFlowGraphImpl
7+
private import internal.ControlFlowElements
78
private import internal.Splitting
89

910
/** An entry node for a given scope. */
@@ -66,11 +67,11 @@ class ExitNode extends ControlFlowNode, TExitNode {
6667
*/
6768
class AstCfgNode extends ControlFlowNode, TElementNode {
6869
private Splits splits;
69-
private AstNode n;
70+
private ControlFlowElement n;
7071

7172
AstCfgNode() { this = TElementNode(_, n, splits) }
7273

73-
final override AstNode getNode() { result = n }
74+
final override ControlFlowElement getNode() { result = n }
7475

7576
override Location getLocation() { result = n.getLocation() }
7677

@@ -96,7 +97,7 @@ class AstCfgNode extends ControlFlowNode, TElementNode {
9697
class ExprCfgNode extends AstCfgNode {
9798
Expr e;
9899

99-
ExprCfgNode() { e = this.getNode() }
100+
ExprCfgNode() { e = this.getNode().asAstNode() }
100101

101102
/** Gets the underlying expression. */
102103
Expr getExpr() { result = e }

swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ private import SuccessorTypes
66
private import internal.ControlFlowGraphImpl
77
private import internal.Completion
88
private import internal.Scope
9+
private import internal.ControlFlowElements
910

1011
/** An AST node with an associated control-flow graph. */
1112
class CfgScope extends Scope instanceof CfgScope::Range_ {
1213
/** Gets the CFG scope that this scope is nested under, if any. */
1314
final CfgScope getOuterCfgScope() {
14-
exists(AstNode parent |
15-
parent = getParent(this) and
15+
exists(ControlFlowElement parent |
16+
parent.asAstNode() = getParentOfAst(this) and
1617
result = getCfgScope(parent)
1718
)
1819
}
@@ -31,7 +32,7 @@ class ControlFlowNode extends TCfgNode {
3132
string toString() { none() }
3233

3334
/** Gets the AST node that this node corresponds to, if any. */
34-
AstNode getNode() { none() }
35+
ControlFlowElement getNode() { none() }
3536

3637
/** Gets the location of this control flow node. */
3738
Location getLocation() { none() }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
private import swift
2+
private import Completion
3+
private import ControlFlowGraphImplShared
4+
private import ControlFlowElements
5+
6+
abstract class AstControlFlowTree extends ControlFlowTree {
7+
AstNode ast;
8+
9+
AstControlFlowTree() { ast = this.asAstNode() }
10+
11+
AstNode getAst() { result = ast }
12+
}
13+
14+
/**
15+
* Holds if `first` is the first element executed within control flow
16+
* element `cft`.
17+
*/
18+
predicate astFirst(AstNode cft, ControlFlowElement first) {
19+
first(any(ControlFlowTree tree | cft = tree.asAstNode()), first)
20+
}
21+
22+
/**
23+
* Holds if `last` with completion `c` is a potential last element executed
24+
* within control flow element `cft`.
25+
*/
26+
predicate astLast(AstNode cft, ControlFlowElement last, Completion c) {
27+
last(any(ControlFlowTree tree | cft = tree.asAstNode()), last, c)
28+
}
29+
30+
abstract class AstPreOrderTree extends AstControlFlowTree, PreOrderTree { }
31+
32+
abstract class AstPostOrderTree extends AstControlFlowTree, PostOrderTree { }
33+
34+
abstract class AstStandardTree extends AstControlFlowTree, StandardTree { }
35+
36+
abstract class AstStandardPreOrderTree extends AstStandardTree, StandardPreOrderTree { }
37+
38+
abstract class AstStandardPostOrderTree extends AstStandardTree, StandardPostOrderTree { }
39+
40+
abstract class AstLeafTree extends AstPreOrderTree, AstPostOrderTree, LeafTree { }

swift/ql/lib/codeql/swift/controlflow/internal/Completion.qll

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
private import swift
88
private import codeql.swift.controlflow.ControlFlowGraph
9+
private import ControlFlowElements
910
private import ControlFlowGraphImpl
1011
private import SuccessorTypes
1112

@@ -41,8 +42,8 @@ private predicate completionIsValidForStmt(Stmt stmt, Completion c) {
4142

4243
/** A completion of a statement or an expression. */
4344
abstract class Completion extends TCompletion {
44-
private predicate isValidForSpecific(AstNode n) {
45-
completionIsValidForStmt(n, this)
45+
private predicate isValidForSpecific(ControlFlowElement n) {
46+
completionIsValidForStmt(n.asAstNode(), this)
4647
or
4748
mustHaveBooleanCompletion(n) and
4849
(
@@ -52,22 +53,24 @@ abstract class Completion extends TCompletion {
5253
this = TBooleanCompletion(_)
5354
)
5455
or
55-
mustHaveMatchingCompletion(n) and
56+
mustHaveMatchingCompletion(n.asAstNode()) and
5657
(
57-
exists(boolean value | isMatchingConstant(n, value) | this = TMatchingCompletion(value))
58+
exists(boolean value | isMatchingConstant(n.asAstNode(), value) |
59+
this = TMatchingCompletion(value)
60+
)
5861
or
59-
not isMatchingConstant(n, _) and
62+
not isMatchingConstant(n.asAstNode(), _) and
6063
this = TMatchingCompletion(_)
6164
)
6265
or
63-
mustHaveThrowCompletion(n, this)
66+
mustHaveThrowCompletion(n.asAstNode(), this)
6467
}
6568

6669
/** Holds if this completion is valid for node `n`. */
67-
predicate isValidFor(AstNode n) {
70+
predicate isValidFor(ControlFlowElement n) {
6871
this.isValidForSpecific(n)
6972
or
70-
mayHaveThrowCompletion(n, this)
73+
mayHaveThrowCompletion(n.asAstNode(), this)
7174
or
7275
not any(Completion c).isValidForSpecific(n) and
7376
this = TSimpleCompletion()
@@ -87,25 +90,35 @@ abstract class Completion extends TCompletion {
8790
}
8891

8992
/** Holds if node `n` has the Boolean constant value `value`. */
90-
private predicate isBooleanConstant(AstNode n, boolean value) {
93+
private predicate isBooleanConstant(ControlFlowElement n, boolean value) {
9194
mustHaveBooleanCompletion(n) and
92-
value = n.(BooleanLiteralExpr).getValue()
95+
value = n.asAstNode().(BooleanLiteralExpr).getValue()
9396
or
9497
// Boolean consants hidden inside conversions are also
9598
// constants that resolve to the same value.
96-
isBooleanConstant(n.getResolveStep(), value)
99+
exists(ControlFlowElement parent |
100+
parent.asAstNode() = n.asAstNode().getResolveStep() and
101+
isBooleanConstant(parent, value)
102+
)
97103
}
98104

99105
/**
100106
* Holds if a normal completion of `n` must be a Boolean completion.
101107
*/
102-
private predicate mustHaveBooleanCompletion(AstNode n) { inBooleanContext(n) }
108+
private predicate mustHaveBooleanCompletion(ControlFlowElement n) { inBooleanContext(n) }
103109

104110
/**
105111
* Holds if `n` is used in a Boolean context. That is, the value
106112
* that `n` evaluates to determines a true/false branch successor.
107113
*/
108-
private predicate inBooleanContext(AstNode n) {
114+
private predicate inBooleanContext(ControlFlowElement n) {
115+
astInBooleanContext(n.asAstNode()) or
116+
astInBooleanContext(n.(PropertyGetterElement).getRef()) or
117+
astInBooleanContext(n.(PropertySetterElement).getAssignExpr()) or
118+
astInBooleanContext(n.(PropertyObserverElement).getAssignExpr())
119+
}
120+
121+
private predicate astInBooleanContext(AstNode n) {
109122
n = any(ConditionElement condElem).getFullyUnresolved()
110123
or
111124
n = any(StmtCondition stmtCond).getFullyUnresolved()
@@ -115,30 +128,30 @@ private predicate inBooleanContext(AstNode n) {
115128
exists(LogicalAndExpr parent |
116129
n = parent.getLeftOperand().getFullyConverted()
117130
or
118-
inBooleanContext(parent) and
131+
astInBooleanContext(parent) and
119132
n = parent.getRightOperand().getFullyConverted()
120133
)
121134
or
122135
exists(LogicalOrExpr parent |
123136
n = parent.getLeftOperand().getFullyConverted()
124137
or
125-
inBooleanContext(parent) and
138+
astInBooleanContext(parent) and
126139
n = parent.getRightOperand().getFullyConverted()
127140
)
128141
or
129-
n = any(NotExpr parent | inBooleanContext(parent)).getOperand().getFullyConverted()
142+
n = any(NotExpr parent | astInBooleanContext(parent)).getOperand().getFullyConverted()
130143
or
131144
exists(IfExpr ifExpr |
132145
ifExpr.getCondition().getFullyConverted() = n
133146
or
134-
inBooleanContext(ifExpr) and
147+
astInBooleanContext(ifExpr) and
135148
n = ifExpr.getBranch(_).getFullyConverted()
136149
)
137150
or
138151
exists(ForEachStmt foreach | n = foreach.getWhere().getFullyConverted())
139152
or
140153
exists(Exprs::Conversions::ConversionOrIdentityTree parent |
141-
inBooleanContext(parent) and
154+
astInBooleanContext(parent.getAst()) and
142155
parent.convertsFrom(n)
143156
)
144157
}
@@ -477,3 +490,18 @@ class ThrowCompletion extends TThrowCompletion, Completion {
477490

478491
override string toString() { result = "throw" }
479492
}
493+
494+
/**
495+
* Hold if `c` represents normal evaluation of a statement or an
496+
* expression.
497+
*/
498+
predicate completionIsNormal(Completion c) { c instanceof NormalCompletion }
499+
500+
/**
501+
* Hold if `c` represents simple (normal) evaluation of a statement or an
502+
* expression.
503+
*/
504+
predicate completionIsSimple(Completion c) { c instanceof SimpleCompletion }
505+
506+
/** Holds if `c` is a valid completion for `e`. */
507+
predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) }

0 commit comments

Comments
 (0)