Skip to content

Commit 6c8982b

Browse files
authored
Merge pull request github#9469 from rdmarsh2/rdmarsh2/swift/dataflow-inout
Swift: Dataflow through inout parameters
2 parents 20d9aaf + d7f839a commit 6c8982b

File tree

15 files changed

+327
-23
lines changed

15 files changed

+327
-23
lines changed

swift/codegen/schema.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ ConcreteVarDecl:
11031103

11041104
ParamDecl:
11051105
_extends: VarDecl
1106+
is_inout: predicate
11061107

11071108
AssociatedTypeDecl:
11081109
_extends: AbstractTypeParamDecl

swift/extractor/visitors/DeclVisitor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class DeclVisitor : public AstVisitorBase<DeclVisitor> {
6666
void visitParamDecl(swift::ParamDecl* decl) {
6767
auto label = dispatcher_.assignNewLabel(decl);
6868
dispatcher_.emit(ParamDeclsTrap{label});
69+
if (decl->isInOut()) {
70+
dispatcher_.emit(ParamDeclIsInoutTrap{label});
71+
}
6972
emitVarDecl(decl, label);
7073
}
7174

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,15 @@ class ExprCfgNode extends AstCfgNode {
102102
/** Gets the underlying expression. */
103103
Expr getExpr() { result = e }
104104
}
105+
106+
class ApplyExprCfgNode extends ExprCfgNode {
107+
override ApplyExpr e;
108+
109+
ExprCfgNode getArgument(int index) {
110+
result.getNode().asAstNode() = e.getArgument(index).getExpr()
111+
}
112+
}
113+
114+
class CallExprCfgNode extends ApplyExprCfgNode {
115+
override CallExpr e;
116+
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ module Ssa {
7171
value.getNode().asAstNode() = var.getParentInitializer()
7272
)
7373
}
74+
75+
cached
76+
predicate isInoutDef(ExprCfgNode argument) {
77+
exists(
78+
CallExpr c, BasicBlock bb, int blockIndex, int argIndex, VarDecl v, InOutExpr argExpr // TODO: use CFG node for assignment expr
79+
|
80+
this.definesAt(v, bb, blockIndex) and
81+
bb.getNode(blockIndex).getNode().asAstNode() = c and
82+
c.getArgument(argIndex).getExpr() = argExpr and
83+
argExpr = argument.getNode().asAstNode() and
84+
argExpr.getSubExpr() = v.getAnAccess() // TODO: fields?
85+
)
86+
}
7487
}
7588

7689
cached

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ private import DataFlowPrivate
33
private import DataFlowPublic
44
private import codeql.swift.controlflow.ControlFlowGraph
55

6-
newtype TReturnKind = TNormalReturnKind()
6+
newtype TReturnKind =
7+
TNormalReturnKind() or
8+
TParamReturnKind(int i) { exists(ParamDecl param | param.getIndex() = i) }
79

810
/**
911
* Gets a node that can read the value returned from `call` with return kind
@@ -28,16 +30,33 @@ class NormalReturnKind extends ReturnKind, TNormalReturnKind {
2830
override string toString() { result = "return" }
2931
}
3032

33+
/**
34+
* A value returned from a callable using an `inout` parameter.
35+
*/
36+
class ParamReturnKind extends ReturnKind, TParamReturnKind {
37+
int index;
38+
39+
ParamReturnKind() { this = TParamReturnKind(index) }
40+
41+
int getIndex() { result = index }
42+
43+
override string toString() { result = "param(" + index + ")" }
44+
}
45+
3146
/**
3247
* A callable. This includes callables from source code, as well as callables
3348
* defined in library code.
3449
*/
3550
class DataFlowCallable extends TDataFlowCallable {
51+
AbstractFunctionDecl func;
52+
53+
DataFlowCallable() { this = TDataFlowFunc(func) }
54+
3655
/** Gets a textual representation of this callable. */
37-
string toString() { none() }
56+
string toString() { result = func.toString() }
3857

3958
/** Gets the location of this callable. */
40-
Location getLocation() { none() }
59+
Location getLocation() { result = func.getLocation() }
4160
}
4261

4362
/**
@@ -48,7 +67,7 @@ class DataFlowCall extends ExprNode {
4867
DataFlowCall() { this.asExpr() instanceof CallExpr }
4968

5069
/** Gets the enclosing callable. */
51-
DataFlowCallable getEnclosingCallable() { none() }
70+
DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(this.getCfgNode().getScope()) }
5271
}
5372

5473
cached

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

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import swift
22
private import DataFlowPublic
33
private import DataFlowDispatch
4+
private import codeql.swift.controlflow.ControlFlowGraph
45
private import codeql.swift.controlflow.CfgNodes
56
private import codeql.swift.dataflow.Ssa
67
private import codeql.swift.controlflow.BasicBlocks
@@ -20,7 +21,7 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
2021
}
2122

2223
abstract class NodeImpl extends Node {
23-
DataFlowCallable getEnclosingCallable() { none() }
24+
abstract DataFlowCallable getEnclosingCallable();
2425

2526
/** Do not call: use `getLocation()` instead. */
2627
abstract Location getLocationImpl();
@@ -60,7 +61,21 @@ private module Cached {
6061
cached
6162
newtype TNode =
6263
TExprNode(ExprCfgNode e) or
63-
TSsaDefinitionNode(Ssa::Definition def)
64+
TSsaDefinitionNode(Ssa::Definition def) or
65+
TInoutReturnNode(ParamDecl param) { param.isInout() } or
66+
TInOutUpdateNode(ParamDecl param, CallExpr call) {
67+
param.isInout() and
68+
call.getStaticTarget() = param.getDeclaringFunction()
69+
}
70+
71+
private predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
72+
def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode()) and
73+
(
74+
nodeTo instanceof InoutReturnNode
75+
implies
76+
nodeTo.(InoutReturnNode).getParameter() = def.getSourceVariable()
77+
)
78+
}
6479

6580
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
6681
exists(Ssa::Definition def |
@@ -70,14 +85,34 @@ private module Cached {
7085
or
7186
// step from def to first read
7287
nodeFrom.asDefinition() = def and
73-
nodeTo.getCfgNode() = def.getAFirstRead()
88+
nodeTo.getCfgNode() = def.getAFirstRead() and
89+
(
90+
nodeTo instanceof InoutReturnNode
91+
implies
92+
nodeTo.(InoutReturnNode).getParameter() = def.getSourceVariable()
93+
)
7494
or
7595
// use-use flow
76-
def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode())
96+
localSsaFlowStepUseUse(def, nodeFrom, nodeTo)
7797
or
98+
//localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
99+
//or
78100
// step from previous read to Phi node
79101
localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition())
80102
)
103+
or
104+
exists(ParamReturnKind kind, ExprCfgNode arg |
105+
arg =
106+
nodeFrom
107+
.(InOutUpdateNode)
108+
.getCall(kind)
109+
.getCfgNode()
110+
.(CallExprCfgNode)
111+
.getArgument(kind.getIndex()) and
112+
nodeTo.asDefinition().(Ssa::WriteDefinition).isInoutDef(arg)
113+
)
114+
or
115+
nodeFrom.asExpr() = nodeTo.asExpr().(InOutExpr).getSubExpr()
81116
}
82117

83118
/**
@@ -172,6 +207,31 @@ private module ReturnNodes {
172207

173208
override ReturnKind getKind() { result instanceof NormalReturnKind }
174209
}
210+
211+
class InoutReturnNodeImpl extends ReturnNode, TInoutReturnNode, NodeImpl {
212+
ParamDecl param;
213+
ControlFlowNode exit;
214+
215+
InoutReturnNodeImpl() {
216+
this = TInoutReturnNode(param) and
217+
exit instanceof ExitNode and
218+
exit.getScope() = param.getDeclaringFunction()
219+
}
220+
221+
override ReturnKind getKind() { result.(ParamReturnKind).getIndex() = param.getIndex() }
222+
223+
override ControlFlowNode getCfgNode() { result = exit }
224+
225+
override DataFlowCallable getEnclosingCallable() {
226+
result = TDataFlowFunc(param.getDeclaringFunction())
227+
}
228+
229+
ParamDecl getParameter() { result = param }
230+
231+
override Location getLocationImpl() { result = exit.getLocation() }
232+
233+
override string toStringImpl() { result = param.toString() + "[return]" }
234+
}
175235
}
176236

177237
import ReturnNodes
@@ -188,6 +248,26 @@ private module OutNodes {
188248
result = this and kind instanceof NormalReturnKind
189249
}
190250
}
251+
252+
class InOutUpdateNode extends OutNode, TInOutUpdateNode, NodeImpl {
253+
ParamDecl param;
254+
CallExpr call;
255+
256+
InOutUpdateNode() { this = TInOutUpdateNode(param, call) }
257+
258+
override DataFlowCall getCall(ReturnKind kind) {
259+
result.asExpr() = call and
260+
kind.(ParamReturnKind).getIndex() = param.getIndex()
261+
}
262+
263+
override DataFlowCallable getEnclosingCallable() {
264+
result = TDataFlowFunc(this.getCall(_).getCfgNode().getScope())
265+
}
266+
267+
override Location getLocationImpl() { result = call.getLocation() }
268+
269+
override string toStringImpl() { result = param.toString() }
270+
}
191271
}
192272

193273
import OutNodes

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ class SsaDefinitionNode extends Node, TSsaDefinitionNode {
8181
override Ssa::Definition asDefinition() { result = def }
8282
}
8383

84+
class InoutReturnNode extends Node instanceof InoutReturnNodeImpl {
85+
ParamDecl getParameter() { result = super.getParameter() }
86+
}
87+
8488
/**
8589
* A node associated with an object after an operation that might have
8690
* changed its state.

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
private import swift
44
private import codeql.swift.controlflow.BasicBlocks as BasicBlocks
55
private import codeql.swift.controlflow.ControlFlowGraph
6+
private import codeql.swift.controlflow.CfgNodes
67

78
class BasicBlock = BasicBlocks::BasicBlock;
89

@@ -28,6 +29,14 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
2829
certain = true
2930
)
3031
or
32+
exists(CallExpr call, Argument arg |
33+
arg.getExpr().(InOutExpr).getSubExpr() = v.getAnAccess() and
34+
call.getAnArgument() = arg and
35+
call.getStaticTarget().getParam(arg.getIndex()).isInout() and
36+
bb.getNode(i).getNode().asAstNode() = call and
37+
certain = false
38+
)
39+
or
3140
v instanceof ParamDecl and
3241
bb.getNode(i).getNode().asAstNode() = v and
3342
certain = true
@@ -42,4 +51,12 @@ predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain)
4251
v = ref.getDecl() and
4352
certain = true
4453
)
54+
or
55+
exists(ExitNode exit, AbstractFunctionDecl func |
56+
bb.getNode(i) = exit and
57+
v.(ParamDecl).isInout() and
58+
func.getAParam() = v and
59+
bb.getScope() = func and
60+
certain = true
61+
)
4562
}
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
21
private import codeql.swift.generated.decl.ParamDecl
2+
private import codeql.swift.elements.decl.AbstractFunctionDecl
33

4-
class ParamDecl extends ParamDeclBase { }
4+
class ParamDecl extends ParamDeclBase {
5+
/** Gets the function which declares this parameter. */
6+
AbstractFunctionDecl getDeclaringFunction() { result.getAParam() = this }
7+
8+
/** Gets the index of this parameter in its declaring function's parameter list. */
9+
int getIndex() { exists(AbstractFunctionDecl func | func.getParam(result) = this) }
10+
}

swift/ql/lib/codeql/swift/generated/decl/ParamDecl.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ import codeql.swift.elements.decl.VarDecl
33

44
class ParamDeclBase extends @param_decl, VarDecl {
55
override string getAPrimaryQlClass() { result = "ParamDecl" }
6+
7+
predicate isInout() { param_decl_is_inout(this) }
68
}

0 commit comments

Comments
 (0)