Skip to content

Commit 48bdf13

Browse files
committed
Ruby: Take overrides into account for singleton methods defined on modules
1 parent 7608276 commit 48bdf13

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ private module Cached {
389389
// ```
390390
exists(DataFlow::Node sourceNode, Module m |
391391
flowsToMethodCall(call, sourceNode, method) and
392-
singletonMethodOnModule(result, method, m)
392+
result = lookupSingletonMethod(m, method)
393393
|
394394
// ```rb
395395
// def C.singleton; end # <- result
@@ -725,14 +725,33 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
725725
selfInModule(object.(SelfVariableReadAccess).getVariable(), m)
726726
)
727727
or
728-
flowsToSingletonMethodObject(trackModuleAccess(m), method, name)
728+
exists(DataFlow::LocalSourceNode sourceNode |
729+
m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
730+
flowsToSingletonMethodObject(sourceNode, method, name)
731+
)
729732
or
730733
exists(Module other |
731734
extendCallModule(m, other) and
732735
method = lookupMethod(other, name)
733736
)
734737
}
735738

739+
pragma[nomagic]
740+
private MethodBase lookupSingletonMethod(Module m, string name) {
741+
singletonMethodOnModule(result, name, m)
742+
or
743+
// cannot be part of `singletonMethodOnModule` because it would introduce
744+
// negative recursion below
745+
exists(DataFlow::LocalSourceNode sourceNode |
746+
sourceNode = trackModuleAccess(m) and
747+
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
748+
flowsToSingletonMethodObject(sourceNode, result, name)
749+
)
750+
or
751+
not singletonMethodOnModule(_, name, m) and
752+
result = lookupSingletonMethod(m.getSuperClass(), name)
753+
}
754+
736755
/**
737756
* Holds if `method` is a singleton method named `name`, defined on expression
738757
* `object`, where `object` is not likely to resolve to a module:

ruby/ql/test/library-tests/modules/callgraph.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ getTarget
161161
| calls.rb:412:9:412:44 | call to puts | calls.rb:102:5:102:30 | puts |
162162
| calls.rb:416:1:416:29 | call to singleton1 | calls.rb:406:9:408:11 | singleton1 |
163163
| calls.rb:417:1:417:29 | call to singleton2 | calls.rb:411:5:413:7 | singleton2 |
164+
| calls.rb:418:1:418:34 | call to call_singleton1 | calls.rb:383:9:385:11 | call_singleton1 |
165+
| calls.rb:419:1:419:34 | call to call_singleton2 | calls.rb:392:5:394:7 | call_singleton2 |
164166
| calls.rb:424:13:424:48 | call to puts | calls.rb:102:5:102:30 | puts |
165167
| calls.rb:429:9:429:44 | call to puts | calls.rb:102:5:102:30 | puts |
166168
| calls.rb:432:13:432:48 | call to puts | calls.rb:102:5:102:30 | puts |
@@ -290,8 +292,6 @@ unresolvedCall
290292
| calls.rb:274:1:274:14 | call to singleton_g |
291293
| calls.rb:276:1:276:14 | call to singleton_g |
292294
| calls.rb:313:9:313:20 | call to instance |
293-
| calls.rb:418:1:418:34 | call to call_singleton1 |
294-
| calls.rb:419:1:419:34 | call to call_singleton2 |
295295
| calls.rb:422:8:422:13 | call to rand |
296296
| calls.rb:422:8:422:17 | ... > ... |
297297
| calls.rb:439:9:439:10 | call to m3 |

0 commit comments

Comments
 (0)