@@ -8,10 +8,17 @@ private import FlowSummaryImpl as FlowSummaryImpl
8
8
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
9
9
private import codeql.ruby.dataflow.FlowSummary
10
10
private import codeql.ruby.dataflow.SSA
11
+ private import codeql.ruby.dataflow.internal.SsaImpl as SsaImpl
11
12
12
13
newtype TReturnKind =
13
14
TNormalReturnKind ( ) or
14
- TBreakReturnKind ( )
15
+ TBreakReturnKind ( ) or
16
+ TCapturedReturnKind ( LocalVariable v ) {
17
+ exists ( Ssa:: Definition def |
18
+ SsaImpl:: captureFlowOut ( _, def , _) and
19
+ v = def .getSourceVariable ( )
20
+ )
21
+ }
15
22
16
23
/**
17
24
* Gets a node that can read the value returned from `call` with return kind
@@ -43,6 +50,19 @@ class BreakReturnKind extends ReturnKind, TBreakReturnKind {
43
50
override string toString ( ) { result = "break" }
44
51
}
45
52
53
+ /**
54
+ * A value implicitly returned by updating a captured variable.
55
+ */
56
+ class CapturedReturnKind extends ReturnKind , TCapturedReturnKind {
57
+ LocalVariable v ;
58
+
59
+ CapturedReturnKind ( ) { this = TCapturedReturnKind ( v ) }
60
+
61
+ LocalVariable getVariable ( ) { result = v }
62
+
63
+ override string toString ( ) { result = "captured write to " + v }
64
+ }
65
+
46
66
/** A callable defined in library code, identified by a unique string. */
47
67
abstract class LibraryCallable extends string {
48
68
bindingset [ this ]
@@ -151,6 +171,33 @@ private class NormalCall extends DataFlowCall, TNormalCall {
151
171
override Location getLocation ( ) { result = c .getLocation ( ) }
152
172
}
153
173
174
+ /**
175
+ * A call that may ultimately reach a callable, which reads or updates
176
+ * (contents of) a captured variable.
177
+ */
178
+ class CapturedCall extends DataFlowCall , TCapturedCall {
179
+ private CfgNodes:: ExprNodes:: CallCfgNode c ;
180
+
181
+ CapturedCall ( ) { this = TCapturedCall ( c ) }
182
+
183
+ /** Gets a target, in which a captured variable is referenced. */
184
+ Callable getATarget ( ) {
185
+ exists ( Ssa:: Definition def | result = def .getScope ( ) |
186
+ SsaImpl:: captureFlowIn ( c , _, _, _, def )
187
+ or
188
+ SsaImpl:: captureFlowOut ( c , def , _)
189
+ )
190
+ }
191
+
192
+ override CfgNodes:: ExprNodes:: CallCfgNode asCall ( ) { none ( ) }
193
+
194
+ override DataFlowCallable getEnclosingCallable ( ) { result = TCfgScope ( c .getScope ( ) ) }
195
+
196
+ override string toString ( ) { result = "<captured> " + c .toString ( ) }
197
+
198
+ override Location getLocation ( ) { result = c .getLocation ( ) }
199
+ }
200
+
154
201
/** A call for which we want to compute call targets. */
155
202
private class RelevantCall extends CfgNodes:: ExprNodes:: CallCfgNode {
156
203
pragma [ nomagic]
@@ -341,6 +388,11 @@ private module Cached {
341
388
TNormalCall ( CfgNodes:: ExprNodes:: CallCfgNode c ) or
342
389
TSummaryCall ( FlowSummaryImpl:: Public:: SummarizedCallable c , DataFlow:: Node receiver ) {
343
390
FlowSummaryImpl:: Private:: summaryCallbackRange ( c , receiver )
391
+ } or
392
+ TCapturedCall ( CfgNodes:: ExprNodes:: CallCfgNode c ) {
393
+ SsaImpl:: captureFlowIn ( c , _, _, _, _)
394
+ or
395
+ SsaImpl:: captureFlowOut ( c , _, _)
344
396
}
345
397
346
398
pragma [ nomagic]
@@ -478,6 +530,8 @@ private module Cached {
478
530
result = viableSourceCallable ( call )
479
531
or
480
532
result = viableLibraryCallable ( call )
533
+ or
534
+ result .asCallable ( ) = call .( CapturedCall ) .getATarget ( )
481
535
}
482
536
483
537
cached
@@ -499,7 +553,13 @@ private module Cached {
499
553
THashSplatArgumentPosition ( ) or
500
554
TSplatAllArgumentPosition ( ) or
501
555
TAnyArgumentPosition ( ) or
502
- TAnyKeywordArgumentPosition ( )
556
+ TAnyKeywordArgumentPosition ( ) or
557
+ TCapturedArgumentPosition ( LocalVariable v ) {
558
+ exists ( Ssa:: Definition def |
559
+ SsaImpl:: captureFlowIn ( _, _, _, _, def ) and
560
+ v = def .getSourceVariable ( )
561
+ )
562
+ }
503
563
504
564
cached
505
565
newtype TParameterPosition =
@@ -521,7 +581,13 @@ private module Cached {
521
581
THashSplatParameterPosition ( ) or
522
582
TSplatAllParameterPosition ( ) or
523
583
TAnyParameterPosition ( ) or
524
- TAnyKeywordParameterPosition ( )
584
+ TAnyKeywordParameterPosition ( ) or
585
+ TCapturedParameterPosition ( LocalVariable v ) {
586
+ exists ( Ssa:: Definition def |
587
+ SsaImpl:: captureFlowIn ( _, _, _, _, def ) and
588
+ v = def .getSourceVariable ( )
589
+ )
590
+ }
525
591
}
526
592
527
593
import Cached
@@ -921,7 +987,7 @@ private predicate paramReturnFlow(
921
987
DataFlow:: Node nodeFrom , DataFlow:: PostUpdateNode nodeTo , StepSummary summary
922
988
) {
923
989
exists ( RelevantCall call , DataFlow:: Node arg , DataFlow:: ParameterNode p , Expr nodeFromPreExpr |
924
- TypeTrackerSpecific:: callStep ( call , arg , p ) and
990
+ TypeTrackerSpecific:: callStep ( TNormalCall ( call ) , arg , p ) and
925
991
nodeTo .getPreUpdateNode ( ) = arg and
926
992
summary .toString ( ) = "return" and
927
993
(
@@ -1151,6 +1217,9 @@ class ParameterPosition extends TParameterPosition {
1151
1217
/** Holds if this position represents any positional parameter. */
1152
1218
predicate isAnyNamed ( ) { this = TAnyKeywordParameterPosition ( ) }
1153
1219
1220
+ /** Holds if this position represents a captured variable `v`. */
1221
+ predicate isCapturedVariable ( LocalVariable v ) { this = TCapturedParameterPosition ( v ) }
1222
+
1154
1223
/** Gets a textual representation of this position. */
1155
1224
string toString ( ) {
1156
1225
this .isSelf ( ) and result = "self"
@@ -1170,6 +1239,8 @@ class ParameterPosition extends TParameterPosition {
1170
1239
this .isAny ( ) and result = "any"
1171
1240
or
1172
1241
this .isAnyNamed ( ) and result = "any-named"
1242
+ or
1243
+ exists ( LocalVariable v | this .isCapturedVariable ( v ) and result = "captured " + v )
1173
1244
}
1174
1245
}
1175
1246
@@ -1196,6 +1267,9 @@ class ArgumentPosition extends TArgumentPosition {
1196
1267
/** Holds if this position represents any positional parameter. */
1197
1268
predicate isAnyNamed ( ) { this = TAnyKeywordArgumentPosition ( ) }
1198
1269
1270
+ /** Holds if this position represents a captured variable `v`. */
1271
+ predicate isCapturedVariable ( LocalVariable v ) { this = TCapturedArgumentPosition ( v ) }
1272
+
1199
1273
/**
1200
1274
* Holds if this position represents a synthesized argument containing all keyword
1201
1275
* arguments wrapped in a hash.
@@ -1221,6 +1295,8 @@ class ArgumentPosition extends TArgumentPosition {
1221
1295
this .isHashSplat ( ) and result = "**"
1222
1296
or
1223
1297
this .isSplatAll ( ) and result = "*"
1298
+ or
1299
+ exists ( LocalVariable v | this .isCapturedVariable ( v ) and result = "captured " + v )
1224
1300
}
1225
1301
}
1226
1302
@@ -1256,4 +1332,6 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
1256
1332
ppos .isAnyNamed ( ) and apos .isKeyword ( _)
1257
1333
or
1258
1334
apos .isAnyNamed ( ) and ppos .isKeyword ( _)
1335
+ or
1336
+ exists ( LocalVariable v | apos .isCapturedVariable ( v ) and ppos .isCapturedVariable ( v ) )
1259
1337
}
0 commit comments