Skip to content

Commit 0855797

Browse files
authored
Merge pull request github#12499 from hvitved/ruby/more-constructor-flow
Ruby: Add missing flow through `self.new` constructor calls
2 parents 04f422e + 163bb2b commit 0855797

File tree

7 files changed

+674
-538
lines changed

7 files changed

+674
-538
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Data flow through `initialize` methods is now taken into account also when the receiver of a `new` call is an (implicit or explicit) `self`.

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

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,7 @@ private Callable viableSourceCallableNonInit(RelevantCall call) {
299299
not call.getExpr() instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
300300
}
301301

302-
private Callable viableSourceCallableInit(RelevantCall call) {
303-
result = getInitializeTarget(call) and
304-
not isUserDefinedNew(getTarget(call))
305-
}
302+
private Callable viableSourceCallableInit(RelevantCall call) { result = getInitializeTarget(call) }
306303

307304
/** Holds if `call` may resolve to the returned source-code method. */
308305
private Callable viableSourceCallable(RelevantCall call) {
@@ -374,9 +371,14 @@ private module Cached {
374371
*/
375372
cached
376373
Method getInitializeTarget(RelevantCall new) {
377-
exists(Module m |
378-
moduleFlowsToMethodCallReceiver(new, m, "new") and
379-
result = lookupMethod(m, "initialize")
374+
exists(Module m, boolean exact |
375+
isStandardNewCall(new, m, exact) and
376+
result = lookupMethod(m, "initialize", exact) and
377+
// In the case where `exact = false`, we need to check that there is
378+
// no user-defined `new` method in between `m` and the enclosing module
379+
// of the `initialize` method (`isStandardNewCall` already checks that
380+
// there is no user-defined `new` method in `m` or any of `m`'s ancestors)
381+
not hasUserDefinedNew(result.getEnclosingModule().getModule())
380382
)
381383
}
382384

@@ -481,6 +483,35 @@ private predicate hasUserDefinedNew(Module m) {
481483
)
482484
}
483485

486+
/**
487+
* Holds if `new` is a call to `new`, targeting a class of type `m` (or a
488+
* sub class, when `exact = false`), where there is no user-defined
489+
* `self.new` on `m`.
490+
*/
491+
pragma[nomagic]
492+
private predicate isStandardNewCall(RelevantCall new, Module m, boolean exact) {
493+
exists(DataFlow::LocalSourceNode sourceNode |
494+
flowsToMethodCallReceiver(new, sourceNode, "new") and
495+
// `m` should not have a user-defined `self.new` method
496+
not hasUserDefinedNew(m)
497+
|
498+
// `C.new`
499+
sourceNode = trackModuleAccess(m) and
500+
exact = true
501+
or
502+
// `self.new` inside a module
503+
selfInModule(sourceNode.(SsaSelfDefinitionNode).getVariable(), m) and
504+
exact = true
505+
or
506+
// `self.new` inside a singleton method
507+
exists(MethodBase caller |
508+
selfInMethod(sourceNode.(SsaSelfDefinitionNode).getVariable(), caller, m) and
509+
singletonMethod(caller, _, _) and
510+
exact = false
511+
)
512+
)
513+
}
514+
484515
/** Holds if `n` is an instance of type `tp`. */
485516
private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
486517
n.asExpr().getExpr() instanceof NilLiteral and
@@ -535,27 +566,7 @@ private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
535566
tp = TResolved("Proc") and
536567
exact = true
537568
or
538-
exists(RelevantCall call, DataFlow::LocalSourceNode sourceNode |
539-
flowsToMethodCallReceiver(call, sourceNode, "new") and
540-
n.asExpr() = call and
541-
// `tp` should not have a user-defined `self.new` method
542-
not hasUserDefinedNew(tp)
543-
|
544-
// `C.new`
545-
sourceNode = trackModuleAccess(tp) and
546-
exact = true
547-
or
548-
// `self.new` inside a module
549-
selfInModule(sourceNode.(SsaSelfDefinitionNode).getVariable(), tp) and
550-
exact = true
551-
or
552-
// `self.new` inside a singleton method
553-
exists(MethodBase caller |
554-
selfInMethod(sourceNode.(SsaSelfDefinitionNode).getVariable(), caller, tp) and
555-
singletonMethod(caller, _, _) and
556-
exact = false
557-
)
558-
)
569+
isStandardNewCall(n.asExpr(), tp, exact)
559570
or
560571
// `self` reference in method or top-level (but not in module or singleton method,
561572
// where instance methods cannot be called; only singleton methods)

0 commit comments

Comments
 (0)