Skip to content

Commit 8d62d59

Browse files
authored
Merge pull request #12807 from MathiasVP/dataflow-for-keypaths
Swift: Dataflow for keypaths
2 parents c44fbc1 + 231b0fc commit 8d62d59

File tree

12 files changed

+474
-444
lines changed

12 files changed

+474
-444
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,25 @@ class ApplyExprCfgNode extends ExprCfgNode {
179179
class CallExprCfgNode extends ApplyExprCfgNode {
180180
override CallExpr e;
181181
}
182+
183+
/** A control-flow node that wraps a key-path application. */
184+
class KeyPathApplicationExprCfgNode extends ExprCfgNode {
185+
override KeyPathApplicationExpr e;
186+
187+
/**
188+
* Gets the control-flow node that wraps the key-path of
189+
* this control-flow element.
190+
*/
191+
CfgNode getKeyPath() { result.getAst() = e.getKeyPath() }
192+
193+
/**
194+
* Gets the control-flow node that wraps the base of
195+
* this control-flow element.
196+
*/
197+
CfgNode getBase() { result.getAst() = e.getBase() }
198+
}
199+
200+
/** A control-flow node that wraps a key-path expression. */
201+
class KeyPathExprCfgNode extends ExprCfgNode {
202+
override KeyPathExpr e;
203+
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ module CfgScope {
5858
}
5959

6060
private class KeyPathScope extends Range_ instanceof KeyPathExpr {
61-
AstControlFlowTree tree;
61+
KeyPathControlFlowTree tree;
6262

6363
KeyPathScope() { tree.getAst() = this }
6464

@@ -76,6 +76,12 @@ module CfgScope {
7676

7777
final override predicate exit(ControlFlowElement last, Completion c) { last(tree, last, c) }
7878
}
79+
80+
private class KeyPathControlFlowTree extends StandardPostOrderTree, KeyPathElement {
81+
final override ControlFlowElement getChildElement(int i) {
82+
result.asAstNode() = expr.getComponent(i)
83+
}
84+
}
7985
}
8086

8187
/** Holds if `first` is first executed when entering `scope`. */
@@ -88,6 +94,14 @@ predicate succExit(CfgScope::Range_ scope, ControlFlowElement last, Completion c
8894
scope.exit(last, c)
8995
}
9096

97+
private class KeyPathComponentTree extends AstStandardPostOrderTree {
98+
override KeyPathComponent ast;
99+
100+
final override ControlFlowElement getChildElement(int i) {
101+
result.asAstNode() = ast.getSubscriptArgument(i).getExpr().getFullyConverted()
102+
}
103+
}
104+
91105
/**
92106
* Control-flow for statements.
93107
*/

swift/ql/lib/codeql/swift/dataflow/Ssa.qll

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,39 @@ module Ssa {
2121

2222
class ExitBasicBlock = BasicBlocks::ExitBasicBlock;
2323

24-
class SourceVariable = VarDecl;
24+
private newtype TSourceVariable =
25+
TNormalSourceVariable(VarDecl v) or
26+
TKeyPathSourceVariable(EntryNode entry) { entry.getScope() instanceof KeyPathExpr }
27+
28+
abstract class SourceVariable extends TSourceVariable {
29+
abstract string toString();
30+
31+
VarDecl asVarDecl() { none() }
32+
33+
EntryNode asKeyPath() { none() }
34+
35+
DeclRefExpr getAnAccess() { result.getDecl() = this.asVarDecl() }
36+
}
37+
38+
private class NormalSourceVariable extends SourceVariable, TNormalSourceVariable {
39+
VarDecl v;
40+
41+
NormalSourceVariable() { this = TNormalSourceVariable(v) }
42+
43+
override string toString() { result = v.toString() }
44+
45+
override VarDecl asVarDecl() { result = v }
46+
}
47+
48+
private class KeyPathSourceVariable extends SourceVariable, TKeyPathSourceVariable {
49+
EntryNode enter;
50+
51+
KeyPathSourceVariable() { this = TKeyPathSourceVariable(enter) }
52+
53+
override string toString() { result = enter.toString() }
54+
55+
override EntryNode asKeyPath() { result = enter }
56+
}
2557

2658
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
2759
exists(AssignExpr assign |
@@ -40,17 +72,22 @@ module Ssa {
4072
// ```
4173
exists(NamedPattern pattern |
4274
bb.getNode(i).getNode().asAstNode() = pattern and
43-
v = pattern.getVarDecl() and
75+
v.asVarDecl() = pattern.getVarDecl() and
4476
certain = true
4577
)
4678
or
47-
v instanceof ParamDecl and
48-
bb.getNode(i).getNode().asAstNode() = v and
79+
exists(ParamDecl p |
80+
p = v.asVarDecl() and
81+
bb.getNode(i).getNode().asAstNode() = p and
82+
certain = true
83+
)
84+
or
85+
bb.getNode(i) = v.asKeyPath() and
4986
certain = true
5087
or
5188
// Mark the subexpression as a write of the local variable declared in the `TapExpr`.
5289
exists(TapExpr tap |
53-
v = tap.getVar() and
90+
v.asVarDecl() = tap.getVar() and
5491
bb.getNode(i).getNode().asAstNode() = tap.getSubExpr() and
5592
certain = true
5693
)
@@ -60,7 +97,7 @@ module Ssa {
6097
exists(DeclRefExpr ref |
6198
not isLValue(ref) and
6299
bb.getNode(i).getNode().asAstNode() = ref and
63-
v = ref.getDecl() and
100+
v.asVarDecl() = ref.getDecl() and
64101
certain = true
65102
)
66103
or
@@ -71,24 +108,26 @@ module Ssa {
71108
)
72109
or
73110
exists(ExitNode exit, AbstractFunctionDecl func |
74-
func.getAParam() = v or func.getSelfParam() = v
75-
|
111+
[func.getAParam(), func.getSelfParam()] = v.asVarDecl() and
76112
bb.getNode(i) = exit and
77-
modifiableParam(v) and
113+
modifiableParam(v.asVarDecl()) and
78114
bb.getScope() = func and
79115
certain = true
80116
)
81117
or
82118
// Mark the `TapExpr` as a read of the of the local variable.
83119
exists(TapExpr tap |
84-
v = tap.getVar() and
120+
v.asVarDecl() = tap.getVar() and
85121
bb.getNode(i).getNode().asAstNode() = tap and
86122
certain = true
87123
)
88124
}
89125
}
90126

91-
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
127+
/**
128+
* INTERNAL: Do not use.
129+
*/
130+
module SsaImpl = SsaImplCommon::Make<SsaInput>;
92131

93132
cached
94133
class Definition extends SsaImpl::Definition {
@@ -97,7 +136,7 @@ module Ssa {
97136

98137
cached
99138
ControlFlowNode getARead() {
100-
exists(VarDecl v, SsaInput::BasicBlock bb, int i |
139+
exists(SsaInput::SourceVariable v, SsaInput::BasicBlock bb, int i |
101140
SsaImpl::ssaDefReachesRead(v, this, bb, i) and
102141
SsaInput::variableRead(bb, i, v, true) and
103142
result = bb.getNode(i)

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ newtype TDataFlowCall =
7474
TPropertyGetterCall(PropertyGetterCfgNode getter) or
7575
TPropertySetterCall(PropertySetterCfgNode setter) or
7676
TPropertyObserverCall(PropertyObserverCfgNode observer) or
77+
TKeyPathCall(KeyPathApplicationExprCfgNode keyPathApplication) or
7778
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
7879
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
7980
}
@@ -89,6 +90,9 @@ class DataFlowCall extends TDataFlowCall {
8990
/** Gets the underlying source code call, if any. */
9091
ApplyExprCfgNode asCall() { none() }
9192

93+
/** Gets the underlying key-path application node, if any. */
94+
KeyPathApplicationExprCfgNode asKeyPath() { none() }
95+
9296
/**
9397
* Gets the i'th argument of call.class
9498
* The qualifier is considered to have index `-1`.
@@ -138,6 +142,25 @@ private class NormalCall extends DataFlowCall, TNormalCall {
138142
override Location getLocation() { result = apply.getLocation() }
139143
}
140144

145+
private class KeyPathCall extends DataFlowCall, TKeyPathCall {
146+
private KeyPathApplicationExprCfgNode apply;
147+
148+
KeyPathCall() { this = TKeyPathCall(apply) }
149+
150+
override KeyPathApplicationExprCfgNode asKeyPath() { result = apply }
151+
152+
override CfgNode getArgument(int i) {
153+
i = -1 and
154+
result = apply.getBase()
155+
}
156+
157+
override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(apply.getScope()) }
158+
159+
override string toString() { result = apply.toString() }
160+
161+
override Location getLocation() { result = apply.getLocation() }
162+
}
163+
141164
class PropertyGetterCall extends DataFlowCall, TPropertyGetterCall {
142165
private PropertyGetterCfgNode getter;
143166

0 commit comments

Comments
 (0)