@@ -10,6 +10,7 @@ private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
10
10
private import codeql.swift.frameworks.StandardLibrary.PointerTypes
11
11
private import codeql.swift.frameworks.StandardLibrary.Array
12
12
private import codeql.swift.frameworks.StandardLibrary.Dictionary
13
+ private import codeql.dataflow.VariableCapture as VariableCapture
13
14
14
15
/** Gets the callable in which this node occurs. */
15
16
DataFlowCallable nodeGetEnclosingCallable ( Node n ) { result = n .( NodeImpl ) .getEnclosingCallable ( ) }
@@ -98,6 +99,16 @@ private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl {
98
99
}
99
100
}
100
101
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
+
101
112
private predicate localFlowSsaInput ( Node nodeFrom , Ssa:: Definition def , Ssa:: Definition next ) {
102
113
exists ( BasicBlock bb , int i | def .lastRefRedef ( bb , i , next ) |
103
114
def .definesAt ( _, bb , i ) and
@@ -140,19 +151,22 @@ private module Cached {
140
151
[
141
152
any ( Argument arg | modifiable ( arg ) ) .getExpr ( ) , any ( MemberRefExpr ref ) .getBase ( ) ,
142
153
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 ( )
144
156
] )
145
157
} or
146
158
TDictionarySubscriptNode ( SubscriptExpr e ) {
147
159
e .getBase ( ) .getType ( ) .getCanonicalType ( ) instanceof CanonicalDictionaryType
148
- }
160
+ } or
161
+ TCaptureNode ( CaptureFlow:: SynthesizedCaptureNode cn ) or
162
+ TClosureSelfParameterNode ( ClosureExpr closure )
149
163
150
164
private predicate localSsaFlowStepUseUse ( Ssa:: Definition def , Node nodeFrom , Node nodeTo ) {
151
165
def .adjacentReadPair ( nodeFrom .getCfgNode ( ) , nodeTo .getCfgNode ( ) ) and
152
166
(
153
- nodeTo instanceof InoutReturnNode
167
+ nodeTo instanceof InoutReturnNodeImpl
154
168
implies
155
- nodeTo .( InoutReturnNode ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
169
+ nodeTo .( InoutReturnNodeImpl ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
156
170
)
157
171
}
158
172
@@ -167,7 +181,7 @@ private module Cached {
167
181
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
168
182
*/
169
183
private predicate localFlowSsaParamInput ( Node nodeFrom , Node nodeTo ) {
170
- nodeTo = getParameterDefNode ( nodeFrom .( ParameterNode ) . getParameter ( ) )
184
+ nodeTo = getParameterDefNode ( nodeFrom .asParameter ( ) )
171
185
}
172
186
173
187
private predicate localFlowStepCommon ( Node nodeFrom , Node nodeTo ) {
@@ -180,9 +194,9 @@ private module Cached {
180
194
nodeFrom .asDefinition ( ) = def and
181
195
nodeTo .getCfgNode ( ) = def .getAFirstRead ( ) and
182
196
(
183
- nodeTo instanceof InoutReturnNode
197
+ nodeTo instanceof InoutReturnNodeImpl
184
198
implies
185
- nodeTo .( InoutReturnNode ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
199
+ nodeTo .( InoutReturnNodeImpl ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
186
200
)
187
201
or
188
202
// use-use flow
@@ -279,6 +293,9 @@ private module Cached {
279
293
// flow through a flow summary (extension of `SummaryModelCsv`)
280
294
FlowSummaryImpl:: Private:: Steps:: summaryLocalStep ( nodeFrom .( FlowSummaryNode ) .getSummaryNode ( ) ,
281
295
nodeTo .( FlowSummaryNode ) .getSummaryNode ( ) , true )
296
+ or
297
+ // flow step according to the closure capture library
298
+ captureValueStep ( nodeFrom , nodeTo )
282
299
}
283
300
284
301
/**
@@ -305,7 +322,8 @@ private module Cached {
305
322
TFieldContent ( FieldDecl f ) or
306
323
TTupleContent ( int index ) { exists ( any ( TupleExpr te ) .getElement ( index ) ) } or
307
324
TEnumContent ( ParamDecl f ) { exists ( EnumElementDecl d | d .getAParam ( ) = f ) } or
308
- TCollectionContent ( )
325
+ TCollectionContent ( ) or
326
+ TCapturedVariableContent ( CapturedVariable v )
309
327
}
310
328
311
329
/**
@@ -342,7 +360,9 @@ private predicate hasPatternNode(PatternCfgNode n, Pattern p) {
342
360
import Cached
343
361
344
362
/** 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
+ }
346
366
347
367
/**
348
368
* The intermediate node for a dictionary subscript operation `dict[key]`. In a write, this is used
@@ -396,6 +416,25 @@ private module ParameterNodes {
396
416
override ParamDecl getParameter ( ) { result = param }
397
417
}
398
418
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
+
399
438
class SummaryParameterNode extends ParameterNodeImpl , FlowSummaryNode {
400
439
SummaryParameterNode ( ) {
401
440
FlowSummaryImpl:: Private:: summaryParameterNode ( this .getSummaryNode ( ) , _)
@@ -610,6 +649,18 @@ private module ArgumentNodes {
610
649
pos = TPositionalArgument ( 0 )
611
650
}
612
651
}
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
+ }
613
664
}
614
665
615
666
import ArgumentNodes
@@ -785,6 +836,175 @@ private module OutNodes {
785
836
786
837
import OutNodes
787
838
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
+
788
1008
predicate jumpStep ( Node pred , Node succ ) {
789
1009
FlowSummaryImpl:: Private:: Steps:: summaryJumpStep ( pred .( FlowSummaryNode ) .getSummaryNode ( ) ,
790
1010
succ .( FlowSummaryNode ) .getSummaryNode ( ) )
@@ -897,6 +1117,8 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
897
1117
or
898
1118
FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
899
1119
node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
1120
+ or
1121
+ captureStoreStep ( node1 , any ( Content:: CapturedVariableContent cvc | c .isSingleton ( cvc ) ) , node2 )
900
1122
}
901
1123
902
1124
predicate isLValue ( Expr e ) { any ( AssignExpr assign ) .getDest ( ) = e }
@@ -1001,6 +1223,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
1001
1223
or
1002
1224
FlowSummaryImpl:: Private:: Steps:: summaryReadStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
1003
1225
node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
1226
+ or
1227
+ captureReadStep ( node1 , any ( Content:: CapturedVariableContent cvc | c .isSingleton ( cvc ) ) , node2 )
1004
1228
}
1005
1229
1006
1230
/**
@@ -1094,6 +1318,17 @@ private module PostUpdateNodes {
1094
1318
result .( FlowSummaryNode ) .getSummaryNode ( ) )
1095
1319
}
1096
1320
}
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
+ }
1097
1332
}
1098
1333
1099
1334
private import PostUpdateNodes
@@ -1152,7 +1387,12 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
1152
1387
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
1153
1388
* by default as a heuristic.
1154
1389
*/
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
+ }
1156
1396
1157
1397
/** An approximated `Content`. */
1158
1398
class ContentApprox = Unit ;
0 commit comments