@@ -2,8 +2,9 @@ private import ruby
22private import codeql.ruby.CFG
33private import DataFlowPrivate
44private import codeql.ruby.typetracking.TypeTracker
5- private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlow
65private import codeql.ruby.ast.internal.Module
6+ private import FlowSummaryImpl as FlowSummaryImpl
7+ private import codeql.ruby.dataflow.FlowSummary
78
89newtype TReturnKind =
910 TNormalReturnKind ( ) or
@@ -39,83 +40,183 @@ class BreakReturnKind extends ReturnKind, TBreakReturnKind {
3940 override string toString ( ) { result = "break" }
4041}
4142
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 ( ) }
4347
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+ }
4651
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 ) }
5559
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 ) }
6362
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 ( ) ] }
7365
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 ;
81118
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 {
82169 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 ) {
84183 exists ( string method |
85184 exists ( Module tp |
86- this . instanceMethodCall ( tp , method ) and
185+ instanceMethodCall ( call , tp , method ) and
87186 result = lookupMethod ( tp , method ) and
88187 if result .( Method ) .isPrivate ( )
89188 then
90189 exists ( Self self |
91- self = this .getReceiver ( ) .getExpr ( ) and
190+ self = call .getReceiver ( ) .getExpr ( ) and
92191 pragma [ only_bind_out ] ( self .getEnclosingModule ( ) .getModule ( ) .getSuperClass * ( ) ) =
93192 pragma [ only_bind_out ] ( result .getEnclosingModule ( ) .getModule ( ) )
94193 ) and
95194 // For now, we restrict the scope of top-level declarations to their file.
96195 // This may remove some plausible targets, but also removes a lot of
97196 // implausible targets
98197 if result .getEnclosingModule ( ) instanceof Toplevel
99- then result .getFile ( ) = this .getFile ( )
198+ then result .getFile ( ) = call .getFile ( )
100199 else any ( )
101200 else any ( )
102201 )
103202 or
104203 exists ( DataFlow:: LocalSourceNode sourceNode |
105- this . methodCall ( sourceNode , method ) and
204+ methodCall ( call , sourceNode , method ) and
106205 sourceNode = trackSingletonMethod ( result , method )
107206 )
108207 )
109208 or
110209 exists ( Module superClass , string method |
111- this . superCall ( superClass , method ) and
210+ superCall ( call , superClass , method ) and
112211 result = lookupMethod ( superClass , method )
113212 )
114213 or
115- result = this . yieldCall ( )
214+ result = yieldCall ( call )
116215 }
117216}
118217
218+ import Cached
219+
119220private DataFlow:: LocalSourceNode trackInstance ( Module tp , TypeTracker t ) {
120221 t .start ( ) and
121222 (
@@ -297,8 +398,13 @@ private DataFlow::LocalSourceNode trackModule(Module tp) {
297398
298399/** Gets a viable run-time target for the call `call`. */
299400DataFlowCallable 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+ )
302408}
303409
304410/**
@@ -307,7 +413,7 @@ DataFlowCallable viableCallable(DataFlowCall call) {
307413 * qualifier accesses a parameter of the enclosing callable `c` (including
308414 * the implicit `self` parameter).
309415 */
310- predicate mayBenefitFromCallContext ( DataFlowCall call , Callable c ) { none ( ) }
416+ predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) { none ( ) }
311417
312418/**
313419 * Gets a viable dispatch target of `call` in the context `ctx`. This is
0 commit comments