Skip to content

Commit f846c26

Browse files
authored
Merge pull request #10157 from MathiasVP/swift-field-flow-2
Swift: Add field flow
2 parents 1f0ca6b + a4209df commit f846c26

File tree

14 files changed

+523
-309
lines changed

14 files changed

+523
-309
lines changed

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,6 @@ module Ssa {
8181
value.(PropertyGetterCfgNode).getRef() = init
8282
)
8383
}
84-
85-
cached
86-
predicate isInoutDef(ExprCfgNode argument) {
87-
// TODO: This should probably not be only `ExprCfgNode`s.
88-
exists(
89-
ApplyExpr c, BasicBlock bb, int blockIndex, VarDecl v, InOutExpr argExpr // TODO: use CFG node for assignment expr
90-
|
91-
this.definesAt(v, bb, blockIndex) and
92-
bb.getNode(blockIndex).getNode().asAstNode() = c and
93-
[c.getAnArgument().getExpr(), c.getQualifier()] = argExpr and
94-
argExpr = argument.getNode().asAstNode() and
95-
argExpr.getSubExpr() = v.getAnAccess() // TODO: fields?
96-
)
97-
}
9884
}
9985

10086
cached

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

Lines changed: 127 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,26 @@ private module Cached {
6363
newtype TNode =
6464
TExprNode(CfgNode n, Expr e) { hasExprNode(n, e) } or
6565
TSsaDefinitionNode(Ssa::Definition def) or
66-
TInoutReturnNode(ParamDecl param) { param.isInout() } or
67-
TInOutUpdateNode(Argument arg) { arg.getExpr() instanceof InOutExpr } or
68-
TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state)
69-
70-
private predicate hasExprNode(CfgNode n, Expr e) {
71-
n.(ExprCfgNode).getExpr() = e
72-
or
73-
n.(PropertyGetterCfgNode).getRef() = e
74-
or
75-
n.(PropertySetterCfgNode).getAssignExpr() = e
76-
}
66+
TInoutReturnNode(ParamDecl param) { modifiableParam(param) } or
67+
TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) or
68+
TExprPostUpdateNode(CfgNode n) {
69+
// Obviously, the base of setters needs a post-update node
70+
n = any(PropertySetterCfgNode setter).getBase()
71+
or
72+
// The base of getters and observers needs a post-update node to support reverse reads.
73+
n = any(PropertyGetterCfgNode getter).getBase()
74+
or
75+
n = any(PropertyObserverCfgNode getter).getBase()
76+
or
77+
// Arguments that are `inout` expressions needs a post-update node,
78+
// as well as any class-like argument (since a field can be modified).
79+
// Finally, qualifiers and bases of member reference need post-update nodes to support reverse reads.
80+
hasExprNode(n,
81+
[
82+
any(Argument arg | modifiable(arg)).getExpr(), any(MemberRefExpr ref).getBase(),
83+
any(ApplyExpr apply).getQualifier()
84+
])
85+
}
7786

7887
private predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
7988
def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode()) and
@@ -102,18 +111,12 @@ private module Cached {
102111
// use-use flow
103112
localSsaFlowStepUseUse(def, nodeFrom, nodeTo)
104113
or
105-
//localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
106-
//or
114+
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
115+
or
107116
// step from previous read to Phi node
108117
localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition())
109118
)
110119
or
111-
// flow through writes to inout parameters
112-
exists(ParamReturnKind kind, ExprCfgNode arg |
113-
arg = nodeFrom.(InOutUpdateNode).getCall(kind).asCall().getArgument(kind.getIndex()) and
114-
nodeTo.asDefinition().(Ssa::WriteDefinition).isInoutDef(arg)
115-
)
116-
or
117120
// flow through `&` (inout argument)
118121
nodeFrom.asExpr() = nodeTo.asExpr().(InOutExpr).getSubExpr()
119122
or
@@ -138,10 +141,36 @@ private module Cached {
138141
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { localFlowStepCommon(nodeFrom, nodeTo) }
139142

140143
cached
141-
newtype TContentSet = TODO_TContentSet()
144+
newtype TContentSet = TSingletonContent(Content c)
142145

143146
cached
144-
newtype TContent = TODO_Content()
147+
newtype TContent = TFieldContent(FieldDecl f)
148+
}
149+
150+
/**
151+
* Holds if `arg` can be modified (by overwriting the content completely),
152+
* or if any of its fields can be overwritten by a function call.
153+
*/
154+
private predicate modifiable(Argument arg) {
155+
arg.getExpr() instanceof InOutExpr
156+
or
157+
arg.getExpr().getType() instanceof NominalType
158+
}
159+
160+
predicate modifiableParam(ParamDecl param) {
161+
param.isInout()
162+
or
163+
param instanceof SelfParamDecl
164+
}
165+
166+
private predicate hasExprNode(CfgNode n, Expr e) {
167+
n.(ExprCfgNode).getExpr() = e
168+
or
169+
n.(PropertyGetterCfgNode).getRef() = e
170+
or
171+
n.(PropertySetterCfgNode).getAssignExpr() = e
172+
or
173+
n.(PropertyObserverCfgNode).getAssignExpr() = e
145174
}
146175

147176
import Cached
@@ -341,40 +370,92 @@ private module OutNodes {
341370
}
342371
}
343372

344-
class InOutUpdateNode extends OutNode, TInOutUpdateNode, NodeImpl {
373+
class InOutUpdateArgNode extends OutNode, ExprPostUpdateNode {
345374
Argument arg;
346375

347-
InOutUpdateNode() { this = TInOutUpdateNode(arg) }
376+
InOutUpdateArgNode() {
377+
modifiable(arg) and
378+
hasExprNode(n, arg.getExpr())
379+
}
348380

349381
override DataFlowCall getCall(ReturnKind kind) {
350-
result.asCall().getExpr() = arg.getApplyExpr() and
382+
result.getAnArgument() = n and
351383
kind.(ParamReturnKind).getIndex() = arg.getIndex()
352384
}
385+
}
353386

354-
override DataFlowCallable getEnclosingCallable() {
355-
result = this.getCall(_).getEnclosingCallable()
387+
class InOutUpdateQualifierNode extends OutNode, ExprPostUpdateNode {
388+
InOutUpdateQualifierNode() { hasExprNode(n, any(ApplyExpr apply).getQualifier()) }
389+
390+
override DataFlowCall getCall(ReturnKind kind) {
391+
result.getAnArgument() = n and
392+
kind.(ParamReturnKind).getIndex() = -1
356393
}
394+
}
395+
396+
class PropertySetterOutNode extends OutNode, ExprNodeImpl {
397+
PropertySetterCfgNode setter;
357398

358-
override Location getLocationImpl() { result = arg.getLocation() }
399+
PropertySetterOutNode() { setter = this.getCfgNode() }
359400

360-
override string toStringImpl() { result = arg.toString() }
401+
override DataFlowCall getCall(ReturnKind kind) {
402+
result.(PropertySetterCall).getSetter() = setter and kind.(ParamReturnKind).getIndex() = -1
403+
}
404+
}
405+
406+
class PropertyGetterOutNode extends OutNode, ExprNodeImpl {
407+
PropertyGetterCfgNode getter;
408+
409+
PropertyGetterOutNode() { getter = this.getCfgNode() }
410+
411+
override DataFlowCall getCall(ReturnKind kind) {
412+
result.(PropertyGetterCall).getGetter() = getter and kind instanceof NormalReturnKind
413+
}
414+
}
415+
416+
class PropertyObserverOutNode extends OutNode, ExprNodeImpl {
417+
PropertyObserverCfgNode observer;
418+
419+
PropertyObserverOutNode() { observer = this.getCfgNode() }
420+
421+
override DataFlowCall getCall(ReturnKind kind) {
422+
result.(PropertyGetterCall).getGetter() = observer and kind.(ParamReturnKind).getIndex() = -1
423+
}
361424
}
362425
}
363426

364427
import OutNodes
365428

366429
predicate jumpStep(Node pred, Node succ) { none() }
367430

368-
predicate storeStep(Node node1, ContentSet c, Node node2) { none() }
431+
predicate storeStep(Node node1, ContentSet c, Node node2) {
432+
exists(MemberRefExpr ref, AssignExpr assign |
433+
ref = assign.getDest() and
434+
node1.asExpr() = assign.getSource() and
435+
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = ref.getBase() and
436+
c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember()))
437+
)
438+
}
369439

370-
predicate readStep(Node node1, ContentSet c, Node node2) { none() }
440+
predicate isLValue(Expr e) { any(AssignExpr assign).getDest() = e }
441+
442+
predicate readStep(Node node1, ContentSet c, Node node2) {
443+
exists(MemberRefExpr ref |
444+
not isLValue(ref) and
445+
node1.asExpr() = ref.getBase() and
446+
node2.asExpr() = ref and
447+
c.isSingleton(any(Content::FieldContent ct | ct.getField() = ref.getMember()))
448+
)
449+
}
371450

372451
/**
373452
* Holds if values stored inside content `c` are cleared at node `n`. For example,
374453
* any value stored inside `f` is cleared at the pre-update node associated with `x`
375454
* in `x.f = newValue`.
376455
*/
377-
predicate clearsContent(Node n, ContentSet c) { none() }
456+
predicate clearsContent(Node n, ContentSet c) {
457+
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
458+
}
378459

379460
/**
380461
* Holds if the value that is being tracked is expected to be stored inside content `c`
@@ -408,7 +489,21 @@ abstract class PostUpdateNodeImpl extends Node {
408489
abstract Node getPreUpdateNode();
409490
}
410491

411-
private module PostUpdateNodes { }
492+
private module PostUpdateNodes {
493+
class ExprPostUpdateNode extends PostUpdateNodeImpl, NodeImpl, TExprPostUpdateNode {
494+
CfgNode n;
495+
496+
ExprPostUpdateNode() { this = TExprPostUpdateNode(n) }
497+
498+
override ExprNode getPreUpdateNode() { n = result.getCfgNode() }
499+
500+
override Location getLocationImpl() { result = n.getLocation() }
501+
502+
override string toStringImpl() { result = "[post] " + n.toString() }
503+
504+
override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(n.getScope()) }
505+
}
506+
}
412507

413508
private import PostUpdateNodes
414509

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,18 +139,43 @@ class Content extends TContent {
139139
Location getLocation() { none() }
140140
}
141141

142+
module Content {
143+
/** A field of an object, for example an instance variable. */
144+
class FieldContent extends Content, TFieldContent {
145+
private FieldDecl f;
146+
147+
FieldContent() { this = TFieldContent(f) }
148+
149+
/** Gets the name of the field. */
150+
FieldDecl getField() { result = f }
151+
152+
override string toString() { result = f.toString() }
153+
}
154+
}
155+
142156
/**
143157
* An entity that represents a set of `Content`s.
144158
*
145159
* The set may be interpreted differently depending on whether it is
146160
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
147161
*/
148-
class ContentSet extends Content {
162+
class ContentSet extends TContentSet {
163+
/** Holds if this content set is the singleton `{c}`. */
164+
predicate isSingleton(Content c) { this = TSingletonContent(c) }
165+
166+
/** Gets a textual representation of this content set. */
167+
string toString() {
168+
exists(Content c |
169+
this.isSingleton(c) and
170+
result = c.toString()
171+
)
172+
}
173+
149174
/** Gets a content that may be stored into when storing into this set. */
150-
Content getAStoreContent() { result = this }
175+
Content getAStoreContent() { this.isSingleton(result) }
151176

152177
/** Gets a content that may be read from when reading from this set. */
153-
Content getAReadContent() { result = this }
178+
Content getAReadContent() { this.isSingleton(result) }
154179
}
155180

156181
/**

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSumma
2727
SummaryCall summaryDataFlowCall(Node receiver) { receiver = result.getReceiver() }
2828

2929
/** Gets the type of content `c`. */
30-
DataFlowType getContentType(Content c) { any() }
30+
DataFlowType getContentType(ContentSet c) { any() }
3131

3232
/** Gets the return type of kind `rk` for callable `c`. */
3333
bindingset[c]
@@ -109,13 +109,13 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
109109
}
110110

111111
/** Gets the textual representation of the content in the format used for flow summaries. */
112-
private string getContentSpecificCsv(Content c) {
112+
private string getContentSpecificCsv(ContentSet c) {
113113
none() // TODO once we have field flow
114114
}
115115

116116
/** Gets the textual representation of a summary component in the format used for flow summaries. */
117117
string getComponentSpecificCsv(SummaryComponent sc) {
118-
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecificCsv(c))
118+
exists(ContentSet c | sc = TContentSummaryComponent(c) and result = getContentSpecificCsv(c))
119119
or
120120
exists(ReturnKind rk |
121121
sc = TReturnSummaryComponent(rk) and

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

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

89
class BasicBlock = BasicBlocks::BasicBlock;
910

@@ -29,13 +30,6 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
2930
certain = true
3031
)
3132
or
32-
exists(ApplyExpr call, InOutExpr expr |
33-
expr = [call.getAnArgument().getExpr(), call.getQualifier()] and
34-
expr.getSubExpr() = v.getAnAccess() and
35-
bb.getNode(i).getNode().asAstNode() = call and
36-
certain = false
37-
)
38-
or
3933
v instanceof ParamDecl and
4034
bb.getNode(i).getNode().asAstNode() = v and
4135
certain = true
@@ -48,8 +42,6 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
4842
)
4943
}
5044

51-
private predicate isLValue(DeclRefExpr ref) { any(AssignExpr assign).getDest() = ref }
52-
5345
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
5446
exists(DeclRefExpr ref |
5547
not isLValue(ref) and
@@ -58,10 +50,17 @@ predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain)
5850
certain = true
5951
)
6052
or
53+
exists(InOutExpr expr |
54+
bb.getNode(i).getNode().asAstNode() = expr and
55+
expr.getSubExpr() = v.getAnAccess() and
56+
certain = true
57+
)
58+
or
6159
exists(ExitNode exit, AbstractFunctionDecl func |
60+
func.getAParam() = v or func.getSelfParam() = v
61+
|
6262
bb.getNode(i) = exit and
63-
v.(ParamDecl).isInout() and
64-
func.getAParam() = v and
63+
modifiableParam(v) and
6564
bb.getScope() = func and
6665
certain = true
6766
)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ private module Cached {
3232
nodeFrom.asExpr() = [apply.getAnArgument().getExpr(), apply.getQualifier()] and
3333
apply.getStaticTarget().getName() = ["appendLiteral(_:)", "appendInterpolation(_:)"] and
3434
e.getExpr() = [apply.getAnArgument().getExpr(), apply.getQualifier()] and
35-
nodeTo.asDefinition().(Ssa::WriteDefinition).isInoutDef(e)
35+
nodeTo.(PostUpdateNodeImpl).getPreUpdateNode().getCfgNode() = e
3636
)
3737
or
3838
// Flow from the computation of the interpolated string literal to the result of the interpolation.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ predicate localTaintStep = localTaintStepCached/2;
2626
* of `c` at sinks and inputs to additional taint steps.
2727
*/
2828
bindingset[node]
29-
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
29+
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() }

0 commit comments

Comments
 (0)