mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Ruby: Rework call-context sensitivity logic
This commit is contained in:
@@ -975,34 +975,26 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
|
||||
result = trackSingletonMethodOnInstance(method, name, TypeTracker::end())
|
||||
}
|
||||
|
||||
/** Same as `isInstance`, but includes local must-flow through SSA definitions. */
|
||||
private predicate isInstanceLocalMustFlow(DataFlow::Node n, Module tp, boolean exact) {
|
||||
isInstance(n, tp, exact)
|
||||
or
|
||||
exists(DataFlow::Node mid | isInstanceLocalMustFlow(mid, tp, exact) |
|
||||
n.asExpr() = mid.(SsaDefinitionNode).getDefinition().getARead()
|
||||
or
|
||||
n.(SsaDefinitionNode).getDefinition().(Ssa::WriteDefinition).assigns(mid.asExpr())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, the receiver
|
||||
* of `call` is a parameter access, where the corresponding argument of `ctx` is `arg`.
|
||||
*
|
||||
* `name` is the name of the method being called by `call`.
|
||||
* `name` is the name of the method being called by `call`, `source` is a
|
||||
* `LocalSourceNode` that flows to `arg`, and `paramDef` is the SSA definition for the
|
||||
* parameter that is the receiver of `call`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate argFlowsToReceiver(
|
||||
RelevantCall ctx, ArgumentNode arg, RelevantCall call, Callable encl, string name
|
||||
private predicate argMustFlowToReceiver(
|
||||
RelevantCall ctx, DataFlow::LocalSourceNode source, ArgumentNode arg, SsaDefinitionNode paramDef,
|
||||
RelevantCall call, Callable encl, string name
|
||||
) {
|
||||
exists(
|
||||
ParameterNodeImpl p, SsaDefinitionNode ssaNode, ParameterPosition ppos, ArgumentPosition apos
|
||||
|
|
||||
exists(ParameterNodeImpl p, ParameterPosition ppos, ArgumentPosition apos |
|
||||
// the receiver of `call` references `p`
|
||||
LocalFlow::localFlowSsaParamInput(p, ssaNode) and
|
||||
flowsToMethodCallReceiver(pragma[only_bind_into](call), pragma[only_bind_into](ssaNode),
|
||||
pragma[only_bind_into](name)) and
|
||||
exists(DataFlow::Node receiver |
|
||||
LocalFlow::localFlowSsaParamInput(p, paramDef) and
|
||||
methodCall(pragma[only_bind_into](call), receiver, pragma[only_bind_into](name)) and
|
||||
receiver.asExpr() = paramDef.getDefinition().getARead()
|
||||
) and
|
||||
// `p` is a parameter of `encl`,
|
||||
encl = call.getScope() and
|
||||
p.isParameterOf(TCfgScope(encl), ppos) and
|
||||
@@ -1010,7 +1002,8 @@ private predicate argFlowsToReceiver(
|
||||
getTarget(ctx) = encl and
|
||||
// `arg` is the argument for `p` in the call `ctx`
|
||||
arg.sourceArgumentOf(ctx, apos) and
|
||||
parameterMatch(ppos, apos)
|
||||
parameterMatch(ppos, apos) and
|
||||
source.flowsTo(arg)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1027,20 +1020,11 @@ private predicate mayBenefitFromCallContextInstance(
|
||||
RelevantCall ctx, RelevantCall call, ArgumentNode arg, Callable encl, Module tp, boolean exact,
|
||||
string name
|
||||
) {
|
||||
argFlowsToReceiver(ctx, pragma[only_bind_into](arg), call, encl, pragma[only_bind_into](name)) and
|
||||
// `arg` has a relevant instance type
|
||||
isInstanceLocalMustFlow(arg, tp, exact) and
|
||||
exists(lookupMethod(tp, pragma[only_bind_into](name)))
|
||||
}
|
||||
|
||||
/** Same as `resolveConstantReadAccess`, but includes local must-flow through SSA definitions. */
|
||||
private predicate resolveConstantReadAccessMustFlow(DataFlow::Node n, Module tp) {
|
||||
tp = resolveConstantReadAccess(n.asExpr().getExpr())
|
||||
or
|
||||
exists(DataFlow::Node mid | resolveConstantReadAccessMustFlow(mid, tp) |
|
||||
n.asExpr() = mid.(SsaDefinitionNode).getDefinition().getARead()
|
||||
or
|
||||
n.(SsaDefinitionNode).getDefinition().(Ssa::WriteDefinition).assigns(mid.asExpr())
|
||||
exists(DataFlow::LocalSourceNode source |
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, _, call, encl,
|
||||
pragma[only_bind_into](name)) and
|
||||
source = trackInstance(tp, exact) and
|
||||
exists(lookupMethod(tp, pragma[only_bind_into](name)))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1057,10 +1041,12 @@ private predicate mayBenefitFromCallContextSingleton(
|
||||
RelevantCall ctx, RelevantCall call, ArgumentNode arg, Callable encl, Module tp, boolean exact,
|
||||
string name
|
||||
) {
|
||||
argFlowsToReceiver(ctx, pragma[only_bind_into](arg), call, encl, pragma[only_bind_into](name)) and
|
||||
// `arg` has a relevant module type
|
||||
(
|
||||
resolveConstantReadAccessMustFlow(arg, tp) and
|
||||
exists(DataFlow::LocalSourceNode source |
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), pragma[only_bind_into](arg), _, call,
|
||||
encl, pragma[only_bind_into](name)) and
|
||||
exists(lookupSingletonMethod(tp, pragma[only_bind_into](name), exact))
|
||||
|
|
||||
source = trackModuleAccess(tp) and
|
||||
exact = true
|
||||
or
|
||||
exists(SelfVariable self | arg.asExpr().getExpr() = self.getAnAccess() |
|
||||
@@ -1073,8 +1059,7 @@ private predicate mayBenefitFromCallContextSingleton(
|
||||
exact = false
|
||||
)
|
||||
)
|
||||
) and
|
||||
exists(lookupSingletonMethod(tp, pragma[only_bind_into](name), exact))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1101,7 +1086,7 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
exists(RelevantCall call0, Callable res |
|
||||
call0 = call.asCall() and
|
||||
res = result.asCallable() and
|
||||
res = getTarget(call0) and // make sure to not include e.g. private methods
|
||||
result = viableSourceCallable(call) and // make sure to not include e.g. private methods
|
||||
exists(Module m, boolean exact, string name |
|
||||
mayBenefitFromCallContextInstance(ctx.asCall(), pragma[only_bind_into](call0), _, _,
|
||||
pragma[only_bind_into](m), exact, pragma[only_bind_into](name)) and
|
||||
@@ -1113,18 +1098,22 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
)
|
||||
)
|
||||
or
|
||||
// `ctx` cannot provide a type bound
|
||||
exists(RelevantCall call0, RelevantCall ctx0, ArgumentNode arg, string name |
|
||||
// `ctx` cannot provide a type bound, and the receiver of the call is `self`;
|
||||
// in this case, still apply an open-world assumption
|
||||
exists(
|
||||
RelevantCall call0, RelevantCall ctx0, ArgumentNode arg, SsaSelfDefinitionNode self,
|
||||
string name
|
||||
|
|
||||
call0 = call.asCall() and
|
||||
ctx0 = ctx.asCall() and
|
||||
argFlowsToReceiver(ctx0, arg, call0, _, name) and
|
||||
argMustFlowToReceiver(ctx0, _, arg, self, call0, _, name) and
|
||||
not mayBenefitFromCallContextInstance(ctx0, call0, arg, _, _, _, name) and
|
||||
not mayBenefitFromCallContextSingleton(ctx0, call0, arg, _, _, _, name) and
|
||||
result = viableSourceCallable(call)
|
||||
)
|
||||
or
|
||||
// library calls should always be able to resolve
|
||||
argFlowsToReceiver(ctx.asCall(), _, call.asCall(), _, _) and
|
||||
argMustFlowToReceiver(ctx.asCall(), _, _, _, call.asCall(), _, _) and
|
||||
result = viableLibraryCallable(call)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user