Skip to content

Commit da90440

Browse files
authored
Merge pull request github#9333 from rdmarsh2/rdmarsh2/swift/dataflow-local-flow
Swift: local dataflow
2 parents 47051ec + bba3564 commit da90440

File tree

8 files changed

+212
-9
lines changed

8 files changed

+212
-9
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().asAstNode() and
65+
value.getNode().asAstNode() = a.getSource()
66+
)
67+
}
68+
}
69+
}

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

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
private import swift
22
private import DataFlowPublic
33
private import DataFlowDispatch
4+
private import codeql.swift.controlflow.CfgNodes
5+
private import codeql.swift.dataflow.Ssa
46

57
/** Gets the callable in which this node occurs. */
68
DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() }
@@ -25,22 +27,54 @@ abstract class NodeImpl extends Node {
2527
abstract string toStringImpl();
2628
}
2729

30+
private class ExprNodeImpl extends ExprNode, NodeImpl {
31+
override Location getLocationImpl() { result = expr.getLocation() }
32+
33+
override string toStringImpl() { result = expr.toString() }
34+
}
35+
36+
private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl {
37+
override Location getLocationImpl() { result = def.getLocation() }
38+
39+
override string toStringImpl() { result = def.toString() }
40+
}
41+
2842
/** A collection of cached types and predicates to be evaluated in the same stage. */
2943
cached
3044
private module Cached {
3145
cached
32-
newtype TNode = TODO_TNode()
46+
newtype TNode =
47+
TExprNode(ExprCfgNode e) or
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+
}
3365

3466
/**
3567
* This is the local flow predicate that is used as a building block in global
3668
* data flow.
3769
*/
3870
cached
39-
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { none() }
71+
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
72+
localFlowStepCommon(nodeFrom, nodeTo)
73+
}
4074

4175
/** This is the local flow predicate that is exposed. */
4276
cached
43-
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { none() }
77+
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { localFlowStepCommon(nodeFrom, nodeTo) }
4478

4579
cached
4680
newtype TContentSet = TODO_TContentSet()
@@ -58,6 +92,20 @@ private module ParameterNodes {
5892
abstract class ParameterNodeImpl extends NodeImpl {
5993
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
6094
}
95+
96+
class NormalParameterNode extends ParameterNodeImpl, TNormalParameterNode {
97+
ParamDecl param;
98+
99+
NormalParameterNode() { this = TNormalParameterNode(param) }
100+
101+
override Location getLocationImpl() { result = param.getLocation() }
102+
103+
override string toStringImpl() { result = param.toString() }
104+
105+
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
106+
none() // TODO
107+
}
108+
}
61109
}
62110

63111
import ParameterNodes

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ private import DataFlowDispatch
33
private import DataFlowPrivate
44
private import codeql.swift.controlflow.ControlFlowGraph
55
private import codeql.swift.controlflow.BasicBlocks
6+
private import codeql.swift.controlflow.CfgNodes
7+
private import codeql.swift.dataflow.Ssa
68

79
/**
810
* An element, viewed as a node in a data flow graph. Either an expression
@@ -29,6 +31,21 @@ class Node extends TNode {
2931
) {
3032
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
3133
}
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() }
3249
}
3350

3451
/**
@@ -38,13 +55,31 @@ class Node extends TNode {
3855
* to multiple `ExprNode`s, just like it may correspond to multiple
3956
* `ControlFlow::Node`s.
4057
*/
41-
class ExprNode extends Node { }
58+
class ExprNode extends Node, TExprNode {
59+
ExprCfgNode expr;
60+
61+
ExprNode() { this = TExprNode(expr) }
62+
63+
override Expr asExpr() { result = expr.getNode().asAstNode() }
64+
65+
override ControlFlowNode getCfgNode() { result = expr }
66+
}
4267

4368
/**
4469
* The value of a parameter at function entry, viewed as a node in a data
4570
* flow graph.
4671
*/
47-
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+
}
4883

4984
/**
5085
* A node associated with an object after an operation that might have

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,25 @@ class SourceVariable = VarDecl;
1616

1717
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
1818
exists(AssignExpr assign |
19-
bb.getNode(i).getNode() = assign and
19+
bb.getNode(i).getNode().asAstNode() = assign and
2020
assign.getDest() = v.getAnAccess() and
2121
certain = true
2222
)
23+
or
24+
exists(PatternBindingDecl decl, Pattern pattern |
25+
bb.getNode(i).getNode().asAstNode() = 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 }
2633

2734
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
2835
exists(DeclRefExpr ref |
2936
not isLValue(ref) and
30-
bb.getNode(i).getNode() = ref and
37+
bb.getNode(i).getNode().asAstNode() = ref and
3138
v = ref.getDecl() and
3239
certain = true
3340
)
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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
| file://:0:0:0:0 | Phi | test.swift:15:15:15:15 | DeclRefExpr |
2+
| file://:0:0:0:0 | Phi | test.swift:21:15:21:15 | DeclRefExpr |
3+
| test.swift:6:9:6:13 | WriteDef | test.swift:7:15:7:15 | DeclRefExpr |
4+
| test.swift:7:15:7:15 | DeclRefExpr | test.swift:8:10:8:10 | DeclRefExpr |
5+
| test.swift:8:5:8:10 | WriteDef | test.swift:10:15:10:15 | DeclRefExpr |
6+
| test.swift:8:10:8:10 | DeclRefExpr | test.swift:8:5:8:10 | WriteDef |
7+
| test.swift:8:10:8:10 | DeclRefExpr | test.swift:9:15:9:15 | DeclRefExpr |
8+
| test.swift:9:15:9:15 | DeclRefExpr | test.swift:11:8:11:8 | DeclRefExpr |
9+
| test.swift:12:9:12:14 | WriteDef | test.swift:13:19:13:19 | DeclRefExpr |
10+
| test.swift:12:14:12:14 | IntegerLiteralExpr | test.swift:12:9:12:14 | WriteDef |
11+
| test.swift:15:15:15:15 | DeclRefExpr | test.swift:19:14:19:14 | DeclRefExpr |
12+
| test.swift:17:10:17:10 | IntegerLiteralExpr | test.swift:17:5:17:10 | WriteDef |
13+
| test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:9:19:14 | WriteDef |
14+
| test.swift:19:14:19:14 | DeclRefExpr | test.swift:19:14:19:14 | DeclRefExpr |
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)