Skip to content

Commit b171dc9

Browse files
authored
Merge pull request github#11477 from hvitved/ruby/call-ctx-rewrite
Ruby: Rework call-context sensitivity logic
2 parents 5bb1319 + bfbe5bd commit b171dc9

File tree

3 files changed

+185
-169
lines changed

3 files changed

+185
-169
lines changed

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

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -975,42 +975,35 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
975975
result = trackSingletonMethodOnInstance(method, name, TypeTracker::end())
976976
}
977977

978-
/** Same as `isInstance`, but includes local must-flow through SSA definitions. */
979-
private predicate isInstanceLocalMustFlow(DataFlow::Node n, Module tp, boolean exact) {
980-
isInstance(n, tp, exact)
981-
or
982-
exists(DataFlow::Node mid | isInstanceLocalMustFlow(mid, tp, exact) |
983-
n.asExpr() = mid.(SsaDefinitionNode).getDefinition().getARead()
984-
or
985-
n.(SsaDefinitionNode).getDefinition().(Ssa::WriteDefinition).assigns(mid.asExpr())
986-
)
987-
}
988-
989978
/**
990979
* Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, the receiver
991980
* of `call` is a parameter access, where the corresponding argument of `ctx` is `arg`.
992981
*
993-
* `name` is the name of the method being called by `call`.
982+
* `name` is the name of the method being called by `call`, `source` is a
983+
* `LocalSourceNode` that flows to `arg`, and `paramDef` is the SSA definition for the
984+
* parameter that is the receiver of `call`.
994985
*/
995986
pragma[nomagic]
996-
private predicate argFlowsToReceiver(
997-
RelevantCall ctx, ArgumentNode arg, RelevantCall call, Callable encl, string name
987+
private predicate argMustFlowToReceiver(
988+
RelevantCall ctx, DataFlow::LocalSourceNode source, ArgumentNode arg, SsaDefinitionNode paramDef,
989+
RelevantCall call, Callable encl, string name
998990
) {
999-
exists(
1000-
ParameterNodeImpl p, SsaDefinitionNode ssaNode, ParameterPosition ppos, ArgumentPosition apos
1001-
|
991+
exists(ParameterNodeImpl p, ParameterPosition ppos, ArgumentPosition apos |
1002992
// the receiver of `call` references `p`
1003-
LocalFlow::localFlowSsaParamInput(p, ssaNode) and
1004-
flowsToMethodCallReceiver(pragma[only_bind_into](call), pragma[only_bind_into](ssaNode),
1005-
pragma[only_bind_into](name)) and
993+
exists(DataFlow::Node receiver |
994+
LocalFlow::localFlowSsaParamInput(p, paramDef) and
995+
methodCall(pragma[only_bind_into](call), receiver, pragma[only_bind_into](name)) and
996+
receiver.asExpr() = paramDef.getDefinition().getARead()
997+
) and
1006998
// `p` is a parameter of `encl`,
1007999
encl = call.getScope() and
10081000
p.isParameterOf(TCfgScope(encl), ppos) and
10091001
// `ctx` targets `encl`
10101002
getTarget(ctx) = encl and
10111003
// `arg` is the argument for `p` in the call `ctx`
10121004
arg.sourceArgumentOf(ctx, apos) and
1013-
parameterMatch(ppos, apos)
1005+
parameterMatch(ppos, apos) and
1006+
source.flowsTo(arg)
10141007
)
10151008
}
10161009

@@ -1027,20 +1020,11 @@ private predicate mayBenefitFromCallContextInstance(
10271020
RelevantCall ctx, RelevantCall call, ArgumentNode arg, Callable encl, Module tp, boolean exact,
10281021
string name
10291022
) {
1030-
argFlowsToReceiver(ctx, pragma[only_bind_into](arg), call, encl, pragma[only_bind_into](name)) and
1031-
// `arg` has a relevant instance type
1032-
isInstanceLocalMustFlow(arg, tp, exact) and
1033-
exists(lookupMethod(tp, pragma[only_bind_into](name)))
1034-
}
1035-
1036-
/** Same as `resolveConstantReadAccess`, but includes local must-flow through SSA definitions. */
1037-
private predicate resolveConstantReadAccessMustFlow(DataFlow::Node n, Module tp) {
1038-
tp = resolveConstantReadAccess(n.asExpr().getExpr())
1039-
or
1040-
exists(DataFlow::Node mid | resolveConstantReadAccessMustFlow(mid, tp) |
1041-
n.asExpr() = mid.(SsaDefinitionNode).getDefinition().getARead()
1042-
or
1043-
n.(SsaDefinitionNode).getDefinition().(Ssa::WriteDefinition).assigns(mid.asExpr())
1023+
exists(DataFlow::LocalSourceNode source |
1024+
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, _, call, encl,
1025+
pragma[only_bind_into](name)) and
1026+
source = trackInstance(tp, exact) and
1027+
exists(lookupMethod(tp, pragma[only_bind_into](name)))
10441028
)
10451029
}
10461030

@@ -1057,10 +1041,12 @@ private predicate mayBenefitFromCallContextSingleton(
10571041
RelevantCall ctx, RelevantCall call, ArgumentNode arg, Callable encl, Module tp, boolean exact,
10581042
string name
10591043
) {
1060-
argFlowsToReceiver(ctx, pragma[only_bind_into](arg), call, encl, pragma[only_bind_into](name)) and
1061-
// `arg` has a relevant module type
1062-
(
1063-
resolveConstantReadAccessMustFlow(arg, tp) and
1044+
exists(DataFlow::LocalSourceNode source |
1045+
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), pragma[only_bind_into](arg), _, call,
1046+
encl, pragma[only_bind_into](name)) and
1047+
exists(lookupSingletonMethod(tp, pragma[only_bind_into](name), exact))
1048+
|
1049+
source = trackModuleAccess(tp) and
10641050
exact = true
10651051
or
10661052
exists(SelfVariable self | arg.asExpr().getExpr() = self.getAnAccess() |
@@ -1073,8 +1059,7 @@ private predicate mayBenefitFromCallContextSingleton(
10731059
exact = false
10741060
)
10751061
)
1076-
) and
1077-
exists(lookupSingletonMethod(tp, pragma[only_bind_into](name), exact))
1062+
)
10781063
}
10791064

10801065
/**
@@ -1101,7 +1086,7 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
11011086
exists(RelevantCall call0, Callable res |
11021087
call0 = call.asCall() and
11031088
res = result.asCallable() and
1104-
res = getTarget(call0) and // make sure to not include e.g. private methods
1089+
result = viableSourceCallable(call) and // make sure to not include e.g. private methods
11051090
exists(Module m, boolean exact, string name |
11061091
mayBenefitFromCallContextInstance(ctx.asCall(), pragma[only_bind_into](call0), _, _,
11071092
pragma[only_bind_into](m), exact, pragma[only_bind_into](name)) and
@@ -1113,18 +1098,22 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
11131098
)
11141099
)
11151100
or
1116-
// `ctx` cannot provide a type bound
1117-
exists(RelevantCall call0, RelevantCall ctx0, ArgumentNode arg, string name |
1101+
// `ctx` cannot provide a type bound, and the receiver of the call is `self`;
1102+
// in this case, still apply an open-world assumption
1103+
exists(
1104+
RelevantCall call0, RelevantCall ctx0, ArgumentNode arg, SsaSelfDefinitionNode self,
1105+
string name
1106+
|
11181107
call0 = call.asCall() and
11191108
ctx0 = ctx.asCall() and
1120-
argFlowsToReceiver(ctx0, arg, call0, _, name) and
1109+
argMustFlowToReceiver(ctx0, _, arg, self, call0, _, name) and
11211110
not mayBenefitFromCallContextInstance(ctx0, call0, arg, _, _, _, name) and
11221111
not mayBenefitFromCallContextSingleton(ctx0, call0, arg, _, _, _, name) and
11231112
result = viableSourceCallable(call)
11241113
)
11251114
or
11261115
// library calls should always be able to resolve
1127-
argFlowsToReceiver(ctx.asCall(), _, call.asCall(), _, _) and
1116+
argMustFlowToReceiver(ctx.asCall(), _, _, _, call.asCall(), _, _) and
11281117
result = viableLibraryCallable(call)
11291118
)
11301119
}

0 commit comments

Comments
 (0)