diff --git a/swift/ql/lib/change-notes/2023-08-28-shared-capture-flow.md b/swift/ql/lib/change-notes/2023-08-28-shared-capture-flow.md new file mode 100644 index 000000000000..0620c2c7a7b8 --- /dev/null +++ b/swift/ql/lib/change-notes/2023-08-28-shared-capture-flow.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* Introduced support for flow through captured variables that properly adheres to inter-procedural control flow. diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 4d4f76d533d1..b7992116705c 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -10,6 +10,7 @@ private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import codeql.swift.frameworks.StandardLibrary.PointerTypes private import codeql.swift.frameworks.StandardLibrary.Array private import codeql.swift.frameworks.StandardLibrary.Dictionary +private import codeql.dataflow.VariableCapture as VariableCapture /** Gets the callable in which this node occurs. */ DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() } @@ -76,6 +77,16 @@ private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl { } } +private class CaptureNodeImpl extends CaptureNode, NodeImpl { + override Location getLocationImpl() { result = this.getSynthesizedCaptureNode().getLocation() } + + override string toStringImpl() { result = this.getSynthesizedCaptureNode().toString() } + + override DataFlowCallable getEnclosingCallable() { + result.asSourceCallable() = this.getSynthesizedCaptureNode().getEnclosingCallable() + } +} + private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) { exists(BasicBlock bb, int i | def.lastRefRedef(bb, i, next) | def.definesAt(_, bb, i) and @@ -116,6 +127,7 @@ private module Cached { any(SubscriptExpr se).getBase() ]) } or + TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn) or TDictionarySubscriptNode(SubscriptExpr e) { e.getBase().getType().getCanonicalType() instanceof CanonicalDictionaryType } @@ -234,6 +246,9 @@ private module Cached { // flow through a flow summary (extension of `SummaryModelCsv`) FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), nodeTo.(FlowSummaryNode).getSummaryNode(), true) + or + // flow step according to the closure capture library + captureValueStep(nodeFrom, nodeTo) } /** @@ -261,7 +276,8 @@ private module Cached { TTupleContent(int index) { exists(any(TupleExpr te).getElement(index)) } or TEnumContent(ParamDecl f) { exists(EnumElementDecl d | d.getAParam() = f) } or TArrayContent() or - TCollectionContent() + TCollectionContent() or + TCapturedVariableContent(CapturedVariable v) } /** @@ -327,7 +343,7 @@ private module ParameterNodes { predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() } /** Gets the parameter associated with this node, if any. */ - ParamDecl getParameter() { none() } + override ParamDecl getParameter() { none() } } class SourceParameterNode extends ParameterNodeImpl, TSourceParameterNode { @@ -550,7 +566,7 @@ private module ReturnNodes { result = TDataFlowFunc(param.getDeclaringFunction()) } - ParamDecl getParameter() { result = param } + override ParamDecl getParameter() { result = param } override Location getLocationImpl() { result = exit.getLocation() } @@ -677,6 +693,144 @@ private module OutNodes { import OutNodes +/** + * Holds if there is a data flow step from `e1` to `e2` that only steps from + * child to parent in the AST. + */ +private predicate simpleAstFlowStep(Expr e1, Expr e2) { + e2.(IfExpr).getBranch(_) = e1 + or + e2.(AssignExpr).getSource() = e1 + or + e2.(ArrayExpr).getAnElement() = e1 +} + +private predicate closureFlowStep(Expr e1, Expr e2) { + simpleAstFlowStep(e1, e2) + or + exists(Ssa::WriteDefinition def | + def.getARead().getNode().asAstNode() = e2 and + def.assigns(any(CfgNode cfg | cfg.getNode().asAstNode() = e1)) + ) +} + +private module CaptureInput implements VariableCapture::InputSig { + private import swift as S + private import codeql.swift.controlflow.BasicBlocks as B + + class Location = S::Location; + + class BasicBlock instanceof B::BasicBlock { + string toString() { result = super.toString() } + + Callable getEnclosingCallable() { result = super.getScope() } + + Location getLocation() { result = super.getLocation() } + } + + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { + result.(B::BasicBlock).immediatelyDominates(bb) + } + + BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.(B::BasicBlock).getASuccessor() } + + //TODO: support capture of `this` in lambdas + class CapturedVariable instanceof S::VarDecl { + CapturedVariable() { + any(S::CapturedDecl capturedDecl).getDecl() = this and + exists(this.getEnclosingCallable()) + } + + string toString() { result = super.toString() } + + Callable getCallable() { result = super.getEnclosingCallable() } + + Location getLocation() { result = super.getLocation() } + } + + class CapturedParameter extends CapturedVariable instanceof S::ParamDecl { } + + class Expr instanceof S::AstNode { + string toString() { result = super.toString() } + + Location getLocation() { result = super.getLocation() } + + predicate hasCfgNode(BasicBlock bb, int i) { + this = bb.(B::BasicBlock).getNode(i).getNode().asAstNode() + } + } + + class VariableWrite extends Expr { + CapturedVariable variable; + Expr source; + + VariableWrite() { + exists(S::Assignment a | this = a | + a.getDest().(DeclRefExpr).getDecl() = variable and + source = a.getSource() + ) + or + exists(S::PatternBindingDecl pbd, S::NamedPattern np | this = pbd and pbd.getAPattern() = np | + np.getVarDecl() = variable and + source = np.getMatchingExpr() + ) + // TODO: support multiple variables in LHS of =, in both of above cases. + } + + CapturedVariable getVariable() { result = variable } + + Expr getSource() { result = source } + } + + class VariableRead extends Expr instanceof S::DeclRefExpr { + CapturedVariable v; + + VariableRead() { this.getDecl() = v /* TODO: this should be an R-value only. */ } + + CapturedVariable getVariable() { result = v } + } + + class ClosureExpr extends Expr instanceof S::Callable { + ClosureExpr() { any(S::CapturedDecl c).getScope() = this } + + predicate hasBody(Callable body) { this = body } + + predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) } + } + + class Callable extends S::Callable { + predicate isConstructor() { this instanceof S::Initializer } + } +} + +class CapturedVariable = CaptureInput::CapturedVariable; + +class CapturedParameter = CaptureInput::CapturedParameter; + +module CaptureFlow = VariableCapture::Flow; + +private CaptureFlow::ClosureNode asClosureNode(Node n) { + result = n.(CaptureNode).getSynthesizedCaptureNode() or + result.(CaptureFlow::ExprNode).getExpr() = n.asExpr() or + result.(CaptureFlow::ExprPostUpdateNode).getExpr() = + n.(PostUpdateNode).getPreUpdateNode().asExpr() or + result.(CaptureFlow::ParameterNode).getParameter() = n.getParameter() or + result.(CaptureFlow::ThisParameterNode).getCallable().getSelfParam() = n.getParameter() or + result.(CaptureFlow::MallocNode).getClosureExpr() = n.getCfgNode().getNode().asAstNode() // TODO: figure out why the java version had PostUpdateNode logic here +} + +private predicate captureStoreStep(Node node1, Content::CapturedVariableContent c, Node node2) { + CaptureFlow::storeStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2)) +} + +private predicate captureReadStep(Node node1, Content::CapturedVariableContent c, Node node2) { + CaptureFlow::readStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2)) +} + +predicate captureValueStep(Node node1, Node node2) { + CaptureFlow::localFlowStep(asClosureNode(node1), asClosureNode(node2)) +} + predicate jumpStep(Node pred, Node succ) { FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(), succ.(FlowSummaryNode).getSummaryNode()) @@ -789,6 +943,8 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { or FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c, node2.(FlowSummaryNode).getSummaryNode()) + or + captureStoreStep(node1, any(Content::CapturedVariableContent cvc | c.isSingleton(cvc)), node2) } predicate isLValue(Expr e) { any(AssignExpr assign).getDest() = e } @@ -891,6 +1047,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) { or FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c, node2.(FlowSummaryNode).getSummaryNode()) + or + captureReadStep(node1, any(Content::CapturedVariableContent cvc | c.isSingleton(cvc)), node2) } /** @@ -982,6 +1140,17 @@ private module PostUpdateNodes { result.(FlowSummaryNode).getSummaryNode()) } } + + class CapturePostUpdateNode extends PostUpdateNodeImpl, CaptureNode { + private CaptureNode pre; + + CapturePostUpdateNode() { + CaptureFlow::capturePostUpdateNode(this.getSynthesizedCaptureNode(), + pre.getSynthesizedCaptureNode()) + } + + override Node getPreUpdateNode() { result = pre } + } } private import PostUpdateNodes diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll index 04ab0e27a102..302a727456b7 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll @@ -51,6 +51,11 @@ class Node extends TNode { * Gets this node's underlying SSA definition, if any. */ Ssa::Definition asDefinition() { none() } + + /** + * Gets the parameter that corresponds to this node, if any. + */ + ParamDecl getParameter() { none() } } /** @@ -96,7 +101,7 @@ class ParameterNode extends Node instanceof ParameterNodeImpl { result = this.(ParameterNodeImpl).getEnclosingCallable() } - ParamDecl getParameter() { result = this.(ParameterNodeImpl).getParameter() } + override ParamDecl getParameter() { result = this.(ParameterNodeImpl).getParameter() } } /** @@ -109,9 +114,7 @@ class SsaDefinitionNode extends Node, TSsaDefinitionNode { override Ssa::Definition asDefinition() { result = def } } -class InoutReturnNode extends Node instanceof InoutReturnNodeImpl { - ParamDecl getParameter() { result = super.getParameter() } -} +class InoutReturnNode extends Node instanceof InoutReturnNodeImpl { } /** * A node associated with an object after an operation that might have @@ -129,6 +132,18 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl { Node getPreUpdateNode() { result = super.getPreUpdateNode() } } +/** + * A synthesized data flow node representing a closure object that tracks + * captured variables. + */ +class CaptureNode extends Node, TCaptureNode { + private CaptureFlow::SynthesizedCaptureNode cn; + + CaptureNode() { this = TCaptureNode(cn) } + + CaptureFlow::SynthesizedCaptureNode getSynthesizedCaptureNode() { result = cn } +} + /** * Gets a node corresponding to expression `e`. */ @@ -234,6 +249,17 @@ module Content { class CollectionContent extends Content, TCollectionContent { override string toString() { result = "Collection element" } } + + /** A captured variable. */ + class CapturedVariableContent extends Content, TCapturedVariableContent { + CapturedVariable v; + + CapturedVariableContent() { this = TCapturedVariableContent(v) } + + CapturedVariable getVariable() { result = v } + + override string toString() { result = v.toString() } + } } /** diff --git a/swift/ql/test/TestUtilities/InlineFlowTest.qll b/swift/ql/test/TestUtilities/InlineFlowTest.qll new file mode 100644 index 000000000000..4ab2a7cdf38f --- /dev/null +++ b/swift/ql/test/TestUtilities/InlineFlowTest.qll @@ -0,0 +1,117 @@ +/** + * Provides a simple base test for flow-related tests using inline expectations. + * + * Example for a test.ql: + * ```ql + * import swift + * import TestUtilities.InlineFlowTest + * import DefaultFlowTest + * import PathGraph + * + * from PathNode source, PathNode sink + * where flowPath(source, sink) + * select sink, source, sink, "$@", source, source.toString() + * ``` + * + * To declare expectations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files. + * Example of the corresponding test file, e.g. Test.java + * ```swift + * func source() -> Any { return nil } + * func taint() -> Any { return nil } + * func sink(_ o: Any) { } + * + * func test() { + * let s = source() + * sink(s) // $ hasValueFlow + * let t = "foo" + taint() + * sink(t); // $ hasTaintFlow + * } + * ``` + * + * If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import + * `ValueFlowTest`. Similarly, if you are only interested in taint flow, then instead of + * importing `DefaultFlowTest`, you can import `TaintFlowTest`. In both cases + * `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`. + * + * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. + */ + +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.ExternalFlow +import codeql.swift.dataflow.TaintTracking +import TestUtilities.InlineExpectationsTest + +private predicate defaultSource(DataFlow::Node source) { + source.asExpr().(CallExpr).getStaticTarget().(Function).getShortName() = ["source", "taint"] +} + +private predicate defaultSink(DataFlow::Node sink) { + exists(CallExpr ca | ca.getStaticTarget().(Function).getShortName() = "sink" | + sink.asExpr() = ca.getAnArgument().getExpr() + ) +} + +module DefaultFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { defaultSource(source) } + + predicate isSink(DataFlow::Node sink) { defaultSink(sink) } + + int fieldFlowBranchLimit() { result = 1000 } +} + +private module NoFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { none() } + + predicate isSink(DataFlow::Node sink) { none() } +} + +private string getSourceArgString(DataFlow::Node src) { + defaultSource(src) and + src.asExpr().(CallExpr).getAnArgument().getExpr().(StringLiteralExpr).getValue() = result +} + +module FlowTest { + module ValueFlow = DataFlow::Global; + + module TaintFlow = TaintTracking::Global; + + private module InlineTest implements TestSig { + string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | ValueFlow::flow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + } + } + + import MakeTest + import DataFlow::MergePathGraph + + predicate flowPath(PathNode source, PathNode sink) { + ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or + TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) + } +} + +module DefaultFlowTest = FlowTest; + +module ValueFlowTest { + import FlowTest +} + +module TaintFlowTest { + import FlowTest +} diff --git a/swift/ql/test/library-tests/dataflow/capture/closures.swift b/swift/ql/test/library-tests/dataflow/capture/closures.swift new file mode 100644 index 000000000000..d72a337973b7 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/capture/closures.swift @@ -0,0 +1,185 @@ +func sink(_ value: T) { print("sink:", value) } +func source(_ label: String, _ value: T) -> T { return value } +func taint(_ label: String, _ value: T) -> T { return value } + +func hello() -> String { + let value = "Hello world!" + return source("hello", value) +} + +func captureList() { + let y: Int = source("captureList", 123); + { [x = hello()] () in + sink(x) // $ MISSING: hasValueFlow=hello + sink(y) // $ MISSING: hasValueFlow=captureList + }() +} + +func setAndCallEscape() { + let x = source("setAndCallEscape", 0) + + let escape = { + sink(x) // $ MISSING: hasValueFlow=setAndCallEscape + return x + 1 + } + + sink(escape()) // $ MISSING: hasTaintFlow=setAndCallEscape +} + +var escape: (() -> Int)? = nil + +func setEscape() { + let x = source("setEscape", 0) + escape = { + sink(x) // $ MISSING: hasValueFlow=setEscape + return x + 1 + } +} + +func callEscape() { + setEscape() + sink(escape?()) // $ MISSING: hasTaintFlow=setEscape +} + +func logical() -> Bool { + let f: ((Int) -> Int)? = { x in + sink(x) // $ hasValueFlow=logical + return x + 1 + } + + let x: Int? = source("logical", 42) + return f != nil + && (x != nil + && f!(x!) == 43) +} + +func asyncTest() { + func withCallback(_ callback: @escaping (Int) async -> Int) { + @Sendable + func wrapper(_ x: Int) async -> Int { + return await callback(x + 1) // $ MISSING: hasValueFlow=asyncTest + } + Task { + print("asyncTest():", await wrapper(source("asyncTest", 40))) + } + } + withCallback { x in + x + 1 // $ MISSING: hasTaintFlow=asyncTest + } +} + +func foo() -> Int { + var x = 1 + let f = { y in x += y } + x = source("foo", 41) + let r = { x } + sink(r()) // $ MISSING: hasValueFlow=foo + f(1) + return r() // $ MISSING: hasTaintFlow=foo +} + +func bar() -> () -> Int { + var x = 1 + let f = { y in x += y } + x = source("bar", 41) + let r = { x } + f(1) + return r // constantly 42 +} + +var g: ((Int) -> Void)? = nil +func baz() -> () -> Int { + var x = 1 + g = { y in x += y } + x = source("baz", 41) + let r = { x } + g!(1) + return r +} + +func sharedCapture() -> Int { + let (incrX, getX) = { + var x = source("sharedCapture", 0) + return ({ x += 1 }, { x }) + }() + + let doubleIncrX = { + incrX() + incrX() + } + + sink(getX()) // $ MISSING: hasValueFlow=sharedCapture + doubleIncrX() + sink(getX()) // $ MISSING: hasTaintFlow=sharedCapture + doubleIncrX() + return getX() +} + +func sharedCaptureMultipleWriters() { + var x = 123 + + let callSink1 = { sink(x) } // $ MISSING: hasValueFlow=setter1 + let callSink2 = { sink(x) } // $ MISSING: hasValueFlow=setter2 + + let makeSetter = { y in + let setter = { x = y } + return setter + } + + let setter1 = makeSetter(source("setter1", 1)) + let setter2 = makeSetter(source("setter2", 2)) + + setter1() + callSink1() + + setter2() + callSink2() +} + +func taintCollections(array: inout Array) { + array[0] = source("array", 0) + sink(array) + sink(array[0]) // $ hasValueFlow=array + array.withContiguousStorageIfAvailable({ + buffer in + sink(array) + sink(array[0]) // $ MISSING: hasValueFlow=array + }) +} + +func simplestTest() { + let x = source("simplestTest", 0) + sink(x) // $ hasValueFlow=simplestTest +} + +func main() { + print("captureList():") + captureList() // Hello world! 123 + + print("callEscape():") + callEscape() // 1 + + print("logical():", logical()) // true + + print("asyncTest():") + asyncTest() // 42 + + print("foo():", foo()) // 42 + + let a = bar() + let b = baz() + + print("bar():", a(), a()) // $ MISSING: hasTaintFlow=bar + + print("baz():", b(), b()) // $ MISSING: hasTaintFlow=baz + + g!(1) + print("g!(1):", b(), b()) // $ MISSING: hasTaintFlow=baz + + print("sharedCapture():", sharedCapture()) // 4 + + print("sharedCaptureMultipleWriters():") + sharedCaptureMultipleWriters() // 42, -1 +} + +main() diff --git a/swift/ql/test/library-tests/dataflow/capture/inlinetest.expected b/swift/ql/test/library-tests/dataflow/capture/inlinetest.expected new file mode 100644 index 000000000000..48de9172b362 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/capture/inlinetest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/swift/ql/test/library-tests/dataflow/capture/inlinetest.ql b/swift/ql/test/library-tests/dataflow/capture/inlinetest.ql new file mode 100644 index 000000000000..50e3f8d2f7de --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/capture/inlinetest.ql @@ -0,0 +1,2 @@ +import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/swift/ql/test/library-tests/dataflow/capture/partialflowtest.expected b/swift/ql/test/library-tests/dataflow/capture/partialflowtest.expected new file mode 100644 index 000000000000..ec17df2b76d1 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/capture/partialflowtest.expected @@ -0,0 +1,256 @@ +| closures.swift:7:17:7:17 | hello | closures.swift:2:16:2:25 | label | +| closures.swift:7:17:7:17 | hello | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:7:17:7:17 | hello | closures.swift:2:33:2:42 | value | +| closures.swift:7:17:7:17 | hello | closures.swift:2:59:2:59 | value | +| closures.swift:7:17:7:17 | hello | closures.swift:7:10:7:31 | call to source(_:_:) | +| closures.swift:7:17:7:17 | hello | closures.swift:7:26:7:26 | value | +| closures.swift:7:17:7:17 | hello | closures.swift:12:6:12:6 | x | +| closures.swift:7:17:7:17 | hello | closures.swift:12:10:12:16 | call to hello() | +| closures.swift:11:23:11:23 | captureList | closures.swift:2:16:2:25 | label | +| closures.swift:11:23:11:23 | captureList | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:11:23:11:23 | captureList | closures.swift:2:33:2:42 | value | +| closures.swift:11:23:11:23 | captureList | closures.swift:2:59:2:59 | value | +| closures.swift:11:23:11:23 | captureList | closures.swift:11:7:11:7 | y | +| closures.swift:11:23:11:23 | captureList | closures.swift:11:7:11:10 | ... as ... | +| closures.swift:11:23:11:23 | captureList | closures.swift:11:16:11:41 | call to source(_:_:) | +| closures.swift:11:23:11:23 | captureList | closures.swift:11:38:11:38 | 123 | +| closures.swift:19:18:19:18 | setAndCallEscape | closures.swift:2:16:2:25 | label | +| closures.swift:19:18:19:18 | setAndCallEscape | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:19:18:19:18 | setAndCallEscape | closures.swift:2:33:2:42 | value | +| closures.swift:19:18:19:18 | setAndCallEscape | closures.swift:2:59:2:59 | value | +| closures.swift:19:18:19:18 | setAndCallEscape | closures.swift:19:7:19:7 | x | +| closures.swift:19:18:19:18 | setAndCallEscape | closures.swift:19:11:19:39 | call to source(_:_:) | +| closures.swift:19:18:19:18 | setAndCallEscape | closures.swift:19:38:19:38 | 0 | +| closures.swift:32:18:32:18 | setEscape | closures.swift:2:16:2:25 | label | +| closures.swift:32:18:32:18 | setEscape | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:32:18:32:18 | setEscape | closures.swift:2:33:2:42 | value | +| closures.swift:32:18:32:18 | setEscape | closures.swift:2:59:2:59 | value | +| closures.swift:32:18:32:18 | setEscape | closures.swift:32:7:32:7 | x | +| closures.swift:32:18:32:18 | setEscape | closures.swift:32:11:32:32 | call to source(_:_:) | +| closures.swift:32:18:32:18 | setEscape | closures.swift:32:31:32:31 | 0 | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | SSA def(value) | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | SSA def(value) [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | SSA def(value) [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | SSA def(value) [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | SSA def(value) [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | SSA def(value) [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | value | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | value [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | value [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | value [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | value [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:14:1:23 | value [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:34:1:43 | [...] [Array element, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:34:1:43 | [...] [Array element, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:34:1:43 | [...] [Array element, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:34:1:43 | [...] [Array element, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:34:1:43 | [...] [Array element] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:43:1:43 | value | +| closures.swift:50:24:50:24 | logical | closures.swift:1:43:1:43 | value [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:43:1:43 | value [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:43:1:43 | value [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:43:1:43 | value [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:1:43:1:43 | value [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:16:2:25 | label | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | SSA def(value) [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | SSA def(value) [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | SSA def(value) [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | SSA def(value) [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | SSA def(value) [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | value | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | value [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | value [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | value [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | value [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:33:2:42 | value [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:59:2:59 | value | +| closures.swift:50:24:50:24 | logical | closures.swift:2:59:2:59 | value [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:59:2:59 | value [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:59:2:59 | value [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:59:2:59 | value [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:2:59:2:59 | value [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | SSA def(x) | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | SSA def(x) [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | SSA def(x) [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | SSA def(x) [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | SSA def(x) [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | SSA def(x) [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | x | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | x [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | x [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | x [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | x [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:45:30:45:30 | x [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:46:10:46:10 | x | +| closures.swift:50:24:50:24 | logical | closures.swift:46:10:46:10 | x [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:46:10:46:10 | x [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:46:10:46:10 | x [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:46:10:46:10 | x [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:46:10:46:10 | x [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:47:12:47:12 | x | +| closures.swift:50:24:50:24 | logical | closures.swift:47:12:47:12 | x [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:47:12:47:12 | x [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:47:12:47:12 | x [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:47:12:47:12 | x [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:47:12:47:12 | x [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | SSA def(x) | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | SSA def(x) [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | SSA def(x) [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | SSA def(x) [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | SSA def(x) [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | SSA def(x) [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | x | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | x [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | x [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | x [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | x [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:7 | x [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:13 | ... as ... | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:13 | ... as ... [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:13 | ... as ... [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:13 | ... as ... [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:13 | ... as ... [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:7:50:13 | ... as ... [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:17:50:37 | call to source(_:_:) | +| closures.swift:50:24:50:24 | logical | closures.swift:50:17:50:37 | call to source(_:_:) [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:17:50:37 | call to source(_:_:) [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:17:50:37 | call to source(_:_:) [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:17:50:37 | call to source(_:_:) [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:17:50:37 | call to source(_:_:) [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:35:50:35 | 42 | +| closures.swift:50:24:50:24 | logical | closures.swift:50:35:50:35 | 42 [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:35:50:35 | 42 [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:35:50:35 | 42 [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:35:50:35 | 42 [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:50:35:50:35 | 42 [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:52:11:52:11 | x | +| closures.swift:50:24:50:24 | logical | closures.swift:52:11:52:11 | x [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:52:11:52:11 | x [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:52:11:52:11 | x [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:52:11:52:11 | x [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:52:11:52:11 | x [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:17 | x | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:17 | x [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:17 | x [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:17 | x [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:17 | x [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:17 | x [some:0] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:18 | ...! | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:18 | ...! [some:0, ... (2)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:18 | ...! [some:0, ... (3)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:18 | ...! [some:0, ... (4)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:18 | ...! [some:0, ... (5)] | +| closures.swift:50:24:50:24 | logical | closures.swift:53:17:53:18 | ...! [some:0] | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:2:16:2:25 | label | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:2:33:2:42 | value | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:2:59:2:59 | value | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:59:18:59:23 | SSA def(x) | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:59:18:59:23 | x | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:60:29:60:29 | x | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:63:43:63:65 | call to source(_:_:) | +| closures.swift:63:50:63:50 | asyncTest | closures.swift:63:63:63:63 | 40 | +| closures.swift:74:14:74:14 | foo | closures.swift:2:16:2:25 | label | +| closures.swift:74:14:74:14 | foo | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:74:14:74:14 | foo | closures.swift:2:33:2:42 | value | +| closures.swift:74:14:74:14 | foo | closures.swift:2:59:2:59 | value | +| closures.swift:74:14:74:14 | foo | closures.swift:74:7:74:23 | call to source(_:_:) | +| closures.swift:74:14:74:14 | foo | closures.swift:74:21:74:21 | 41 | +| closures.swift:84:14:84:14 | bar | closures.swift:2:16:2:25 | label | +| closures.swift:84:14:84:14 | bar | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:84:14:84:14 | bar | closures.swift:2:33:2:42 | value | +| closures.swift:84:14:84:14 | bar | closures.swift:2:59:2:59 | value | +| closures.swift:84:14:84:14 | bar | closures.swift:84:7:84:23 | call to source(_:_:) | +| closures.swift:84:14:84:14 | bar | closures.swift:84:21:84:21 | 41 | +| closures.swift:94:14:94:14 | baz | closures.swift:2:16:2:25 | label | +| closures.swift:94:14:94:14 | baz | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:94:14:94:14 | baz | closures.swift:2:33:2:42 | value | +| closures.swift:94:14:94:14 | baz | closures.swift:2:59:2:59 | value | +| closures.swift:94:14:94:14 | baz | closures.swift:94:7:94:23 | call to source(_:_:) | +| closures.swift:94:14:94:14 | baz | closures.swift:94:21:94:21 | 41 | +| closures.swift:102:20:102:20 | sharedCapture | closures.swift:2:16:2:25 | label | +| closures.swift:102:20:102:20 | sharedCapture | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:102:20:102:20 | sharedCapture | closures.swift:2:33:2:42 | value | +| closures.swift:102:20:102:20 | sharedCapture | closures.swift:2:59:2:59 | value | +| closures.swift:102:20:102:20 | sharedCapture | closures.swift:102:9:102:9 | x | +| closures.swift:102:20:102:20 | sharedCapture | closures.swift:102:13:102:38 | call to source(_:_:) | +| closures.swift:102:20:102:20 | sharedCapture | closures.swift:102:37:102:37 | 0 | +| closures.swift:129:35:129:35 | setter1 | closures.swift:2:16:2:25 | label | +| closures.swift:129:35:129:35 | setter1 | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:129:35:129:35 | setter1 | closures.swift:2:33:2:42 | value | +| closures.swift:129:35:129:35 | setter1 | closures.swift:2:59:2:59 | value | +| closures.swift:129:35:129:35 | setter1 | closures.swift:124:22:124:22 | y | +| closures.swift:129:35:129:35 | setter1 | closures.swift:125:9:125:9 | SSA def(setter) [y] | +| closures.swift:129:35:129:35 | setter1 | closures.swift:125:9:125:9 | setter [y] | +| closures.swift:129:35:129:35 | setter1 | closures.swift:125:18:125:26 | y | +| closures.swift:129:35:129:35 | setter1 | closures.swift:125:18:125:26 | { ... } [y] | +| closures.swift:129:35:129:35 | setter1 | closures.swift:126:12:126:12 | setter [y] | +| closures.swift:129:35:129:35 | setter1 | closures.swift:129:7:129:7 | SSA def(setter1) [y] | +| closures.swift:129:35:129:35 | setter1 | closures.swift:129:7:129:7 | setter1 [y] | +| closures.swift:129:35:129:35 | setter1 | closures.swift:129:17:129:48 | call to ... [y] | +| closures.swift:129:35:129:35 | setter1 | closures.swift:129:28:129:47 | call to source(_:_:) | +| closures.swift:129:35:129:35 | setter1 | closures.swift:129:46:129:46 | 1 | +| closures.swift:129:35:129:35 | setter1 | closures.swift:132:3:132:3 | setter1 [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:2:16:2:25 | label | +| closures.swift:130:35:130:35 | setter2 | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:130:35:130:35 | setter2 | closures.swift:2:33:2:42 | value | +| closures.swift:130:35:130:35 | setter2 | closures.swift:2:59:2:59 | value | +| closures.swift:130:35:130:35 | setter2 | closures.swift:124:22:124:22 | y | +| closures.swift:130:35:130:35 | setter2 | closures.swift:125:9:125:9 | SSA def(setter) [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:125:9:125:9 | setter [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:125:18:125:26 | y | +| closures.swift:130:35:130:35 | setter2 | closures.swift:125:18:125:26 | { ... } [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:126:12:126:12 | setter [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:130:7:130:7 | SSA def(setter2) [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:130:7:130:7 | setter2 [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:130:17:130:48 | call to ... [y] | +| closures.swift:130:35:130:35 | setter2 | closures.swift:130:28:130:47 | call to source(_:_:) | +| closures.swift:130:35:130:35 | setter2 | closures.swift:130:46:130:46 | 2 | +| closures.swift:130:35:130:35 | setter2 | closures.swift:135:3:135:3 | setter2 [y] | +| closures.swift:140:21:140:21 | array | closures.swift:1:14:1:23 | SSA def(value) | +| closures.swift:140:21:140:21 | array | closures.swift:1:14:1:23 | SSA def(value) [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:1:14:1:23 | value | +| closures.swift:140:21:140:21 | array | closures.swift:1:14:1:23 | value [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:1:34:1:43 | [...] [Array element, ... (2)] | +| closures.swift:140:21:140:21 | array | closures.swift:1:34:1:43 | [...] [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:1:43:1:43 | value | +| closures.swift:140:21:140:21 | array | closures.swift:1:43:1:43 | value [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:2:16:2:25 | label | +| closures.swift:140:21:140:21 | array | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:140:21:140:21 | array | closures.swift:2:33:2:42 | value | +| closures.swift:140:21:140:21 | array | closures.swift:2:59:2:59 | value | +| closures.swift:140:21:140:21 | array | closures.swift:139:1:148:1 | array[return] [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:140:3:140:3 | &... [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:140:3:140:3 | [post] array [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:140:3:140:3 | array [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:140:3:140:10 | ...[...] | +| closures.swift:140:21:140:21 | array | closures.swift:140:14:140:31 | call to source(_:_:) | +| closures.swift:140:21:140:21 | array | closures.swift:140:30:140:30 | 0 | +| closures.swift:140:21:140:21 | array | closures.swift:141:8:141:8 | array [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:142:8:142:8 | &... [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:142:8:142:8 | array [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:142:8:142:15 | ...[...] | +| closures.swift:140:21:140:21 | array | closures.swift:143:3:143:3 | array [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:143:42:147:3 | array [Array element] | +| closures.swift:140:21:140:21 | array | closures.swift:143:42:147:3 | { ... } [array, ... (2)] | +| closures.swift:140:21:140:21 | array | closures.swift:144:5:144:5 | buffer [Collection element] | +| closures.swift:140:21:140:21 | array | file://:0:0:0:0 | [summary param] 0 in withContiguousStorageIfAvailable(_:) [array, ... (2)] | +| closures.swift:140:21:140:21 | array | file://:0:0:0:0 | [summary param] this in withContiguousStorageIfAvailable(_:) [Array element] | +| closures.swift:140:21:140:21 | array | file://:0:0:0:0 | [summary] read: Argument[this].ArrayElement in withContiguousStorageIfAvailable(_:) | +| closures.swift:140:21:140:21 | array | file://:0:0:0:0 | [summary] to write: Argument[0].Parameter[0] in withContiguousStorageIfAvailable(_:) [Collection element] | +| closures.swift:140:21:140:21 | array | file://:0:0:0:0 | [summary] to write: Argument[0].Parameter[0].CollectionElement in withContiguousStorageIfAvailable(_:) | +| closures.swift:140:21:140:21 | array | file://:0:0:0:0 | [summary] to write: Argument[0].Parameter[this] in withContiguousStorageIfAvailable(_:) [array, ... (2)] | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:1:14:1:23 | SSA def(value) | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:1:14:1:23 | value | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:1:34:1:43 | [...] [Array element] | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:1:43:1:43 | value | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:2:16:2:25 | label | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:2:33:2:42 | SSA def(value) | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:2:33:2:42 | value | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:2:59:2:59 | value | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:151:7:151:7 | SSA def(x) | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:151:7:151:7 | x | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:151:11:151:35 | call to source(_:_:) | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:151:34:151:34 | 0 | +| closures.swift:151:18:151:18 | simplestTest | closures.swift:152:8:152:8 | x | diff --git a/swift/ql/test/library-tests/dataflow/capture/partialflowtest.ql b/swift/ql/test/library-tests/dataflow/capture/partialflowtest.ql new file mode 100644 index 000000000000..34593374bccb --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/capture/partialflowtest.ql @@ -0,0 +1,32 @@ +import swift +import codeql.swift.dataflow.DataFlow + +predicate source(StringLiteralExpr label, Expr value) { + exists(ApplyExpr ae | + ae.getStaticTarget().hasName(["source(_:_:)", "taint(_:_:)"]) and + ae.getFile().getBaseName() = "closures.swift" + | + ae.getArgument(0).getExpr() = label and + ae.getArgument(1).getExpr() = value + ) +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { source(n.asExpr(), _) } + + predicate isSink(DataFlow::Node n) { none() } + + predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { + source(n1.asExpr(), n2.asExpr()) + } +} + +module Flow = DataFlow::Global; + +int explorationLimit() { result = 100 } + +module PartialFlow = Flow::FlowExploration; + +from PartialFlow::PartialPathNode src, PartialFlow::PartialPathNode sink +where PartialFlow::partialFlow(src, sink, _) +select src, sink diff --git a/swift/ql/test/library-tests/dataflow/taint/libraries/int.swift b/swift/ql/test/library-tests/dataflow/taint/libraries/int.swift index c4cd65dade5f..f1f868c4df4e 100644 --- a/swift/ql/test/library-tests/dataflow/taint/libraries/int.swift +++ b/swift/ql/test/library-tests/dataflow/taint/libraries/int.swift @@ -147,7 +147,7 @@ func taintCollections(array: inout Array, contiguousArray: inout Contiguous sink(arg: buffer) sink(arg: buffer[0]) // $ tainted=142 sink(arg: array) - sink(arg: array[0]) // $ MISSING: tainted=142 + sink(arg: array[0]) // $ tainted=142 }) contiguousArray[0] = source2() diff --git a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected index 3a2d4eb80c6a..7d03fb6562e3 100644 --- a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected +++ b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected @@ -1,4 +1,5 @@ edges +| UncontrolledFormatString.swift:57:12:57:22 | format | UncontrolledFormatString.swift:59:16:59:16 | format | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:70:28:70:28 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:73:28:73:28 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:74:28:74:28 | tainted | @@ -11,12 +12,16 @@ edges | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:84:54:84:54 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:85:72:85:72 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:88:11:88:11 | tainted | +| UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:89:11:89:11 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:91:61:91:61 | tainted | | UncontrolledFormatString.swift:81:47:81:47 | tainted | UncontrolledFormatString.swift:81:30:81:54 | call to NSString.init(string:) | | UncontrolledFormatString.swift:82:65:82:65 | tainted | UncontrolledFormatString.swift:82:48:82:72 | call to NSString.init(string:) | | UncontrolledFormatString.swift:84:54:84:54 | tainted | UncontrolledFormatString.swift:84:37:84:61 | call to NSString.init(string:) | | UncontrolledFormatString.swift:85:72:85:72 | tainted | UncontrolledFormatString.swift:85:55:85:79 | call to NSString.init(string:) | +| UncontrolledFormatString.swift:89:11:89:11 | tainted | UncontrolledFormatString.swift:57:12:57:22 | format | nodes +| UncontrolledFormatString.swift:57:12:57:22 | format | semmle.label | format | +| UncontrolledFormatString.swift:59:16:59:16 | format | semmle.label | format | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | | UncontrolledFormatString.swift:70:28:70:28 | tainted | semmle.label | tainted | | UncontrolledFormatString.swift:73:28:73:28 | tainted | semmle.label | tainted | @@ -34,9 +39,11 @@ nodes | UncontrolledFormatString.swift:85:55:85:79 | call to NSString.init(string:) | semmle.label | call to NSString.init(string:) | | UncontrolledFormatString.swift:85:72:85:72 | tainted | semmle.label | tainted | | UncontrolledFormatString.swift:88:11:88:11 | tainted | semmle.label | tainted | +| UncontrolledFormatString.swift:89:11:89:11 | tainted | semmle.label | tainted | | UncontrolledFormatString.swift:91:61:91:61 | tainted | semmle.label | tainted | subpaths #select +| UncontrolledFormatString.swift:59:16:59:16 | format | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:59:16:59:16 | format | This format string depends on $@. | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | this user-provided value | | UncontrolledFormatString.swift:70:28:70:28 | tainted | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:70:28:70:28 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | this user-provided value | | UncontrolledFormatString.swift:73:28:73:28 | tainted | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:73:28:73:28 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | this user-provided value | | UncontrolledFormatString.swift:74:28:74:28 | tainted | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:74:28:74:28 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | this user-provided value | diff --git a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift index e5665eedeac7..05fc1cb25648 100644 --- a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift +++ b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.swift @@ -56,7 +56,7 @@ func getVaList(_ args: [CVarArg]) -> CVaListPointer { return (nil as CVaListPoin func MyLog(_ format: String, _ args: CVarArg...) { withVaList(args) { arglist in - NSLogv(format, arglist) // BAD [NOT DETECTED] + NSLogv(format, arglist) // BAD } }