Skip to content

Commit cf22ade

Browse files
authored
Swift: initial local data flow implementation
1 parent 117a1ad commit cf22ade

File tree

8 files changed

+179
-7
lines changed

8 files changed

+179
-7
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
cached
2+
module Ssa {
3+
private import swift
4+
private import internal.SsaImplCommon as SsaImplCommon
5+
private import internal.SsaImplSpecific as SsaImplSpecific
6+
private import codeql.swift.controlflow.CfgNodes
7+
private import codeql.swift.controlflow.ControlFlowGraph
8+
private import codeql.swift.controlflow.BasicBlocks
9+
10+
cached
11+
class Definition extends SsaImplCommon::Definition {
12+
cached
13+
Location getLocation() { none() }
14+
15+
cached
16+
ControlFlowNode getARead() {
17+
exists(VarDecl v, BasicBlock bb, int i |
18+
SsaImplCommon::ssaDefReachesRead(v, this, bb, i) and
19+
SsaImplSpecific::variableRead(bb, i, v, true) and
20+
result = bb.getNode(i)
21+
)
22+
}
23+
24+
cached
25+
ControlFlowNode getAFirstRead() {
26+
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
27+
this.definesAt(_, bb1, i1) and
28+
SsaImplCommon::adjacentDefNoUncertainReads(this, bb1, i1, bb2, i2) and
29+
result = bb2.getNode(i2)
30+
)
31+
}
32+
33+
cached
34+
predicate adjacentReadPair(ControlFlowNode read1, ControlFlowNode read2) {
35+
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
36+
read1 = bb1.getNode(i1) and
37+
SsaImplSpecific::variableRead(bb1, i1, _, true) and
38+
SsaImplCommon::adjacentDefNoUncertainReads(this, bb1, i1, bb2, i2) and
39+
read2 = bb2.getNode(i2)
40+
)
41+
}
42+
}
43+
44+
cached
45+
class WriteDefinition extends Definition, SsaImplCommon::WriteDefinition {
46+
cached
47+
override Location getLocation() {
48+
exists(BasicBlock bb, int i |
49+
this.definesAt(_, bb, i) and
50+
result = bb.getNode(i).getLocation()
51+
)
52+
}
53+
54+
/**
55+
* Holds if this SSA definition represents a direct assignment of `value`
56+
* to the underlying variable.
57+
*/
58+
cached
59+
predicate assigns(ExprCfgNode value) {
60+
exists(
61+
AssignExpr a, BasicBlock bb, int i // TODO: use CFG node for assignment expr
62+
|
63+
this.definesAt(_, bb, i) and
64+
a = bb.getNode(i).getNode() and
65+
value.getNode() = a.getSource()
66+
)
67+
}
68+
}
69+
}

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ private import swift
22
private import DataFlowPublic
33
private import DataFlowDispatch
44
private import codeql.swift.controlflow.CfgNodes
5+
private import codeql.swift.dataflow.Ssa
56

67
/** Gets the callable in which this node occurs. */
78
DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() }
@@ -32,24 +33,48 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
3233
override string toStringImpl() { result = expr.toString() }
3334
}
3435

36+
private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl {
37+
override Location getLocationImpl() { result = def.getLocation() }
38+
39+
override string toStringImpl() { result = def.toString() }
40+
}
41+
3542
/** A collection of cached types and predicates to be evaluated in the same stage. */
3643
cached
3744
private module Cached {
3845
cached
3946
newtype TNode =
4047
TExprNode(ExprCfgNode e) or
41-
TNormalParameterNode(ParamDecl p)
48+
TNormalParameterNode(ParamDecl p) or
49+
TSsaDefinitionNode(Ssa::Definition def)
50+
51+
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
52+
exists(Ssa::Definition def |
53+
// Step from assignment RHS to def
54+
def.(Ssa::WriteDefinition).assigns(nodeFrom.getCfgNode()) and
55+
nodeTo.asDefinition() = def
56+
or
57+
// step from def to first read
58+
nodeFrom.asDefinition() = def and
59+
nodeTo.getCfgNode() = def.getAFirstRead()
60+
or
61+
// use-use flow
62+
def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode())
63+
)
64+
}
4265

4366
/**
4467
* This is the local flow predicate that is used as a building block in global
4568
* data flow.
4669
*/
4770
cached
48-
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { none() }
71+
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
72+
localFlowStepCommon(nodeFrom, nodeTo)
73+
}
4974

5075
/** This is the local flow predicate that is exposed. */
5176
cached
52-
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { none() }
77+
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { localFlowStepCommon(nodeFrom, nodeTo) }
5378

5479
cached
5580
newtype TContentSet = TODO_TContentSet()

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import DataFlowPrivate
44
private import codeql.swift.controlflow.ControlFlowGraph
55
private import codeql.swift.controlflow.BasicBlocks
66
private import codeql.swift.controlflow.CfgNodes
7+
private import codeql.swift.dataflow.Ssa
78

89
/**
910
* An element, viewed as a node in a data flow graph. Either an expression
@@ -30,6 +31,21 @@ class Node extends TNode {
3031
) {
3132
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
3233
}
34+
35+
/**
36+
* Gets this node's underlying expression, if any.
37+
*/
38+
Expr asExpr() { none() }
39+
40+
/**
41+
* Gets this data flow node's corresponding control flow node.
42+
*/
43+
ControlFlowNode getCfgNode() { none() }
44+
45+
/**
46+
* Gets this node's underlying SSA definition, if any.
47+
*/
48+
Ssa::Definition asDefinition() { none() }
3349
}
3450

3551
/**
@@ -39,17 +55,31 @@ class Node extends TNode {
3955
* to multiple `ExprNode`s, just like it may correspond to multiple
4056
* `ControlFlow::Node`s.
4157
*/
42-
class ExprNode extends Node {
58+
class ExprNode extends Node, TExprNode {
4359
ExprCfgNode expr;
4460

4561
ExprNode() { this = TExprNode(expr) }
62+
63+
override Expr asExpr() { result = expr.getNode() }
64+
65+
override ControlFlowNode getCfgNode() { result = expr }
4666
}
4767

4868
/**
4969
* The value of a parameter at function entry, viewed as a node in a data
5070
* flow graph.
5171
*/
52-
class ParameterNode extends Node { }
72+
class ParameterNode extends Node, TNormalParameterNode instanceof ParameterNodeImpl { }
73+
74+
/**
75+
*/
76+
class SsaDefinitionNode extends Node, TSsaDefinitionNode {
77+
Ssa::Definition def;
78+
79+
SsaDefinitionNode() { this = TSsaDefinitionNode(def) }
80+
81+
override Ssa::Definition asDefinition() { result = def }
82+
}
5383

5484
/**
5585
* A node associated with an object after an operation that might have

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
2020
assign.getDest() = v.getAnAccess() and
2121
certain = true
2222
)
23+
or
24+
exists(PatternBindingDecl decl, Pattern pattern |
25+
bb.getNode(i).getNode() = pattern and
26+
decl.getAPattern() = pattern and
27+
v.getParentPattern() = pattern and
28+
certain = true
29+
)
2330
}
2431

2532
private predicate isLValue(DeclRefExpr ref) { any(AssignExpr assign).getDest() = ref }
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
21
private import codeql.swift.generated.decl.VarDecl
2+
private import codeql.swift.elements.expr.DeclRefExpr
33

4-
class VarDecl extends VarDeclBase { }
4+
class VarDecl extends VarDeclBase {
5+
DeclRefExpr getAnAccess() { result.getDecl() = this }
6+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
| file://:0:0:0:0 | Phi | test.swift:15:15:15:15 | DeclRefExpr[VarDecl[var t2]] |
2+
| test.swift:6:9:6:13 | WriteDef | test.swift:7:15:7:15 | DeclRefExpr[VarDecl[var t1]] |
3+
| test.swift:7:15:7:15 | DeclRefExpr[VarDecl[var t1]] | test.swift:8:10:8:10 | DeclRefExpr[VarDecl[var t1]] |
4+
| test.swift:8:5:8:10 | WriteDef | test.swift:10:15:10:15 | DeclRefExpr[VarDecl[var t2]] |
5+
| test.swift:8:10:8:10 | DeclRefExpr[VarDecl[var t1]] | test.swift:8:5:8:10 | WriteDef |
6+
| test.swift:8:10:8:10 | DeclRefExpr[VarDecl[var t1]] | test.swift:9:15:9:15 | DeclRefExpr[VarDecl[var t1]] |
7+
| test.swift:9:15:9:15 | DeclRefExpr[VarDecl[var t1]] | test.swift:11:8:11:8 | DeclRefExpr[VarDecl[var t1]] |
8+
| test.swift:12:9:12:14 | WriteDef | test.swift:13:19:13:19 | DeclRefExpr[VarDecl[var t2]] |
9+
| test.swift:12:14:12:14 | IntegerLiteralExpr(0) | test.swift:12:9:12:14 | WriteDef |
10+
| test.swift:17:5:17:10 | WriteDef | test.swift:21:15:21:15 | DeclRefExpr[VarDecl[var t1]] |
11+
| test.swift:17:10:17:10 | IntegerLiteralExpr(0) | test.swift:17:5:17:10 | WriteDef |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import swift
2+
import codeql.swift.dataflow.DataFlow
3+
4+
from DataFlow::Node pred, DataFlow::Node succ
5+
where DataFlow::localFlowStep(pred, succ)
6+
select pred, succ
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
func source() -> Int { return 0; }
2+
func sink(arg: Int) {}
3+
4+
func intraprocedural_with_local_flow() -> Void {
5+
var t2: Int
6+
var t1: Int = source()
7+
sink(arg: t1)
8+
t2 = t1
9+
sink(arg: t1)
10+
sink(arg: t2)
11+
if(t1 != 0) {
12+
t2 = 0
13+
sink(arg: t2)
14+
}
15+
sink(arg: t2)
16+
17+
t1 = 0;
18+
while(false) {
19+
t1 = t2
20+
}
21+
sink(arg: t1)
22+
}

0 commit comments

Comments
 (0)