Skip to content

Commit 49fee35

Browse files
authored
Merge pull request #13947 from rdmarsh2/rdmarsh2/swift/dictionary-flow-tuples
Swift: collection/tuple content for dictionary flow
2 parents 32ed82e + 603f2cd commit 49fee35

File tree

8 files changed

+298
-3
lines changed

8 files changed

+298
-3
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added flow steps through `Dictionary` keys and values.

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

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ private import codeql.swift.dataflow.FlowSummary as FlowSummary
99
private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
1010
private import codeql.swift.frameworks.StandardLibrary.PointerTypes
1111
private import codeql.swift.frameworks.StandardLibrary.Array
12+
private import codeql.swift.frameworks.StandardLibrary.Dictionary
1213

1314
/** Gets the callable in which this node occurs. */
1415
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() }
@@ -114,6 +115,9 @@ private module Cached {
114115
any(ApplyExpr apply).getQualifier(), any(TupleElementExpr te).getSubExpr(),
115116
any(SubscriptExpr se).getBase()
116117
])
118+
} or
119+
TDictionarySubscriptNode(SubscriptExpr e) {
120+
e.getBase().getType().getCanonicalType() instanceof CanonicalDictionaryType
117121
}
118122

119123
private predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
@@ -296,6 +300,28 @@ import Cached
296300
/** Holds if `n` should be hidden from path explanations. */
297301
predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode }
298302

303+
/**
304+
* The intermediate node for a dictionary subscript operation `dict[key]`. In a write, this is used
305+
* as the destination of the `storeStep`s that add `TupleContent`s and the source of the storeStep
306+
* that adds `CollectionContent`. In a read, this is the destination of the `readStep` that pops
307+
* `CollectionContent` and the source of the `readStep` that pops `TupleContent[0]`
308+
*/
309+
private class DictionarySubscriptNode extends NodeImpl, TDictionarySubscriptNode {
310+
SubscriptExpr expr;
311+
312+
DictionarySubscriptNode() { this = TDictionarySubscriptNode(expr) }
313+
314+
override DataFlowCallable getEnclosingCallable() {
315+
result.asSourceCallable() = expr.getEnclosingCallable()
316+
}
317+
318+
override string toStringImpl() { result = "DictionarySubscriptNode" }
319+
320+
override Location getLocationImpl() { result = expr.getLocation() }
321+
322+
SubscriptExpr getExpr() { result = expr }
323+
}
324+
299325
private module ParameterNodes {
300326
abstract class ParameterNodeImpl extends NodeImpl {
301327
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
@@ -735,6 +761,32 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
735761
c instanceof OptionalSomeContentSet
736762
)
737763
or
764+
// assignment to a dictionary value via subscript operator, with intermediate step
765+
// `dict[key] = value`
766+
exists(AssignExpr assign, SubscriptExpr subscript |
767+
subscript = assign.getDest() and
768+
(
769+
subscript.getArgument(0).getExpr() = node1.asExpr() and
770+
node2.(DictionarySubscriptNode).getExpr() = subscript and
771+
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 0))
772+
or
773+
assign.getSource() = node1.asExpr() and
774+
node2.(DictionarySubscriptNode).getExpr() = subscript and
775+
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 1))
776+
or
777+
node1.(DictionarySubscriptNode).getExpr() = subscript and
778+
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = subscript.getBase() and
779+
c.isSingleton(any(Content::CollectionContent cc))
780+
)
781+
)
782+
or
783+
// creation of a dictionary `[key: value, ...]`
784+
exists(DictionaryExpr dict |
785+
node1.asExpr() = dict.getAnElement() and
786+
node2.asExpr() = dict and
787+
c.isSingleton(any(Content::CollectionContent cc))
788+
)
789+
or
738790
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
739791
node2.(FlowSummaryNode).getSummaryNode())
740792
}
@@ -826,6 +878,17 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
826878
)
827879
)
828880
or
881+
// read of a dictionary value via subscript operator
882+
exists(SubscriptExpr subscript |
883+
subscript.getBase() = node1.asExpr() and
884+
node2.(DictionarySubscriptNode).getExpr() = subscript and
885+
c.isSingleton(any(Content::CollectionContent cc))
886+
or
887+
subscript = node2.asExpr() and
888+
node1.(DictionarySubscriptNode).getExpr() = subscript and
889+
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 1))
890+
)
891+
or
829892
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
830893
node2.(FlowSummaryNode).getSummaryNode())
831894
}
@@ -836,7 +899,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
836899
* in `x.f = newValue`.
837900
*/
838901
predicate clearsContent(Node n, ContentSet c) {
839-
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
902+
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode() and
903+
(
904+
c.isSingleton(any(Content::FieldContent fc)) or
905+
c.isSingleton(any(Content::TupleContent tc)) or
906+
c.isSingleton(any(Content::EnumContent ec))
907+
)
840908
}
841909

842910
/**

swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Array.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ private import codeql.swift.dataflow.ExternalFlow
99
* An instance of the `Array` type.
1010
*/
1111
class ArrayType extends Type {
12-
ArrayType() { this.getName().matches("Array<%") or this.getName().matches("[%]") }
12+
ArrayType() { this.getCanonicalType().getName().matches("Array<%") }
1313
}
1414

1515
/**
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Provides models for the Swift `Dictionary` class.
3+
*/
4+
5+
import swift
6+
private import codeql.swift.dataflow.ExternalFlow
7+
8+
/**
9+
* An instance of the `Dictionary` type.
10+
*/
11+
class CanonicalDictionaryType extends BoundGenericType {
12+
CanonicalDictionaryType() { this.getName().matches("Dictionary<%") }
13+
}
14+
15+
/**
16+
* A model for `Dictionary` and related class members that permit data flow.
17+
*/
18+
private class DictionarySummaries extends SummaryModelCsv {
19+
override predicate row(string row) {
20+
row =
21+
[
22+
";Dictionary;true;updateValue(_:forKey:);;;Argument[0];Argument[-1].CollectionElement.TupleElement[1];value",
23+
";Dictionary;true;updateValue(_:forKey:);;;Argument[1];Argument[-1].CollectionElement.TupleElement[0];value",
24+
";Dictionary;true;updateValue(_:forKey:);;;Argument[-1].CollectionElement.TupleElement[1];ReturnValue.OptionalSome;value"
25+
]
26+
}
27+
}

0 commit comments

Comments
 (0)