@@ -2,8 +2,9 @@ private import ruby
2
2
private import codeql.ruby.CFG
3
3
private import DataFlowPrivate
4
4
private import codeql.ruby.typetracking.TypeTracker
5
- private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlow
6
5
private import codeql.ruby.ast.internal.Module
6
+ private import FlowSummaryImpl as FlowSummaryImpl
7
+ private import codeql.ruby.dataflow.FlowSummary
7
8
8
9
newtype TReturnKind =
9
10
TNormalReturnKind ( ) or
@@ -39,83 +40,183 @@ class BreakReturnKind extends ReturnKind, TBreakReturnKind {
39
40
override string toString ( ) { result = "break" }
40
41
}
41
42
42
- class DataFlowCallable = CfgScope ;
43
+ /** A callable defined in library code, identified by a unique string. */
44
+ abstract class LibraryCallable extends string {
45
+ bindingset [ this ]
46
+ LibraryCallable ( ) { any ( ) }
43
47
44
- class DataFlowCall extends CfgNodes:: ExprNodes:: CallCfgNode {
45
- DataFlowCallable getEnclosingCallable ( ) { result = this .getScope ( ) }
48
+ /** Gets a call to this library callable. */
49
+ abstract Call getACall ( ) ;
50
+ }
46
51
47
- pragma [ nomagic]
48
- private predicate methodCall ( DataFlow:: LocalSourceNode sourceNode , string method ) {
49
- exists ( DataFlow:: Node nodeTo |
50
- method = this .getExpr ( ) .( MethodCall ) .getMethodName ( ) and
51
- nodeTo .asExpr ( ) = this .getReceiver ( ) and
52
- sourceNode .flowsTo ( nodeTo )
53
- )
54
- }
52
+ /**
53
+ * A callable. This includes callables from source code, as well as callables
54
+ * defined in library code.
55
+ */
56
+ class DataFlowCallable extends TDataFlowCallable {
57
+ /** Gets the underlying source code callable, if any. */
58
+ Callable asCallable ( ) { this = TCfgScope ( result ) }
55
59
56
- private Block yieldCall ( ) {
57
- this .getExpr ( ) instanceof YieldCall and
58
- exists ( BlockParameterNode node |
59
- node = trackBlock ( result ) and
60
- node .getMethod ( ) = this .getExpr ( ) .getEnclosingMethod ( )
61
- )
62
- }
60
+ /** Get the underlying library callable, if any. */
61
+ LibraryCallable asLibraryCallable ( ) { this = TLibraryCallable ( result ) }
63
62
64
- pragma [ nomagic]
65
- private predicate superCall ( Module superClass , string method ) {
66
- this .getExpr ( ) instanceof SuperCall and
67
- exists ( Module tp |
68
- tp = this .getExpr ( ) .getEnclosingModule ( ) .getModule ( ) and
69
- superClass = tp .getSuperClass ( ) and
70
- method = this .getExpr ( ) .getEnclosingMethod ( ) .getName ( )
71
- )
72
- }
63
+ /** Gets a textual representation of this callable. */
64
+ string toString ( ) { result = [ this .asCallable ( ) .toString ( ) , this .asLibraryCallable ( ) ] }
73
65
74
- pragma [ nomagic]
75
- private predicate instanceMethodCall ( Module tp , string method ) {
76
- exists ( DataFlow:: LocalSourceNode sourceNode |
77
- this .methodCall ( sourceNode , method ) and
78
- sourceNode = trackInstance ( tp )
79
- )
80
- }
66
+ /** Gets the location of this callable. */
67
+ Location getLocation ( ) { result = this .asCallable ( ) .getLocation ( ) }
68
+ }
69
+
70
+ /**
71
+ * A call. This includes calls from source code, as well as call(back)s
72
+ * inside library callables with a flow summary.
73
+ */
74
+ class DataFlowCall extends TDataFlowCall {
75
+ /** Gets the enclosing callable. */
76
+ DataFlowCallable getEnclosingCallable ( ) { none ( ) }
77
+
78
+ /** Gets the underlying source code call, if any. */
79
+ CfgNodes:: ExprNodes:: CallCfgNode asCall ( ) { none ( ) }
80
+
81
+ /** Gets a textual representation of this call. */
82
+ string toString ( ) { none ( ) }
83
+
84
+ /** Gets the location of this call. */
85
+ Location getLocation ( ) { none ( ) }
86
+ }
87
+
88
+ /**
89
+ * A synthesized call inside a callable with a flow summary.
90
+ *
91
+ * For example, in
92
+ * ```rb
93
+ * ints.each do |i|
94
+ * puts i
95
+ * end
96
+ * ```
97
+ *
98
+ * there is a call to the block argument inside `each`.
99
+ */
100
+ class SummaryCall extends DataFlowCall , TSummaryCall {
101
+ private FlowSummaryImpl:: Public:: SummarizedCallable c ;
102
+ private DataFlow:: Node receiver ;
103
+
104
+ SummaryCall ( ) { this = TSummaryCall ( c , receiver ) }
105
+
106
+ /** Gets the data flow node that this call targets. */
107
+ DataFlow:: Node getReceiver ( ) { result = receiver }
108
+
109
+ override DataFlowCallable getEnclosingCallable ( ) { result = c }
110
+
111
+ override string toString ( ) { result = "[summary] call to " + receiver + " in " + c }
112
+
113
+ override Location getLocation ( ) { result = c .getLocation ( ) }
114
+ }
115
+
116
+ private class NormalCall extends DataFlowCall , TNormalCall {
117
+ private CfgNodes:: ExprNodes:: CallCfgNode c ;
81
118
119
+ NormalCall ( ) { this = TNormalCall ( c ) }
120
+
121
+ override CfgNodes:: ExprNodes:: CallCfgNode asCall ( ) { result = c }
122
+
123
+ override DataFlowCallable getEnclosingCallable ( ) { result = TCfgScope ( c .getScope ( ) ) }
124
+
125
+ override string toString ( ) { result = c .toString ( ) }
126
+
127
+ override Location getLocation ( ) { result = c .getLocation ( ) }
128
+ }
129
+
130
+ pragma [ nomagic]
131
+ private predicate methodCall (
132
+ CfgNodes:: ExprNodes:: CallCfgNode call , DataFlow:: LocalSourceNode sourceNode , string method
133
+ ) {
134
+ exists ( DataFlow:: Node nodeTo |
135
+ method = call .getExpr ( ) .( MethodCall ) .getMethodName ( ) and
136
+ nodeTo .asExpr ( ) = call .getReceiver ( ) and
137
+ sourceNode .flowsTo ( nodeTo )
138
+ )
139
+ }
140
+
141
+ private Block yieldCall ( CfgNodes:: ExprNodes:: CallCfgNode call ) {
142
+ call .getExpr ( ) instanceof YieldCall and
143
+ exists ( BlockParameterNode node |
144
+ node = trackBlock ( result ) and
145
+ node .getMethod ( ) = call .getExpr ( ) .getEnclosingMethod ( )
146
+ )
147
+ }
148
+
149
+ pragma [ nomagic]
150
+ private predicate superCall ( CfgNodes:: ExprNodes:: CallCfgNode call , Module superClass , string method ) {
151
+ call .getExpr ( ) instanceof SuperCall and
152
+ exists ( Module tp |
153
+ tp = call .getExpr ( ) .getEnclosingModule ( ) .getModule ( ) and
154
+ superClass = tp .getSuperClass ( ) and
155
+ method = call .getExpr ( ) .getEnclosingMethod ( ) .getName ( )
156
+ )
157
+ }
158
+
159
+ pragma [ nomagic]
160
+ private predicate instanceMethodCall ( CfgNodes:: ExprNodes:: CallCfgNode call , Module tp , string method ) {
161
+ exists ( DataFlow:: LocalSourceNode sourceNode |
162
+ methodCall ( call , sourceNode , method ) and
163
+ sourceNode = trackInstance ( tp )
164
+ )
165
+ }
166
+
167
+ cached
168
+ private module Cached {
82
169
cached
83
- DataFlowCallable getTarget ( ) {
170
+ newtype TDataFlowCallable =
171
+ TCfgScope ( CfgScope scope ) or
172
+ TLibraryCallable ( LibraryCallable callable )
173
+
174
+ cached
175
+ newtype TDataFlowCall =
176
+ TNormalCall ( CfgNodes:: ExprNodes:: CallCfgNode c ) or
177
+ TSummaryCall ( FlowSummaryImpl:: Public:: SummarizedCallable c , DataFlow:: Node receiver ) {
178
+ FlowSummaryImpl:: Private:: summaryCallbackRange ( c , receiver )
179
+ }
180
+
181
+ cached
182
+ CfgScope getTarget ( CfgNodes:: ExprNodes:: CallCfgNode call ) {
84
183
exists ( string method |
85
184
exists ( Module tp |
86
- this . instanceMethodCall ( tp , method ) and
185
+ instanceMethodCall ( call , tp , method ) and
87
186
result = lookupMethod ( tp , method ) and
88
187
if result .( Method ) .isPrivate ( )
89
188
then
90
189
exists ( Self self |
91
- self = this .getReceiver ( ) .getExpr ( ) and
190
+ self = call .getReceiver ( ) .getExpr ( ) and
92
191
pragma [ only_bind_out ] ( self .getEnclosingModule ( ) .getModule ( ) .getSuperClass * ( ) ) =
93
192
pragma [ only_bind_out ] ( result .getEnclosingModule ( ) .getModule ( ) )
94
193
) and
95
194
// For now, we restrict the scope of top-level declarations to their file.
96
195
// This may remove some plausible targets, but also removes a lot of
97
196
// implausible targets
98
197
if result .getEnclosingModule ( ) instanceof Toplevel
99
- then result .getFile ( ) = this .getFile ( )
198
+ then result .getFile ( ) = call .getFile ( )
100
199
else any ( )
101
200
else any ( )
102
201
)
103
202
or
104
203
exists ( DataFlow:: LocalSourceNode sourceNode |
105
- this . methodCall ( sourceNode , method ) and
204
+ methodCall ( call , sourceNode , method ) and
106
205
sourceNode = trackSingletonMethod ( result , method )
107
206
)
108
207
)
109
208
or
110
209
exists ( Module superClass , string method |
111
- this . superCall ( superClass , method ) and
210
+ superCall ( call , superClass , method ) and
112
211
result = lookupMethod ( superClass , method )
113
212
)
114
213
or
115
- result = this . yieldCall ( )
214
+ result = yieldCall ( call )
116
215
}
117
216
}
118
217
218
+ import Cached
219
+
119
220
private DataFlow:: LocalSourceNode trackInstance ( Module tp , TypeTracker t ) {
120
221
t .start ( ) and
121
222
(
@@ -297,8 +398,13 @@ private DataFlow::LocalSourceNode trackModule(Module tp) {
297
398
298
399
/** Gets a viable run-time target for the call `call`. */
299
400
DataFlowCallable viableCallable ( DataFlowCall call ) {
300
- result = call .getTarget ( ) and
301
- not call .getExpr ( ) instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
401
+ result = TCfgScope ( getTarget ( call .asCall ( ) ) ) and
402
+ not call .asCall ( ) .getExpr ( ) instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
403
+ or
404
+ exists ( LibraryCallable callable |
405
+ result = TLibraryCallable ( callable ) and
406
+ call .asCall ( ) .getExpr ( ) = callable .getACall ( )
407
+ )
302
408
}
303
409
304
410
/**
@@ -307,7 +413,7 @@ DataFlowCallable viableCallable(DataFlowCall call) {
307
413
* qualifier accesses a parameter of the enclosing callable `c` (including
308
414
* the implicit `self` parameter).
309
415
*/
310
- predicate mayBenefitFromCallContext ( DataFlowCall call , Callable c ) { none ( ) }
416
+ predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) { none ( ) }
311
417
312
418
/**
313
419
* Gets a viable dispatch target of `call` in the context `ctx`. This is
0 commit comments