Skip to content

Commit 13ada1e

Browse files
committed
Ruby: Remove canonical return nodes
1 parent deee314 commit 13ada1e

File tree

10 files changed

+229
-417
lines changed

10 files changed

+229
-417
lines changed

ruby/ql/lib/codeql/ruby/ApiGraphs.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ module API {
778778
or
779779
exists(TypeTracker t2 |
780780
result = trackUseNode(src, t2).track(t2, t) and
781-
not result instanceof DataFlowPrivate::SelfParameterNode
781+
not result instanceof DataFlow::SelfParameterNode
782782
)
783783
}
784784

@@ -800,7 +800,7 @@ module API {
800800
or
801801
exists(TypeBackTracker t2, DataFlow::LocalSourceNode mid |
802802
mid = trackDefNode(rhs, t2) and
803-
not mid instanceof DataFlowPrivate::SelfParameterNode and
803+
not mid instanceof DataFlow::SelfParameterNode and
804804
result = mid.backtrack(t2, t)
805805
)
806806
}

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

Lines changed: 158 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ private import codeql.ruby.dataflow.FlowSummary
1010
private import codeql.ruby.dataflow.SSA
1111

1212
/**
13-
* A `LocalSourceNode` for a `self` variable. This is either an implicit `self`
14-
* parameter or an implicit SSA entry definition.
13+
* A `LocalSourceNode` for a `self` variable. This is the implicit `self`
14+
* parameter, when it exists, otherwise the implicit SSA entry definition.
1515
*/
1616
private class SelfLocalSourceNode extends DataFlow::LocalSourceNode {
1717
private SelfVariable self;
1818

1919
SelfLocalSourceNode() {
20-
self = this.(SelfParameterNode).getSelfVariable()
20+
self = this.(SelfParameterNodeImpl).getSelfVariable()
2121
or
22-
self = this.(SsaSelfDefinitionNode).getVariable()
22+
self = this.(SsaSelfDefinitionNode).getVariable() and
23+
not LocalFlow::localFlowSsaParamInput(_, this)
2324
}
2425

2526
/** Gets the `self` variable. */
@@ -469,23 +470,43 @@ private module Cached {
469470

470471
import Cached
471472

473+
pragma[nomagic]
474+
private predicate stepCallProj(DataFlow::LocalSourceNode nodeFrom, StepSummary summary) {
475+
StepSummary::stepCall(nodeFrom, _, summary)
476+
}
477+
478+
pragma[nomagic]
479+
private predicate isNotSelf(DataFlow::Node n) { not n instanceof SelfParameterNodeImpl }
480+
472481
pragma[nomagic]
473482
private DataFlow::LocalSourceNode trackModuleAccess(Module m, TypeTracker t) {
474483
t.start() and m = resolveConstantReadAccess(result.asExpr().getExpr())
475484
or
476-
exists(TypeTracker t2, StepSummary summary |
477-
result = trackModuleAccessRec(m, t2, summary) and t = t2.append(summary)
485+
// We exclude steps into `self` parameters, and instead rely on the type of the
486+
// enclosing module
487+
isNotSelf(result) and
488+
(
489+
exists(TypeTracker t2 | t = t2.stepNoCall(trackModuleAccess(m, t2), result))
490+
or
491+
exists(StepSummary summary |
492+
// non-linear recursion
493+
StepSummary::stepCall(trackModuleAccessCall(m, t, summary), result, summary)
494+
)
478495
)
479496
}
480497

481-
/**
482-
* We exclude steps into `self` parameters, and instead rely on the type of the
483-
* enclosing module.
484-
*/
498+
bindingset[t, summary]
499+
pragma[inline_late]
500+
private TypeTracker append(TypeTracker t, StepSummary summary) { result = t.append(summary) }
501+
485502
pragma[nomagic]
486-
private DataFlow::LocalSourceNode trackModuleAccessRec(Module m, TypeTracker t, StepSummary summary) {
487-
StepSummary::step(trackModuleAccess(m, t), result, summary) and
488-
not result instanceof SelfParameterNode
503+
private DataFlow::LocalSourceNode trackModuleAccessCall(Module m, TypeTracker t, StepSummary summary) {
504+
exists(TypeTracker t2 |
505+
// non-linear recursion
506+
result = trackModuleAccess(m, t2) and
507+
stepCallProj(result, summary) and
508+
t = append(t2, summary)
509+
)
489510
}
490511

491512
pragma[nomagic]
@@ -498,7 +519,7 @@ private predicate hasUserDefinedNew(Module m) {
498519
exists(DataFlow::MethodNode method |
499520
// not `getAnAncestor` because singleton methods cannot be included
500521
singletonMethodOnModule(method.asCallableAstNode(), "new", m.getSuperClass*()) and
501-
not method.getSelfParameter().getAMethodCall("allocate").flowsTo(method.getAReturningNode())
522+
not method.getSelfParameter().getAMethodCall("allocate").flowsTo(method.getAReturnNode())
502523
)
503524
}
504525

@@ -610,6 +631,49 @@ private predicate isInstance(DataFlow::Node n, Module tp, boolean exact) {
610631
exact = false
611632
}
612633

634+
private predicate localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
635+
localFlowStepTypeTracker(nodeFrom, nodeTo) and
636+
summary.toString() = "level"
637+
}
638+
639+
pragma[nomagic]
640+
private predicate hasAdjacentTypeCheckedReads(DataFlow::Node node) {
641+
hasAdjacentTypeCheckedReads(_, _, node.asExpr(), _)
642+
}
643+
644+
pragma[nomagic]
645+
private predicate smallStepNoCallForTrackInstance0(
646+
DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary
647+
) {
648+
// We exclude steps into `self` parameters. For those, we instead rely on the type of
649+
// the enclosing module
650+
StepSummary::smallstepNoCall(nodeFrom, nodeTo, summary) and
651+
isNotSelf(nodeTo)
652+
or
653+
// We exclude steps into type checked variables. For those, we instead rely on the
654+
// type being checked against
655+
localFlowStep(nodeFrom, nodeTo, summary) and
656+
not hasAdjacentTypeCheckedReads(nodeTo)
657+
}
658+
659+
pragma[nomagic]
660+
private predicate smallStepNoCallForTrackInstance0Proj(DataFlow::Node nodeFrom, StepSummary summary) {
661+
smallStepNoCallForTrackInstance0(nodeFrom, _, summary)
662+
}
663+
664+
bindingset[t, nodeFrom]
665+
pragma[inline_late]
666+
pragma[noopt]
667+
private TypeTracker smallStepNoCallForTrackInstance(
668+
TypeTracker t, DataFlow::Node nodeFrom, DataFlow::Node nodeTo
669+
) {
670+
exists(StepSummary summary |
671+
smallStepNoCallForTrackInstance0Proj(nodeFrom, summary) and
672+
result = t.append(summary) and
673+
smallStepNoCallForTrackInstance0(nodeFrom, nodeTo, summary)
674+
)
675+
}
676+
613677
pragma[nomagic]
614678
private DataFlow::Node trackInstance(Module tp, boolean exact, TypeTracker t) {
615679
t.start() and
@@ -631,35 +695,33 @@ private DataFlow::Node trackInstance(Module tp, boolean exact, TypeTracker t) {
631695
)
632696
)
633697
or
634-
exists(TypeTracker t2, StepSummary summary |
635-
result = trackInstanceRec(tp, t2, exact, summary) and t = t2.append(summary)
698+
exists(TypeTracker t2 |
699+
t = smallStepNoCallForTrackInstance(t2, trackInstance(tp, exact, t2), result)
700+
)
701+
or
702+
// We exclude steps into `self` parameters. For those, we instead rely on the type of
703+
// the enclosing module
704+
exists(StepSummary summary |
705+
// non-linear recursion
706+
StepSummary::smallstepCall(trackInstanceCall(tp, t, exact, summary), result, summary) and
707+
isNotSelf(result)
636708
)
637-
}
638-
639-
private predicate localFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
640-
localFlowStepTypeTracker(nodeFrom, nodeTo) and
641-
summary.toString() = "level"
642709
}
643710

644711
pragma[nomagic]
645-
private predicate hasAdjacentTypeCheckedReads(DataFlow::Node node) {
646-
hasAdjacentTypeCheckedReads(_, _, node.asExpr(), _)
712+
private predicate smallStepCallProj(DataFlow::Node nodeFrom, StepSummary summary) {
713+
StepSummary::smallstepCall(nodeFrom, _, summary)
647714
}
648715

649-
/**
650-
* We exclude steps into `self` parameters and type checked variables. For those,
651-
* we instead rely on the type of the enclosing module resp. the type being checked
652-
* against, and apply an open-world assumption when determining possible dispatch
653-
* targets.
654-
*/
655716
pragma[nomagic]
656-
private DataFlow::Node trackInstanceRec(Module tp, TypeTracker t, boolean exact, StepSummary summary) {
657-
exists(DataFlow::Node mid | mid = trackInstance(tp, exact, t) |
658-
StepSummary::smallstep(mid, result, summary) and
659-
not result instanceof SelfParameterNode
660-
or
661-
localFlowStep(mid, result, summary) and
662-
not hasAdjacentTypeCheckedReads(result)
717+
private DataFlow::Node trackInstanceCall(
718+
Module tp, TypeTracker t, boolean exact, StepSummary summary
719+
) {
720+
exists(TypeTracker t2 |
721+
// non-linear recursion
722+
result = trackInstance(tp, exact, t2) and
723+
smallStepCallProj(result, summary) and
724+
t = append(t2, summary)
663725
)
664726
}
665727

@@ -710,20 +772,27 @@ pragma[nomagic]
710772
private DataFlow::LocalSourceNode trackBlock(Block block, TypeTracker t) {
711773
t.start() and result.asExpr().getExpr() = block
712774
or
713-
exists(TypeTracker t2, StepSummary summary |
714-
result = trackBlockRec(block, t2, summary) and
715-
t = t2.append(summary)
775+
// We exclude steps into `self` parameters, which may happen when the code
776+
// base contains implementations of `call`.
777+
isNotSelf(result) and
778+
(
779+
exists(TypeTracker t2 | t = t2.stepNoCall(trackBlock(block, t2), result))
780+
or
781+
exists(StepSummary summary |
782+
// non-linear recursion
783+
StepSummary::stepCall(trackBlockCall(block, t, summary), result, summary)
784+
)
716785
)
717786
}
718787

719-
/**
720-
* We exclude steps into `self` parameters, which may happen when the code
721-
* base contains implementations of `call`.
722-
*/
723788
pragma[nomagic]
724-
private DataFlow::LocalSourceNode trackBlockRec(Block block, TypeTracker t, StepSummary summary) {
725-
StepSummary::step(trackBlock(block, t), result, summary) and
726-
not result instanceof SelfParameterNode
789+
private DataFlow::LocalSourceNode trackBlockCall(Block block, TypeTracker t, StepSummary summary) {
790+
exists(TypeTracker t2 |
791+
// non-linear recursion
792+
result = trackBlock(block, t2) and
793+
stepCallProj(result, summary) and
794+
t = append(t2, summary)
795+
)
727796
}
728797

729798
pragma[nomagic]
@@ -942,7 +1011,7 @@ private predicate paramReturnFlow(
9421011
|
9431012
nodeFromPreExpr = p.getParameter().(NamedParameter).getVariable().getAnAccess()
9441013
or
945-
nodeFromPreExpr = p.(SelfParameterNode).getSelfVariable().getAnAccess()
1014+
nodeFromPreExpr = p.(SelfParameterNodeImpl).getSelfVariable().getAnAccess()
9461015
)
9471016
}
9481017

@@ -951,31 +1020,53 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
9511020
t.start() and
9521021
singletonMethodOnInstance(method, name, result.asExpr().getExpr())
9531022
or
954-
exists(TypeTracker t2, StepSummary summary |
955-
result = trackSingletonMethodOnInstanceRec(method, name, t2, summary) and
956-
t = t2.append(summary) and
957-
// Stop flow at redefinitions.
958-
//
959-
// Example:
960-
// ```rb
961-
// def x.foo; end
962-
// def x.foo; end
963-
// x.foo # <- we want to resolve this call to the second definition only
964-
// ```
965-
not singletonMethodOnInstance(_, name, result.asExpr().getExpr())
966-
)
1023+
(
1024+
exists(TypeTracker t2 |
1025+
t = t2.smallstepNoCall(trackSingletonMethodOnInstance(method, name, t2), result)
1026+
)
1027+
or
1028+
exists(StepSummary summary |
1029+
// non-linear recursion
1030+
smallStepCallForTrackSingletonMethodOnInstance(trackSingletonMethodOnInstanceCall(method,
1031+
name, t, summary), result, summary)
1032+
)
1033+
) and
1034+
// Stop flow at redefinitions.
1035+
//
1036+
// Example:
1037+
// ```rb
1038+
// def x.foo; end
1039+
// def x.foo; end
1040+
// x.foo # <- we want to resolve this call to the second definition only
1041+
// ```
1042+
not singletonMethodOnInstance(_, name, result.asExpr().getExpr())
1043+
}
1044+
1045+
pragma[nomagic]
1046+
private predicate smallStepCallForTrackSingletonMethodOnInstance(
1047+
DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary
1048+
) {
1049+
StepSummary::smallstepCall(nodeFrom, nodeTo, summary)
1050+
or
1051+
paramReturnFlow(nodeFrom, nodeTo, summary)
9671052
}
9681053

9691054
pragma[nomagic]
970-
private DataFlow::Node trackSingletonMethodOnInstanceRec(
1055+
private predicate smallStepCallForTrackSingletonMethodOnInstanceProj(
1056+
DataFlow::Node nodeFrom, StepSummary summary
1057+
) {
1058+
smallStepCallForTrackSingletonMethodOnInstance(nodeFrom, _, summary)
1059+
}
1060+
1061+
pragma[nomagic]
1062+
private DataFlow::Node trackSingletonMethodOnInstanceCall(
9711063
MethodBase method, string name, TypeTracker t, StepSummary summary
9721064
) {
973-
exists(DataFlow::Node mid | mid = trackSingletonMethodOnInstance(method, name, t) |
974-
StepSummary::smallstep(mid, result, summary)
975-
or
976-
paramReturnFlow(mid, result, summary)
977-
or
978-
localFlowStep(mid, result, summary)
1065+
exists(TypeTracker t2 |
1066+
// non-linear recursion
1067+
result = trackSingletonMethodOnInstance(method, name, t2) and
1068+
smallStepCallForTrackSingletonMethodOnInstanceProj(result, summary) and
1069+
t = append(t2, summary)
9791070
)
9801071
}
9811072

0 commit comments

Comments
 (0)