Skip to content

Commit 7ca0144

Browse files
authored
Merge pull request github#9342 from rdmarsh2/rdmarsh2/swift/dataflow-global-flow
Swift: initial interprocedural data flow implementation
2 parents 2492744 + ef31aec commit 7ca0144

File tree

13 files changed

+652
-90
lines changed

13 files changed

+652
-90
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ private import swift
33
cached
44
newtype TControlFlowElement =
55
TAstElement(AstNode n) or
6+
TFuncDeclElement(AbstractFunctionDecl func) { func.hasBody() } or
67
TPropertyGetterElement(Decl accessor, Expr ref) { isPropertyGetterElement(accessor, ref) } or
78
TPropertySetterElement(AccessorDecl accessor, AssignExpr assign) {
89
isPropertySetterElement(accessor, assign)
@@ -161,3 +162,13 @@ class PropertyObserverElement extends ControlFlowElement, TPropertyObserverEleme
161162

162163
AssignExpr getAssignExpr() { result = assign }
163164
}
165+
166+
class FuncDeclElement extends ControlFlowElement, TFuncDeclElement {
167+
AbstractFunctionDecl func;
168+
169+
FuncDeclElement() { this = TFuncDeclElement(func) }
170+
171+
override string toString() { result = func.toString() }
172+
173+
override Location getLocation() { result = func.getLocation() }
174+
}

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ module CfgScope {
4848

4949
private class BodyStmtCallableScope extends Range_ instanceof AbstractFunctionDecl {
5050
final override predicate entry(ControlFlowElement first) {
51-
exists(Stmts::BraceStmtTree tree |
52-
tree.getAst() = super.getBody() and
53-
tree.firstInner(first)
51+
exists(Decls::FuncDeclTree tree |
52+
tree.getAst() = this and
53+
first = tree
5454
)
5555
}
5656

5757
final override predicate exit(ControlFlowElement last, Completion c) {
58-
exists(Stmts::BraceStmtTree tree |
59-
tree.getAst() = super.getBody() and
58+
exists(Decls::FuncDeclTree tree |
59+
tree.getAst() = this and
6060
tree.last(last, c)
6161
)
6262
}
@@ -881,6 +881,21 @@ module Decls {
881881
)
882882
}
883883
}
884+
885+
class FuncDeclTree extends StandardPreOrderTree, TFuncDeclElement {
886+
AbstractFunctionDecl ast;
887+
888+
FuncDeclTree() { this = TFuncDeclElement(ast) }
889+
890+
AbstractFunctionDecl getAst() { result = ast }
891+
892+
final override ControlFlowElement getChildElement(int i) {
893+
result.asAstNode() = ast.getParam(i)
894+
or
895+
result.asAstNode() = ast.getBody() and
896+
i = ast.getNumberOfParams()
897+
}
898+
}
884899
}
885900

886901
module Exprs {

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,29 @@ module Ssa {
6464
a = bb.getNode(i).getNode().asAstNode() and
6565
value.getNode().asAstNode() = a.getSource()
6666
)
67+
or
68+
exists(VarDecl var, BasicBlock bb, int blockIndex, PatternBindingDecl pbd |
69+
this.definesAt(var, bb, blockIndex) and
70+
pbd.getAPattern() = bb.getNode(blockIndex).getNode().asAstNode() and
71+
value.getNode().asAstNode() = var.getParentInitializer()
72+
)
73+
}
74+
}
75+
76+
cached
77+
class PhiDefinition extends Definition, SsaImplCommon::PhiNode {
78+
cached
79+
override Location getLocation() {
80+
exists(BasicBlock bb, int i |
81+
this.definesAt(_, bb, i) and
82+
result = bb.getLocation()
83+
)
6784
}
85+
86+
cached
87+
Definition getPhiInput(BasicBlock bb) { SsaImplCommon::phiHasInputFromBlock(this, result, bb) }
88+
89+
cached
90+
Definition getAPhiInput() { result = this.getPhiInput(_) }
6891
}
6992
}

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

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
private import swift
22
private import DataFlowPrivate
3+
private import DataFlowPublic
34

45
newtype TReturnKind = TNormalReturnKind()
56

@@ -42,47 +43,34 @@ class DataFlowCallable extends TDataFlowCallable {
4243
* A call. This includes calls from source code, as well as call(back)s
4344
* inside library callables with a flow summary.
4445
*/
45-
class DataFlowCall extends TDataFlowCall {
46+
class DataFlowCall extends ExprNode {
47+
DataFlowCall() { this.asExpr() instanceof CallExpr }
48+
4649
/** Gets the enclosing callable. */
4750
DataFlowCallable getEnclosingCallable() { none() }
48-
49-
/** Gets a textual representation of this call. */
50-
string toString() { none() }
51-
52-
/** Gets the location of this call. */
53-
Location getLocation() { none() }
54-
55-
/**
56-
* Holds if this element is at the specified location.
57-
* The location spans column `startcolumn` of line `startline` to
58-
* column `endcolumn` of line `endline` in file `filepath`.
59-
* For more information, see
60-
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries).
61-
*/
62-
predicate hasLocationInfo(
63-
string filepath, int startline, int startcolumn, int endline, int endcolumn
64-
) {
65-
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
66-
}
6751
}
6852

6953
cached
7054
private module Cached {
7155
cached
72-
newtype TDataFlowCallable = TODO_TDataFlowCallable()
73-
74-
cached
75-
newtype TDataFlowCall = TODO_TDataFlowCall()
56+
newtype TDataFlowCallable = TDataFlowFunc(FuncDecl func)
7657

7758
/** Gets a viable run-time target for the call `call`. */
7859
cached
79-
DataFlowCallable viableCallable(DataFlowCall call) { none() }
60+
DataFlowCallable viableCallable(DataFlowCall call) {
61+
result = TDataFlowFunc(call.asExpr().(CallExpr).getStaticTarget())
62+
}
8063

8164
cached
82-
newtype TArgumentPosition = TODO_TArgumentPosition()
65+
newtype TArgumentPosition =
66+
TThisArgument() or
67+
// we rely on default exprs generated in the caller for ordering
68+
TPositionalArgument(int n) { n = any(Argument arg).getIndex() }
8369

8470
cached
85-
newtype TParameterPosition = TODO_TParameterPosition()
71+
newtype TParameterPosition =
72+
TThisParameter() or
73+
TPositionalParameter(int n) { n = any(Argument arg).getIndex() }
8674
}
8775

8876
import Cached
@@ -105,12 +93,25 @@ class ParameterPosition extends TParameterPosition {
10593
string toString() { none() }
10694
}
10795

96+
class PositionalParameterPosition extends ParameterPosition, TPositionalParameter {
97+
int getIndex() { this = TPositionalParameter(result) }
98+
}
99+
108100
/** An argument position. */
109101
class ArgumentPosition extends TArgumentPosition {
110102
/** Gets a textual representation of this position. */
111103
string toString() { none() }
112104
}
113105

106+
class PositionalArgumentPosition extends ArgumentPosition, TPositionalArgument {
107+
int getIndex() { this = TPositionalArgument(result) }
108+
}
109+
114110
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
115111
pragma[inline]
116-
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { none() }
112+
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
113+
ppos instanceof TThisParameter and
114+
apos instanceof TThisArgument
115+
or
116+
ppos.(PositionalParameterPosition).getIndex() = apos.(PositionalArgumentPosition).getIndex()
117+
}

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

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ private import DataFlowPublic
33
private import DataFlowDispatch
44
private import codeql.swift.controlflow.CfgNodes
55
private import codeql.swift.dataflow.Ssa
6+
private import codeql.swift.controlflow.BasicBlocks
7+
private import codeql.swift.dataflow.internal.SsaImplCommon as SsaImpl
68

79
/** Gets the callable in which this node occurs. */
810
DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() }
@@ -31,12 +33,25 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
3133
override Location getLocationImpl() { result = expr.getLocation() }
3234

3335
override string toStringImpl() { result = expr.toString() }
36+
37+
override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(expr.getScope()) }
3438
}
3539

3640
private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl {
3741
override Location getLocationImpl() { result = def.getLocation() }
3842

3943
override string toStringImpl() { result = def.toString() }
44+
45+
override DataFlowCallable getEnclosingCallable() {
46+
result = TDataFlowFunc(def.getBasicBlock().getScope())
47+
}
48+
}
49+
50+
private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) {
51+
exists(BasicBlock bb, int i | SsaImpl::lastRefRedef(def, bb, i, next) |
52+
def.definesAt(_, bb, i) and
53+
def = nodeFrom.asDefinition()
54+
)
4055
}
4156

4257
/** A collection of cached types and predicates to be evaluated in the same stage. */
@@ -45,7 +60,6 @@ private module Cached {
4560
cached
4661
newtype TNode =
4762
TExprNode(ExprCfgNode e) or
48-
TNormalParameterNode(ParamDecl p) or
4963
TSsaDefinitionNode(Ssa::Definition def)
5064

5165
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
@@ -60,6 +74,9 @@ private module Cached {
6074
or
6175
// use-use flow
6276
def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode())
77+
or
78+
// step from previous read to Phi node
79+
localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition())
6380
)
6481
}
6582

@@ -93,18 +110,29 @@ private module ParameterNodes {
93110
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
94111
}
95112

96-
class NormalParameterNode extends ParameterNodeImpl, TNormalParameterNode {
113+
class NormalParameterNode extends ParameterNodeImpl, SsaDefinitionNode {
97114
ParamDecl param;
98115

99-
NormalParameterNode() { this = TNormalParameterNode(param) }
116+
NormalParameterNode() {
117+
exists(BasicBlock bb, int i |
118+
super.asDefinition().definesAt(param, bb, i) and
119+
bb.getNode(i).getNode().asAstNode() = param
120+
)
121+
}
100122

101123
override Location getLocationImpl() { result = param.getLocation() }
102124

103125
override string toStringImpl() { result = param.toString() }
104126

105127
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
106-
none() // TODO
128+
exists(FuncDecl f, int index |
129+
c = TDataFlowFunc(f) and
130+
f.getParam(index) = param and
131+
pos = TPositionalParameter(index)
132+
)
107133
}
134+
135+
override DataFlowCallable getEnclosingCallable() { isParameterOf(result, _) }
108136
}
109137
}
110138

@@ -119,7 +147,16 @@ abstract class ArgumentNode extends Node {
119147
final DataFlowCall getCall() { this.argumentOf(result, _) }
120148
}
121149

122-
private module ArgumentNodes { }
150+
private module ArgumentNodes {
151+
class NormalArgumentNode extends ExprNode, ArgumentNode {
152+
NormalArgumentNode() { exists(CallExpr call | call.getAnArgument().getExpr() = this.asExpr()) }
153+
154+
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
155+
call.asExpr().(CallExpr).getArgument(pos.(PositionalArgumentPosition).getIndex()).getExpr() =
156+
this.asExpr()
157+
}
158+
}
159+
}
123160

124161
import ArgumentNodes
125162

@@ -129,7 +166,13 @@ abstract class ReturnNode extends Node {
129166
abstract ReturnKind getKind();
130167
}
131168

132-
private module ReturnNodes { }
169+
private module ReturnNodes {
170+
class ReturnReturnNode extends ReturnNode, ExprNode {
171+
ReturnReturnNode() { exists(ReturnStmt stmt | stmt.getResult() = this.asExpr()) }
172+
173+
override ReturnKind getKind() { result instanceof NormalReturnKind }
174+
}
175+
}
133176

134177
import ReturnNodes
135178

@@ -139,7 +182,13 @@ abstract class OutNode extends Node {
139182
abstract DataFlowCall getCall(ReturnKind kind);
140183
}
141184

142-
private module OutNodes { }
185+
private module OutNodes {
186+
class CallOutNode extends OutNode, DataFlowCall {
187+
override DataFlowCall getCall(ReturnKind kind) {
188+
result = this and kind instanceof NormalReturnKind
189+
}
190+
}
191+
}
143192

144193
import OutNodes
145194

@@ -169,7 +218,9 @@ class DataFlowType extends TDataFlowType {
169218
}
170219

171220
/** Gets the type of `n` used for type pruning. */
172-
DataFlowType getNodeType(NodeImpl n) { none() }
221+
DataFlowType getNodeType(NodeImpl n) {
222+
any() // return the singleton DataFlowType until we support type pruning for Swift
223+
}
173224

174225
/** Gets a string representation of a `DataFlowType`. */
175226
string ppReprType(DataFlowType t) { result = t.toString() }

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class ExprNode extends Node, TExprNode {
6969
* The value of a parameter at function entry, viewed as a node in a data
7070
* flow graph.
7171
*/
72-
class ParameterNode extends Node, TNormalParameterNode instanceof ParameterNodeImpl { }
72+
class ParameterNode extends Node, SsaDefinitionNode instanceof ParameterNodeImpl { }
7373

7474
/**
7575
*/
@@ -98,7 +98,7 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
9898
}
9999

100100
/** Gets a node corresponding to expression `e`. */
101-
ExprNode exprNode(DataFlowExpr e) { none() }
101+
ExprNode exprNode(DataFlowExpr e) { result.asExpr() = e }
102102

103103
/**
104104
* Gets the node corresponding to the value of parameter `p` at function entry.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
2727
v.getParentPattern() = pattern and
2828
certain = true
2929
)
30+
or
31+
v instanceof ParamDecl and
32+
bb.getNode(i).getNode().asAstNode() = v and
33+
certain = true
3034
}
3135

3236
private predicate isLValue(DeclRefExpr ref) { any(AssignExpr assign).getDest() = ref }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
private import codeql.swift.generated.expr.Argument
2+
private import codeql.swift.elements.expr.ApplyExpr
23

34
class Argument extends ArgumentBase {
45
override string toString() { result = this.getLabel() + ": " + this.getExpr().toString() }
6+
7+
int getIndex() { any(ApplyExpr apply).getArgument(result) = this }
58
}

0 commit comments

Comments
 (0)