@@ -10,6 +10,7 @@ private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
1010private import codeql.swift.frameworks.StandardLibrary.PointerTypes
1111private import codeql.swift.frameworks.StandardLibrary.Array
1212private import codeql.swift.frameworks.StandardLibrary.Dictionary
13+ private import codeql.dataflow.VariableCapture as VariableCapture
1314
1415/** Gets the callable in which this node occurs. */
1516DataFlowCallable nodeGetEnclosingCallable ( Node n ) { result = n .( NodeImpl ) .getEnclosingCallable ( ) }
@@ -98,6 +99,16 @@ private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl {
9899 }
99100}
100101
102+ private class CaptureNodeImpl extends CaptureNode , NodeImpl {
103+ override Location getLocationImpl ( ) { result = this .getSynthesizedCaptureNode ( ) .getLocation ( ) }
104+
105+ override string toStringImpl ( ) { result = this .getSynthesizedCaptureNode ( ) .toString ( ) }
106+
107+ override DataFlowCallable getEnclosingCallable ( ) {
108+ result .asSourceCallable ( ) = this .getSynthesizedCaptureNode ( ) .getEnclosingCallable ( )
109+ }
110+ }
111+
101112private predicate localFlowSsaInput ( Node nodeFrom , Ssa:: Definition def , Ssa:: Definition next ) {
102113 exists ( BasicBlock bb , int i | def .lastRefRedef ( bb , i , next ) |
103114 def .definesAt ( _, bb , i ) and
@@ -140,19 +151,22 @@ private module Cached {
140151 [
141152 any ( Argument arg | modifiable ( arg ) ) .getExpr ( ) , any ( MemberRefExpr ref ) .getBase ( ) ,
142153 any ( ApplyExpr apply ) .getQualifier ( ) , any ( TupleElementExpr te ) .getSubExpr ( ) ,
143- any ( SubscriptExpr se ) .getBase ( )
154+ any ( SubscriptExpr se ) .getBase ( ) ,
155+ any ( ApplyExpr apply | not exists ( apply .getStaticTarget ( ) ) ) .getFunction ( )
144156 ] )
145157 } or
146158 TDictionarySubscriptNode ( SubscriptExpr e ) {
147159 e .getBase ( ) .getType ( ) .getCanonicalType ( ) instanceof CanonicalDictionaryType
148- }
160+ } or
161+ TCaptureNode ( CaptureFlow:: SynthesizedCaptureNode cn ) or
162+ TClosureSelfParameterNode ( ClosureExpr closure )
149163
150164 private predicate localSsaFlowStepUseUse ( Ssa:: Definition def , Node nodeFrom , Node nodeTo ) {
151165 def .adjacentReadPair ( nodeFrom .getCfgNode ( ) , nodeTo .getCfgNode ( ) ) and
152166 (
153- nodeTo instanceof InoutReturnNode
167+ nodeTo instanceof InoutReturnNodeImpl
154168 implies
155- nodeTo .( InoutReturnNode ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
169+ nodeTo .( InoutReturnNodeImpl ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
156170 )
157171 }
158172
@@ -167,7 +181,7 @@ private module Cached {
167181 * Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
168182 */
169183 private predicate localFlowSsaParamInput ( Node nodeFrom , Node nodeTo ) {
170- nodeTo = getParameterDefNode ( nodeFrom .( ParameterNode ) . getParameter ( ) )
184+ nodeTo = getParameterDefNode ( nodeFrom .asParameter ( ) )
171185 }
172186
173187 private predicate localFlowStepCommon ( Node nodeFrom , Node nodeTo ) {
@@ -180,9 +194,9 @@ private module Cached {
180194 nodeFrom .asDefinition ( ) = def and
181195 nodeTo .getCfgNode ( ) = def .getAFirstRead ( ) and
182196 (
183- nodeTo instanceof InoutReturnNode
197+ nodeTo instanceof InoutReturnNodeImpl
184198 implies
185- nodeTo .( InoutReturnNode ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
199+ nodeTo .( InoutReturnNodeImpl ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
186200 )
187201 or
188202 // use-use flow
@@ -279,6 +293,9 @@ private module Cached {
279293 // flow through a flow summary (extension of `SummaryModelCsv`)
280294 FlowSummaryImpl:: Private:: Steps:: summaryLocalStep ( nodeFrom .( FlowSummaryNode ) .getSummaryNode ( ) ,
281295 nodeTo .( FlowSummaryNode ) .getSummaryNode ( ) , true )
296+ or
297+ // flow step according to the closure capture library
298+ captureValueStep ( nodeFrom , nodeTo )
282299 }
283300
284301 /**
@@ -305,7 +322,8 @@ private module Cached {
305322 TFieldContent ( FieldDecl f ) or
306323 TTupleContent ( int index ) { exists ( any ( TupleExpr te ) .getElement ( index ) ) } or
307324 TEnumContent ( ParamDecl f ) { exists ( EnumElementDecl d | d .getAParam ( ) = f ) } or
308- TCollectionContent ( )
325+ TCollectionContent ( ) or
326+ TCapturedVariableContent ( CapturedVariable v )
309327}
310328
311329/**
@@ -342,7 +360,9 @@ private predicate hasPatternNode(PatternCfgNode n, Pattern p) {
342360import Cached
343361
344362/** Holds if `n` should be hidden from path explanations. */
345- predicate nodeIsHidden ( Node n ) { n instanceof FlowSummaryNode }
363+ predicate nodeIsHidden ( Node n ) {
364+ n instanceof FlowSummaryNode or n instanceof ClosureSelfParameterNode
365+ }
346366
347367/**
348368 * The intermediate node for a dictionary subscript operation `dict[key]`. In a write, this is used
@@ -396,6 +416,25 @@ private module ParameterNodes {
396416 override ParamDecl getParameter ( ) { result = param }
397417 }
398418
419+ class ClosureSelfParameterNode extends ParameterNodeImpl , TClosureSelfParameterNode {
420+ ClosureExpr closure ;
421+
422+ ClosureSelfParameterNode ( ) { this = TClosureSelfParameterNode ( closure ) }
423+
424+ override predicate isParameterOf ( DataFlowCallable c , ParameterPosition pos ) {
425+ c .asSourceCallable ( ) = closure and
426+ pos instanceof TThisParameter
427+ }
428+
429+ override Location getLocationImpl ( ) { result = closure .getLocation ( ) }
430+
431+ override string toStringImpl ( ) { result = "closure self parameter" }
432+
433+ override DataFlowCallable getEnclosingCallable ( ) { this .isParameterOf ( result , _) }
434+
435+ ClosureExpr getClosure ( ) { result = closure }
436+ }
437+
399438 class SummaryParameterNode extends ParameterNodeImpl , FlowSummaryNode {
400439 SummaryParameterNode ( ) {
401440 FlowSummaryImpl:: Private:: summaryParameterNode ( this .getSummaryNode ( ) , _)
@@ -610,6 +649,18 @@ private module ArgumentNodes {
610649 pos = TPositionalArgument ( 0 )
611650 }
612651 }
652+
653+ class SelfClosureArgumentNode extends ExprNode , ArgumentNode {
654+ ApplyExprCfgNode apply ;
655+
656+ SelfClosureArgumentNode ( ) { n = apply .getFunction ( ) }
657+
658+ override predicate argumentOf ( DataFlowCall call , ArgumentPosition pos ) {
659+ apply = call .asCall ( ) and
660+ not exists ( apply .getStaticTarget ( ) ) and
661+ pos instanceof ThisArgumentPosition
662+ }
663+ }
613664}
614665
615666import ArgumentNodes
@@ -785,6 +836,175 @@ private module OutNodes {
785836
786837import OutNodes
787838
839+ /**
840+ * Holds if there is a data flow step from `e1` to `e2` that only steps from
841+ * child to parent in the AST.
842+ */
843+ private predicate simpleAstFlowStep ( Expr e1 , Expr e2 ) {
844+ e2 .( IfExpr ) .getBranch ( _) = e1
845+ or
846+ e2 .( AssignExpr ) .getSource ( ) = e1
847+ or
848+ e2 .( ArrayExpr ) .getAnElement ( ) = e1
849+ }
850+
851+ private predicate closureFlowStep ( CaptureInput:: Expr e1 , CaptureInput:: Expr e2 ) {
852+ simpleAstFlowStep ( e1 , e2 )
853+ or
854+ exists ( Ssa:: WriteDefinition def |
855+ def .getARead ( ) .getNode ( ) .asAstNode ( ) = e2 and
856+ def .assigns ( any ( CfgNode cfg | cfg .getNode ( ) .asAstNode ( ) = e1 ) )
857+ )
858+ or
859+ e2 .( Pattern ) .getImmediateMatchingExpr ( ) = e1
860+ }
861+
862+ private module CaptureInput implements VariableCapture:: InputSig {
863+ private import swift as S
864+ private import codeql.swift.controlflow.BasicBlocks as B
865+
866+ class Location = S:: Location ;
867+
868+ class BasicBlock instanceof B:: BasicBlock {
869+ string toString ( ) { result = super .toString ( ) }
870+
871+ Callable getEnclosingCallable ( ) { result = super .getScope ( ) }
872+
873+ Location getLocation ( ) { result = super .getLocation ( ) }
874+ }
875+
876+ BasicBlock getImmediateBasicBlockDominator ( BasicBlock bb ) {
877+ result .( B:: BasicBlock ) .immediatelyDominates ( bb )
878+ }
879+
880+ BasicBlock getABasicBlockSuccessor ( BasicBlock bb ) { result = bb .( B:: BasicBlock ) .getASuccessor ( ) }
881+
882+ class CapturedVariable instanceof S:: VarDecl {
883+ CapturedVariable ( ) {
884+ any ( S:: CapturedDecl capturedDecl ) .getDecl ( ) = this and
885+ exists ( this .getEnclosingCallable ( ) )
886+ }
887+
888+ string toString ( ) { result = super .toString ( ) }
889+
890+ Callable getCallable ( ) { result = super .getEnclosingCallable ( ) }
891+
892+ Location getLocation ( ) { result = super .getLocation ( ) }
893+ }
894+
895+ class CapturedParameter extends CapturedVariable instanceof S:: ParamDecl { }
896+
897+ class Expr instanceof S:: AstNode {
898+ string toString ( ) { result = super .toString ( ) }
899+
900+ Location getLocation ( ) { result = super .getLocation ( ) }
901+
902+ predicate hasCfgNode ( BasicBlock bb , int i ) {
903+ this = bb .( B:: BasicBlock ) .getNode ( i ) .getNode ( ) .asAstNode ( )
904+ }
905+ }
906+
907+ class VariableWrite extends Expr {
908+ CapturedVariable variable ;
909+ Expr source ;
910+
911+ VariableWrite ( ) {
912+ exists ( S:: Assignment a | this = a |
913+ a .getDest ( ) .( DeclRefExpr ) .getDecl ( ) = variable and
914+ source = a .getSource ( )
915+ )
916+ or
917+ exists ( S:: NamedPattern np | this = np |
918+ variable = np .getVarDecl ( ) and
919+ source = np .getMatchingExpr ( )
920+ )
921+ }
922+
923+ CapturedVariable getVariable ( ) { result = variable }
924+
925+ Expr getSource ( ) { result = source }
926+ }
927+
928+ class VariableRead extends Expr instanceof S:: DeclRefExpr {
929+ CapturedVariable v ;
930+
931+ VariableRead ( ) { this .getDecl ( ) = v and not isLValue ( this ) }
932+
933+ CapturedVariable getVariable ( ) { result = v }
934+ }
935+
936+ class ClosureExpr extends Expr instanceof S:: Callable {
937+ ClosureExpr ( ) { any ( S:: CapturedDecl c ) .getScope ( ) = this }
938+
939+ predicate hasBody ( Callable body ) { this = body }
940+
941+ predicate hasAliasedAccess ( Expr f ) { closureFlowStep + ( this , f ) and not closureFlowStep ( f , _) }
942+ }
943+
944+ class Callable extends S:: Callable {
945+ predicate isConstructor ( ) {
946+ // A class declaration cannot capture a variable in Swift. Consider this hypothetical example:
947+ // ```
948+ // protocol Interface { }
949+ // func foo() -> Interface {
950+ // let y = 42
951+ // class Impl : Interface {
952+ // let x : Int
953+ // init() {
954+ // x = y
955+ // }
956+ // }
957+ // let object = Impl()
958+ // return object
959+ // }
960+ // ```
961+ // The Swift compiler will reject this with an error message such as
962+ // ```
963+ // error: class declaration cannot close over value 'y' defined in outer scope
964+ // x = y
965+ // ^
966+ // ```
967+ none ( )
968+ }
969+ }
970+ }
971+
972+ class CapturedVariable = CaptureInput:: CapturedVariable ;
973+
974+ class CapturedParameter = CaptureInput:: CapturedParameter ;
975+
976+ module CaptureFlow = VariableCapture:: Flow< CaptureInput > ;
977+
978+ private CaptureFlow:: ClosureNode asClosureNode ( Node n ) {
979+ result = n .( CaptureNode ) .getSynthesizedCaptureNode ( )
980+ or
981+ result .( CaptureFlow:: ExprNode ) .getExpr ( ) = n .asExpr ( )
982+ or
983+ result .( CaptureFlow:: ExprPostUpdateNode ) .getExpr ( ) =
984+ n .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( )
985+ or
986+ result .( CaptureFlow:: ParameterNode ) .getParameter ( ) = n .asParameter ( )
987+ or
988+ result .( CaptureFlow:: ThisParameterNode ) .getCallable ( ) = n .( ClosureSelfParameterNode ) .getClosure ( )
989+ or
990+ exists ( CaptureInput:: VariableWrite write |
991+ result .( CaptureFlow:: VariableWriteSourceNode ) .getVariableWrite ( ) = write and
992+ n .asExpr ( ) = write .getSource ( )
993+ )
994+ }
995+
996+ private predicate captureStoreStep ( Node node1 , Content:: CapturedVariableContent c , Node node2 ) {
997+ CaptureFlow:: storeStep ( asClosureNode ( node1 ) , c .getVariable ( ) , asClosureNode ( node2 ) )
998+ }
999+
1000+ private predicate captureReadStep ( Node node1 , Content:: CapturedVariableContent c , Node node2 ) {
1001+ CaptureFlow:: readStep ( asClosureNode ( node1 ) , c .getVariable ( ) , asClosureNode ( node2 ) )
1002+ }
1003+
1004+ predicate captureValueStep ( Node node1 , Node node2 ) {
1005+ CaptureFlow:: localFlowStep ( asClosureNode ( node1 ) , asClosureNode ( node2 ) )
1006+ }
1007+
7881008predicate jumpStep ( Node pred , Node succ ) {
7891009 FlowSummaryImpl:: Private:: Steps:: summaryJumpStep ( pred .( FlowSummaryNode ) .getSummaryNode ( ) ,
7901010 succ .( FlowSummaryNode ) .getSummaryNode ( ) )
@@ -897,6 +1117,8 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
8971117 or
8981118 FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
8991119 node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
1120+ or
1121+ captureStoreStep ( node1 , any ( Content:: CapturedVariableContent cvc | c .isSingleton ( cvc ) ) , node2 )
9001122}
9011123
9021124predicate isLValue ( Expr e ) { any ( AssignExpr assign ) .getDest ( ) = e }
@@ -1001,6 +1223,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
10011223 or
10021224 FlowSummaryImpl:: Private:: Steps:: summaryReadStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
10031225 node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
1226+ or
1227+ captureReadStep ( node1 , any ( Content:: CapturedVariableContent cvc | c .isSingleton ( cvc ) ) , node2 )
10041228}
10051229
10061230/**
@@ -1094,6 +1318,17 @@ private module PostUpdateNodes {
10941318 result .( FlowSummaryNode ) .getSummaryNode ( ) )
10951319 }
10961320 }
1321+
1322+ class CapturePostUpdateNode extends PostUpdateNodeImpl , CaptureNode {
1323+ private CaptureNode pre ;
1324+
1325+ CapturePostUpdateNode ( ) {
1326+ CaptureFlow:: capturePostUpdateNode ( this .getSynthesizedCaptureNode ( ) ,
1327+ pre .getSynthesizedCaptureNode ( ) )
1328+ }
1329+
1330+ override Node getPreUpdateNode ( ) { result = pre }
1331+ }
10971332}
10981333
10991334private import PostUpdateNodes
@@ -1152,7 +1387,12 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
11521387 * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
11531388 * by default as a heuristic.
11541389 */
1155- predicate allowParameterReturnInSelf ( ParameterNode p ) { none ( ) }
1390+ predicate allowParameterReturnInSelf ( ParameterNode p ) {
1391+ exists ( Callable c |
1392+ c = p .( ParameterNodeImpl ) .getEnclosingCallable ( ) .asSourceCallable ( ) and
1393+ CaptureFlow:: heuristicAllowInstanceParameterReturnInSelf ( c )
1394+ )
1395+ }
11561396
11571397/** An approximated `Content`. */
11581398class ContentApprox = Unit ;
0 commit comments