Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions swift/ql/lib/change-notes/2023-08-04-collection-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* Added `DataFlow::CollectionContent`, which will enable more accurate flow through collections.
3 changes: 3 additions & 0 deletions swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,9 @@ predicate parseContent(AccessPathToken component, Content content) {
or
component.getName() = "ArrayElement" and
content instanceof Content::ArrayContent
or
component.getName() = "CollectionElement" and
content instanceof Content::CollectionContent
}

cached
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ private module Cached {
TFieldContent(FieldDecl f) or
TTupleContent(int index) { exists(any(TupleExpr te).getElement(index)) } or
TEnumContent(ParamDecl f) { exists(EnumElementDecl d | d.getAParam() = f) } or
TArrayContent()
TArrayContent() or
TCollectionContent()
}

/**
Expand Down Expand Up @@ -797,6 +798,9 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
subscript.getBase().getType() instanceof ArrayType and
c.isSingleton(any(Content::ArrayContent ac))
)
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
}

/**
Expand Down Expand Up @@ -898,7 +902,9 @@ class DataFlowExpr = Expr;
* Holds if access paths with `c` at their head always should be tracked at high
* precision. This disables adaptive access path precision for such access paths.
*/
predicate forceHighPrecision(Content c) { c instanceof Content::ArrayContent }
predicate forceHighPrecision(Content c) {
c instanceof Content::ArrayContent or c instanceof Content::CollectionContent
}

/**
* Holds if the node `n` is unreachable when the call context is `call`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ module Content {
class ArrayContent extends Content, TArrayContent {
override string toString() { result = "Array element" }
}

/** An element of a collection. */
class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "Collection element" }
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,20 @@ private string getContentSpecific(ContentSet cs) {
result = "Field[" + c.getField().getName() + "]"
)
or
exists(Content::EnumContent c |
cs.isSingleton(c) and
result = "EnumElement[" + c.getSignature() + "]"
)
or
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this, I missed it in #13795 .

exists(Content::ArrayContent c |
cs.isSingleton(c) and
result = "ArrayElement"
)
or
exists(Content::CollectionContent c |
cs.isSingleton(c) and
result = "CollectionElement"
)
}

/** Gets the textual representation of a summary component in the format used for MaD models. */
Expand All @@ -125,6 +135,16 @@ string getMadRepresentationSpecific(SummaryComponent sc) {
not rk = getReturnValueKind() and
result = "ReturnValue" + "[" + rk + "]"
)
or
exists(ContentSet c |
sc = TWithoutContentSummaryComponent(c) and
result = "WithoutContent" + c.toString()
)
or
exists(ContentSet c |
sc = TWithContentSummaryComponent(c) and
result = "WithContent" + c.toString()
)
}

/** Gets the textual representation of a parameter position in the format used for flow summaries. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ private class CollectionSummaries extends SummaryModelCsv {
";Collection;true;split(separator:maxSplits:omittingEmptySubsequences:);;;Argument[-1];ReturnValue;taint",
";Collection;true;removeFirst();;;Argument[-1];ReturnValue;taint",
";Collection;true;popFirst();;;Argument[-1];ReturnValue;taint",
";Collection;true;randomElement();;;Argument[-1].CollectionElement;ReturnValue.OptionalSome;value",
";Collection;true;randomElement();;;Argument[-1].ArrayElement;ReturnValue.OptionalSome;value",
";RangeReplaceableCollection;true;append(_:);;;Argument[0];Argument[-1];taint",
";RangeReplaceableCollection;true;append(contentsOf:);;;Argument[0];Argument[-1];taint",
";RangeReplaceableCollection;true;remove(at:);;;Argument[-1];ReturnValue;taint",
Expand Down
20 changes: 20 additions & 0 deletions swift/ql/lib/codeql/swift/frameworks/StandardLibrary/Set.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Provides models for `Set` and related Swift classes.
*/

private import codeql.swift.dataflow.ExternalFlow

/**
* A model for `Set` and related class members that permit data flow.
*/
private class SetSummaries extends SummaryModelCsv {
override predicate row(string row) {
row =
[
";Set;true;insert(_:);;;Argument[-1].CollectionElement;ReturnValue.TupleElement[1];value",
";Set;true;insert(_:);;;Argument[0];Argument[-1].CollectionElement;value",
";Set;true;insert(_:);;;Argument[0];ReturnValue.TupleElement[1];value",
";Set;true;init(_:);;;Argument[0].ArrayElement;ReturnValue.CollectionElement;value"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ private import NsObject
private import NsString
private import NsUrl
private import Sequence
private import Set
private import String
private import Url
private import UrlSession
Expand Down
32 changes: 32 additions & 0 deletions swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,19 @@ edges
| test.swift:693:5:693:5 | [post] arr6 [Array element] | test.swift:694:15:694:15 | arr6 [Array element] |
| test.swift:693:17:693:24 | call to source() | test.swift:693:5:693:5 | [post] arr6 [Array element] |
| test.swift:694:15:694:15 | arr6 [Array element] | test.swift:694:15:694:21 | ...[...] |
| test.swift:696:16:696:25 | [...] [Array element] | test.swift:697:15:697:15 | arr7 [Array element] |
| test.swift:696:17:696:24 | call to source() | test.swift:696:16:696:25 | [...] [Array element] |
| test.swift:697:15:697:15 | arr7 [Array element] | test.swift:697:15:697:34 | call to randomElement() [some:0] |
| test.swift:697:15:697:34 | call to randomElement() [some:0] | test.swift:697:15:697:35 | ...! |
| test.swift:703:5:703:5 | [post] set1 [Collection element] | test.swift:704:15:704:15 | set1 [Collection element] |
| test.swift:703:17:703:24 | call to source() | test.swift:703:5:703:5 | [post] set1 [Collection element] |
| test.swift:704:15:704:15 | set1 [Collection element] | test.swift:704:15:704:34 | call to randomElement() [some:0] |
| test.swift:704:15:704:34 | call to randomElement() [some:0] | test.swift:704:15:704:35 | ...! |
| test.swift:706:16:706:30 | call to Set<Element>.init(_:) [Collection element] | test.swift:707:15:707:15 | set2 [Collection element] |
| test.swift:706:20:706:29 | [...] [Array element] | test.swift:706:16:706:30 | call to Set<Element>.init(_:) [Collection element] |
| test.swift:706:21:706:28 | call to source() | test.swift:706:20:706:29 | [...] [Array element] |
| test.swift:707:15:707:15 | set2 [Collection element] | test.swift:707:15:707:34 | call to randomElement() [some:0] |
| test.swift:707:15:707:34 | call to randomElement() [some:0] | test.swift:707:15:707:35 | ...! |
nodes
| file://:0:0:0:0 | .a [x] | semmle.label | .a [x] |
| file://:0:0:0:0 | .str | semmle.label | .str |
Expand Down Expand Up @@ -706,6 +719,22 @@ nodes
| test.swift:693:17:693:24 | call to source() | semmle.label | call to source() |
| test.swift:694:15:694:15 | arr6 [Array element] | semmle.label | arr6 [Array element] |
| test.swift:694:15:694:21 | ...[...] | semmle.label | ...[...] |
| test.swift:696:16:696:25 | [...] [Array element] | semmle.label | [...] [Array element] |
| test.swift:696:17:696:24 | call to source() | semmle.label | call to source() |
| test.swift:697:15:697:15 | arr7 [Array element] | semmle.label | arr7 [Array element] |
| test.swift:697:15:697:34 | call to randomElement() [some:0] | semmle.label | call to randomElement() [some:0] |
| test.swift:697:15:697:35 | ...! | semmle.label | ...! |
| test.swift:703:5:703:5 | [post] set1 [Collection element] | semmle.label | [post] set1 [Collection element] |
| test.swift:703:17:703:24 | call to source() | semmle.label | call to source() |
| test.swift:704:15:704:15 | set1 [Collection element] | semmle.label | set1 [Collection element] |
| test.swift:704:15:704:34 | call to randomElement() [some:0] | semmle.label | call to randomElement() [some:0] |
| test.swift:704:15:704:35 | ...! | semmle.label | ...! |
| test.swift:706:16:706:30 | call to Set<Element>.init(_:) [Collection element] | semmle.label | call to Set<Element>.init(_:) [Collection element] |
| test.swift:706:20:706:29 | [...] [Array element] | semmle.label | [...] [Array element] |
| test.swift:706:21:706:28 | call to source() | semmle.label | call to source() |
| test.swift:707:15:707:15 | set2 [Collection element] | semmle.label | set2 [Collection element] |
| test.swift:707:15:707:34 | call to randomElement() [some:0] | semmle.label | call to randomElement() [some:0] |
| test.swift:707:15:707:35 | ...! | semmle.label | ...! |
subpaths
| test.swift:75:22:75:22 | x | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | test.swift:75:32:75:32 | [post] y |
| test.swift:114:19:114:19 | arg | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | test.swift:114:12:114:22 | call to ... |
Expand Down Expand Up @@ -835,3 +864,6 @@ subpaths
| test.swift:678:15:678:26 | ...[...] | test.swift:676:20:676:27 | call to source() | test.swift:678:15:678:26 | ...[...] | result |
| test.swift:682:15:682:27 | ...[...] | test.swift:681:21:681:28 | call to source() | test.swift:682:15:682:27 | ...[...] | result |
| test.swift:694:15:694:21 | ...[...] | test.swift:693:17:693:24 | call to source() | test.swift:694:15:694:21 | ...[...] | result |
| test.swift:697:15:697:35 | ...! | test.swift:696:17:696:24 | call to source() | test.swift:697:15:697:35 | ...! | result |
| test.swift:704:15:704:35 | ...! | test.swift:703:17:703:24 | call to source() | test.swift:704:15:704:35 | ...! | result |
| test.swift:707:15:707:35 | ...! | test.swift:706:21:706:28 | call to source() | test.swift:707:15:707:35 | ...! | result |
Original file line number Diff line number Diff line change
Expand Up @@ -811,3 +811,22 @@
| test.swift:693:5:693:5 | arr6 | test.swift:693:5:693:5 | &... |
| test.swift:694:15:694:15 | [post] arr6 | test.swift:694:15:694:15 | &... |
| test.swift:694:15:694:15 | arr6 | test.swift:694:15:694:15 | &... |
| test.swift:696:9:696:9 | SSA def(arr7) | test.swift:697:15:697:15 | arr7 |
| test.swift:696:9:696:9 | arr7 | test.swift:696:9:696:9 | SSA def(arr7) |
| test.swift:696:16:696:25 | [...] | test.swift:696:9:696:9 | arr7 |
| test.swift:697:15:697:34 | call to randomElement() | test.swift:697:15:697:35 | ...! |
| test.swift:701:9:701:9 | SSA def(set1) | test.swift:702:15:702:15 | set1 |
| test.swift:701:9:701:9 | set1 | test.swift:701:9:701:9 | SSA def(set1) |
| test.swift:701:9:701:15 | ... as ... | test.swift:701:9:701:9 | set1 |
| test.swift:701:21:701:27 | [...] | test.swift:701:9:701:15 | ... as ... |
| test.swift:702:15:702:15 | [post] set1 | test.swift:703:5:703:5 | set1 |
| test.swift:702:15:702:15 | set1 | test.swift:703:5:703:5 | set1 |
| test.swift:702:15:702:34 | call to randomElement() | test.swift:702:15:702:35 | ...! |
| test.swift:703:5:703:5 | &... | test.swift:704:15:704:15 | set1 |
| test.swift:703:5:703:5 | [post] set1 | test.swift:703:5:703:5 | &... |
| test.swift:703:5:703:5 | set1 | test.swift:703:5:703:5 | &... |
| test.swift:704:15:704:34 | call to randomElement() | test.swift:704:15:704:35 | ...! |
| test.swift:706:9:706:9 | SSA def(set2) | test.swift:707:15:707:15 | set2 |
| test.swift:706:9:706:9 | set2 | test.swift:706:9:706:9 | SSA def(set2) |
| test.swift:706:16:706:30 | call to Set<Element>.init(_:) | test.swift:706:9:706:9 | set2 |
| test.swift:707:15:707:34 | call to randomElement() | test.swift:707:15:707:35 | ...! |
13 changes: 13 additions & 0 deletions swift/ql/test/library-tests/dataflow/dataflow/test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -692,4 +692,17 @@ func testArray() {
var arr6 = [1,2,3]
arr6.insert(source(), at: 2)
sink(arg: arr6[0]) // $ flow=693

var arr7 = [source()]
sink(arg: arr7.randomElement()!) // $ flow=696
}

func testSetCollections() {
var set1: Set = [1,2,3]
sink(arg: set1.randomElement()!)
set1.insert(source())
sink(arg: set1.randomElement()!) // $flow=703

let set2 = Set([source()])
sink(arg: set2.randomElement()!) // $ flow=706
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

}