mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Merge pull request #15468 from hvitved/ruby/ctx-sensitivity-rework
This commit is contained in:
@@ -58,7 +58,7 @@ class Call extends Expr instanceof CallImpl {
|
||||
TCfgScope(result) = viableCallableLambda(c, _)
|
||||
)
|
||||
or
|
||||
result = getTarget(this.getAControlFlowNode())
|
||||
result = getTarget(TNormalCall(this.getAControlFlowNode()))
|
||||
}
|
||||
|
||||
override AstNode getAChild(string pred) {
|
||||
|
||||
@@ -70,7 +70,7 @@ deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryC
|
||||
*/
|
||||
private module LibraryCallbackSummaries {
|
||||
private predicate libraryCall(CfgNodes::ExprNodes::CallCfgNode call) {
|
||||
not exists(getTarget(call))
|
||||
not exists(getTarget(TNormalCall(call)))
|
||||
}
|
||||
|
||||
private DataFlow::LocalSourceNode trackLambdaCreation(TypeTracker t) {
|
||||
|
||||
@@ -87,18 +87,22 @@ abstract class LibraryCallable extends string {
|
||||
* defined in library code.
|
||||
*/
|
||||
class DataFlowCallable extends TDataFlowCallable {
|
||||
/** Gets the underlying source code callable, if any. */
|
||||
Callable asCallable() { this = TCfgScope(result) }
|
||||
/**
|
||||
* Gets the underlying CFG scope, if any.
|
||||
*
|
||||
* This is usually a `Callable`, but can also be a `Toplevel` file.
|
||||
*/
|
||||
CfgScope asCfgScope() { this = TCfgScope(result) }
|
||||
|
||||
/** Gets the underlying library callable, if any. */
|
||||
LibraryCallable asLibraryCallable() { this = TLibraryCallable(result) }
|
||||
|
||||
/** Gets a textual representation of this callable. */
|
||||
string toString() { result = [this.asCallable().toString(), this.asLibraryCallable()] }
|
||||
string toString() { result = [this.asCfgScope().toString(), this.asLibraryCallable()] }
|
||||
|
||||
/** Gets the location of this callable. */
|
||||
Location getLocation() {
|
||||
result = this.asCallable().getLocation()
|
||||
result = this.asCfgScope().getLocation()
|
||||
or
|
||||
this instanceof TLibraryCallable and
|
||||
result instanceof EmptyLocation
|
||||
@@ -109,18 +113,18 @@ class DataFlowCallable extends TDataFlowCallable {
|
||||
* A call. This includes calls from source code, as well as call(back)s
|
||||
* inside library callables with a flow summary.
|
||||
*/
|
||||
class DataFlowCall extends TDataFlowCall {
|
||||
abstract class DataFlowCall extends TDataFlowCall {
|
||||
/** Gets the enclosing callable. */
|
||||
DataFlowCallable getEnclosingCallable() { none() }
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
|
||||
/** Gets the underlying source code call, if any. */
|
||||
CfgNodes::ExprNodes::CallCfgNode asCall() { none() }
|
||||
abstract CfgNodes::ExprNodes::CallCfgNode asCall();
|
||||
|
||||
/** Gets a textual representation of this call. */
|
||||
string toString() { none() }
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the location of this call. */
|
||||
Location getLocation() { none() }
|
||||
abstract Location getLocation();
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
@@ -159,12 +163,14 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
|
||||
|
||||
override CfgNodes::ExprNodes::CallCfgNode asCall() { none() }
|
||||
|
||||
override string toString() { result = "[summary] call to " + receiver + " in " + c }
|
||||
|
||||
override EmptyLocation getLocation() { any() }
|
||||
}
|
||||
|
||||
private class NormalCall extends DataFlowCall, TNormalCall {
|
||||
class NormalCall extends DataFlowCall, TNormalCall {
|
||||
private CfgNodes::ExprNodes::CallCfgNode c;
|
||||
|
||||
NormalCall() { this = TNormalCall(c) }
|
||||
@@ -188,14 +194,17 @@ private class RelevantCall extends CfgNodes::ExprNodes::CallCfgNode {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate methodCall(RelevantCall call, DataFlow::Node receiver, string method) {
|
||||
method = call.getExpr().(MethodCall).getMethodName() and
|
||||
receiver.asExpr() = call.getReceiver()
|
||||
private predicate methodCall(DataFlowCall call, DataFlow::Node receiver, string method) {
|
||||
call.asCall() =
|
||||
any(RelevantCall rc |
|
||||
method = rc.getExpr().(MethodCall).getMethodName() and
|
||||
receiver.asExpr() = rc.getReceiver()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowsToMethodCallReceiver(
|
||||
RelevantCall call, DataFlow::LocalSourceNode sourceNode, string method
|
||||
DataFlowCall call, DataFlow::LocalSourceNode sourceNode, string method
|
||||
) {
|
||||
exists(DataFlow::Node receiver |
|
||||
methodCall(call, receiver, method) and
|
||||
@@ -204,7 +213,7 @@ private predicate flowsToMethodCallReceiver(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate moduleFlowsToMethodCallReceiver(RelevantCall call, Module m, string method) {
|
||||
private predicate moduleFlowsToMethodCallReceiver(DataFlowCall call, Module m, string method) {
|
||||
flowsToMethodCallReceiver(call, trackModuleAccess(m), method)
|
||||
}
|
||||
|
||||
@@ -308,23 +317,23 @@ predicate isUserDefinedNew(SingletonMethod new) {
|
||||
)
|
||||
}
|
||||
|
||||
private Callable viableSourceCallableNonInit(RelevantCall call) {
|
||||
result = getTargetInstance(call, _)
|
||||
private DataFlowCallable viableSourceCallableNonInit(DataFlowCall call) {
|
||||
result.asCfgScope() = getTargetInstance(call, _)
|
||||
or
|
||||
result = getTargetSingleton(call, _)
|
||||
result.asCfgScope() = getTargetSingleton(call, _)
|
||||
or
|
||||
exists(Module cls, string method |
|
||||
superCall(call, cls, method) and
|
||||
result = lookupMethod(cls.getAnImmediateAncestor(), method)
|
||||
superCall(call.asCall(), cls, method) and
|
||||
result.asCfgScope() = lookupMethod(cls.getAnImmediateAncestor(), method)
|
||||
)
|
||||
}
|
||||
|
||||
private Callable viableSourceCallableInit(RelevantCall call) { result = getInitializeTarget(call) }
|
||||
|
||||
/** Holds if `call` may resolve to the returned source-code method. */
|
||||
private Callable viableSourceCallable(RelevantCall call) {
|
||||
private DataFlowCallable viableSourceCallable(DataFlowCall call) {
|
||||
result = viableSourceCallableNonInit(call) or
|
||||
result = viableSourceCallableInit(call)
|
||||
result.asCfgScope() = viableSourceCallableInit(call.asCall())
|
||||
}
|
||||
|
||||
/** Holds if `call` may resolve to the returned summarized library method. */
|
||||
@@ -364,7 +373,7 @@ private predicate extendCallModule(Module m, Module n) {
|
||||
* sub classes when `exact = false`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private Method lookupMethod(Module m, string name, boolean exact) {
|
||||
private CfgScope lookupMethod(Module m, string name, boolean exact) {
|
||||
result = lookupMethod(m, name) and
|
||||
exact in [false, true]
|
||||
or
|
||||
@@ -405,16 +414,16 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
CfgScope getTarget(RelevantCall call) {
|
||||
result = viableSourceCallableNonInit(call)
|
||||
CfgScope getTarget(DataFlowCall call) {
|
||||
result = viableSourceCallableNonInit(call).asCfgScope()
|
||||
or
|
||||
result = blockCall(call)
|
||||
result = blockCall(call.asCall())
|
||||
}
|
||||
|
||||
/** Gets a viable run-time target for the call `call`. */
|
||||
cached
|
||||
DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
result.asCallable() = viableSourceCallable(call.asCall())
|
||||
result = viableSourceCallable(call)
|
||||
or
|
||||
result = viableLibraryCallable(call)
|
||||
}
|
||||
@@ -477,9 +486,6 @@ private module Cached {
|
||||
|
||||
import Cached
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isNotSelf(DataFlow::Node n) { not n instanceof SelfParameterNodeImpl }
|
||||
|
||||
private module TrackModuleInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Module;
|
||||
|
||||
@@ -511,7 +517,7 @@ private predicate hasUserDefinedNew(Module m) {
|
||||
pragma[nomagic]
|
||||
private predicate isStandardNewCall(RelevantCall new, Module m, boolean exact) {
|
||||
exists(DataFlow::LocalSourceNode sourceNode |
|
||||
flowsToMethodCallReceiver(new, sourceNode, "new") and
|
||||
flowsToMethodCallReceiver(TNormalCall(new), sourceNode, "new") and
|
||||
// `m` should not have a user-defined `self.new` method
|
||||
not hasUserDefinedNew(m)
|
||||
|
|
||||
@@ -657,10 +663,7 @@ private module TrackInstanceInput implements CallGraphConstruction::InputSig {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
|
||||
// We exclude steps into `self` parameters. For those, we instead rely on the type of
|
||||
// the enclosing module
|
||||
smallStepNoCall(nodeFrom, nodeTo, summary) and
|
||||
isNotSelf(nodeTo)
|
||||
smallStepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
// We exclude steps into type checked variables. For those, we instead rely on the
|
||||
// type being checked against
|
||||
@@ -692,7 +695,7 @@ private DataFlow::Node trackInstance(Module tp, boolean exact) {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Method lookupInstanceMethodCall(RelevantCall call, string method, boolean exact) {
|
||||
private CfgScope lookupInstanceMethodCall(DataFlowCall call, string method, boolean exact) {
|
||||
exists(Module tp, DataFlow::Node receiver |
|
||||
methodCall(call, pragma[only_bind_into](receiver), pragma[only_bind_into](method)) and
|
||||
receiver = trackInstance(tp, exact) and
|
||||
@@ -707,24 +710,25 @@ private predicate isToplevelMethodInFile(Method m, File f) {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private CfgScope getTargetInstance(RelevantCall call, string method) {
|
||||
private CfgScope getTargetInstance(DataFlowCall call, string method) {
|
||||
exists(boolean exact |
|
||||
result = lookupInstanceMethodCall(call, method, exact) and
|
||||
(
|
||||
if result.(Method).isPrivate()
|
||||
then
|
||||
call.getReceiver().getExpr() instanceof SelfVariableAccess and
|
||||
call.asCall().getReceiver().getExpr() instanceof SelfVariableAccess and
|
||||
// For now, we restrict the scope of top-level declarations to their file.
|
||||
// This may remove some plausible targets, but also removes a lot of
|
||||
// implausible targets
|
||||
(
|
||||
isToplevelMethodInFile(result, call.getFile()) or
|
||||
isToplevelMethodInFile(result, call.asCall().getFile()) or
|
||||
not isToplevelMethodInFile(result, _)
|
||||
)
|
||||
else any()
|
||||
) and
|
||||
if result.(Method).isProtected()
|
||||
then result = lookupMethod(call.getExpr().getEnclosingModule().getModule(), method, exact)
|
||||
then
|
||||
result = lookupMethod(call.asCall().getExpr().getEnclosingModule().getModule(), method, exact)
|
||||
else any()
|
||||
)
|
||||
}
|
||||
@@ -1006,7 +1010,7 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
|
||||
|
||||
/** Holds if a `self` access may be the receiver of `call` directly inside module `m`. */
|
||||
pragma[nomagic]
|
||||
private predicate selfInModuleFlowsToMethodCallReceiver(RelevantCall call, Module m, string method) {
|
||||
private predicate selfInModuleFlowsToMethodCallReceiver(DataFlowCall call, Module m, string method) {
|
||||
exists(SelfLocalSourceNode self |
|
||||
flowsToMethodCallReceiver(call, self, method) and
|
||||
selfInModule(self.getVariable(), m)
|
||||
@@ -1019,7 +1023,7 @@ private predicate selfInModuleFlowsToMethodCallReceiver(RelevantCall call, Modul
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
|
||||
RelevantCall call, Module m, string method
|
||||
DataFlowCall call, Module m, string method
|
||||
) {
|
||||
exists(SelfLocalSourceNode self, MethodBase caller |
|
||||
flowsToMethodCallReceiver(call, self, method) and
|
||||
@@ -1029,7 +1033,7 @@ private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private CfgScope getTargetSingleton(RelevantCall call, string method) {
|
||||
private CfgScope getTargetSingleton(DataFlowCall call, string method) {
|
||||
// singleton method defined on an instance, e.g.
|
||||
// ```rb
|
||||
// c = C.new
|
||||
@@ -1089,41 +1093,60 @@ private CfgScope getTargetSingleton(RelevantCall call, string method) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the parameter at position `pos` inside `encl` must flow to the receiver
|
||||
* of `call`, which targets a method named `name`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate paramMustFlowToReceiver(
|
||||
ParameterPosition pos, DataFlowCall call, DataFlowCallable encl, string name
|
||||
) {
|
||||
exists(ParameterNodeImpl p |
|
||||
// `p` is a parameter of `encl`,
|
||||
p.isParameterOf(encl, pos) and
|
||||
// the receiver of `call` references `p`
|
||||
exists(DataFlow::Node receiver |
|
||||
methodCall(pragma[only_bind_into](call), pragma[only_bind_into](receiver), name) and
|
||||
LocalFlow::localMustFlowStep*(p, receiver)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate mayBenefitFromCallContext(
|
||||
DataFlowCall call, ParameterPosition pos, DataFlowCall ctx
|
||||
) {
|
||||
paramMustFlowToReceiver(pos, call, viableCallable(ctx), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallContext(call, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if `ctx` targets the enclosing callable of `call`, the receiver of `call` is a
|
||||
* parameter access, where the corresponding argument of `ctx` is `arg`.
|
||||
* parameter access (at position `ppos`), where the corresponding argument of `ctx`
|
||||
* is `arg`.
|
||||
*
|
||||
* `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`.
|
||||
* `name` is the name of the method being called by `call` and `source` is a
|
||||
* `LocalSourceNode` that flows to `arg`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate argMustFlowToReceiver(
|
||||
RelevantCall ctx, DataFlow::LocalSourceNode source, DataFlow::Node arg, RelevantCall call,
|
||||
string name
|
||||
RelevantCall ctx, DataFlow::LocalSourceNode source, DataFlow::Node arg, ParameterPosition ppos,
|
||||
DataFlowCall call, string name
|
||||
) {
|
||||
exists(
|
||||
ParameterNodeImpl p, SsaDefinitionExtNode paramDef, ParameterPosition ppos,
|
||||
ArgumentPosition apos, Callable encl
|
||||
|
|
||||
// the receiver of `call` references `p`
|
||||
exists(DataFlow::Node receiver |
|
||||
LocalFlow::localFlowSsaParamInput(p, paramDef) and
|
||||
methodCall(pragma[only_bind_into](call), pragma[only_bind_into](receiver),
|
||||
pragma[only_bind_into](name)) and
|
||||
receiver.asExpr() = paramDef.getDefinitionExt().(Ssa::Definition).getARead()
|
||||
) and
|
||||
// `p` is a parameter of `encl`,
|
||||
encl = call.getScope() and
|
||||
p.isParameterOf(TCfgScope(encl), ppos) and
|
||||
// `arg` is the argument for `p` in the call `ctx`
|
||||
exists(ArgumentPosition apos, DataFlowCallable encl |
|
||||
paramMustFlowToReceiver(ppos, call, encl, name) and
|
||||
parameterMatch(ppos, apos) and
|
||||
source.flowsTo(arg)
|
||||
|
|
||||
encl = viableSourceCallableNonInit(ctx) and
|
||||
encl = viableSourceCallableNonInit(TNormalCall(ctx)) and
|
||||
arg.(ArgumentNode).sourceArgumentOf(ctx, apos)
|
||||
or
|
||||
encl = viableSourceCallableInit(ctx) and
|
||||
encl.asCfgScope() = viableSourceCallableInit(ctx) and
|
||||
if apos.isSelf()
|
||||
then
|
||||
// when we are targeting an initializer, the type of `self` inside the
|
||||
@@ -1131,74 +1154,52 @@ private predicate argMustFlowToReceiver(
|
||||
// of the `new` call
|
||||
arg.asExpr() = ctx
|
||||
else arg.(ArgumentNode).sourceArgumentOf(ctx, apos)
|
||||
or
|
||||
ctx.getAstNode() = encl.asLibraryCallable().getACallSimple() and
|
||||
arg.(ArgumentNode).sourceArgumentOf(ctx, apos)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ctx` targets the enclosing callable of `new`, and
|
||||
* the receiver of `new` is a parameter access, where the corresponding argument
|
||||
* `arg` of `ctx` has type `tp`.
|
||||
*
|
||||
* `new` calls the object creation `new` method.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate mayBenefitFromCallContextInitialize(
|
||||
RelevantCall ctx, RelevantCall new, DataFlow::Node arg, Module tp, string name
|
||||
) {
|
||||
exists(DataFlow::LocalSourceNode source |
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, new, "new") and
|
||||
source = trackModuleAccess(tp) and
|
||||
name = "initialize" and
|
||||
exists(lookupMethod(tp, name))
|
||||
private CfgScope viableImplInCallContextInitialize(RelevantCall call, RelevantCall ctx) {
|
||||
exists(Module m, DataFlow::LocalSourceNode source |
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), _, _, TNormalCall(call), "new") and
|
||||
source = trackModuleAccess(m) and
|
||||
result = getInitializeTarget(call) and
|
||||
result = lookupMethod(m, "initialize")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ctx` targets the enclosing callable of `call`, and
|
||||
* the receiver of `call` is a parameter access, where the corresponding argument
|
||||
* `arg` of `ctx` has type `tp`.
|
||||
*
|
||||
* `name` is the name of the method being called by `call`, and `exact` is pertaining
|
||||
* to the type of the argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate mayBenefitFromCallContextInstance(
|
||||
RelevantCall ctx, RelevantCall call, DataFlow::Node arg, Module tp, boolean exact, string name
|
||||
) {
|
||||
exists(DataFlow::LocalSourceNode source |
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, call,
|
||||
private CfgScope viableImplInCallContextInstance(DataFlowCall call, RelevantCall ctx) {
|
||||
exists(Module m, DataFlow::LocalSourceNode source, string name, boolean exact |
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), _, _, pragma[only_bind_into](call),
|
||||
pragma[only_bind_into](name)) and
|
||||
source = trackInstance(tp, exact) and
|
||||
exists(lookupMethod(tp, pragma[only_bind_into](name)))
|
||||
source = trackInstance(m, exact) and
|
||||
result = getTargetInstance(call, pragma[only_bind_into](name)) and
|
||||
result = lookupMethod(m, pragma[only_bind_into](name), exact)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ctx` targets the enclosing callable of `call`, and
|
||||
* the receiver of `call` is a parameter access, where the corresponding argument
|
||||
* `arg` of `ctx` is a module access targeting a module of type `tp`.
|
||||
*
|
||||
* `name` is the name of the method being called by `call`, and `exact` is pertaining
|
||||
* to the type of the argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate mayBenefitFromCallContextSingleton(
|
||||
RelevantCall ctx, RelevantCall call, DataFlow::Node arg, Module tp, boolean exact, string name
|
||||
) {
|
||||
exists(DataFlow::LocalSourceNode source |
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), pragma[only_bind_into](arg), call,
|
||||
pragma[only_bind_into](name)) and
|
||||
exists(lookupSingletonMethod(tp, pragma[only_bind_into](name), exact))
|
||||
private CfgScope viableImplInCallContextSingleton(DataFlowCall call, RelevantCall ctx) {
|
||||
exists(
|
||||
Module m, DataFlow::LocalSourceNode source, DataFlow::Node arg, string name, boolean exact
|
||||
|
|
||||
source = trackModuleAccess(tp) and
|
||||
argMustFlowToReceiver(ctx, pragma[only_bind_into](source), arg, _, pragma[only_bind_into](call),
|
||||
pragma[only_bind_into](name)) and
|
||||
result = getTargetSingleton(call, pragma[only_bind_into](name)) and
|
||||
result = lookupSingletonMethod(m, pragma[only_bind_into](name), exact)
|
||||
|
|
||||
source = trackModuleAccess(m) and
|
||||
exact = true
|
||||
or
|
||||
exists(SelfVariable self | arg.asExpr().getExpr() = self.getAnAccess() |
|
||||
selfInModule(self, tp) and
|
||||
selfInModule(self, m) and
|
||||
exact = true
|
||||
or
|
||||
exists(MethodBase caller |
|
||||
selfInMethod(self, caller, tp) and
|
||||
selfInMethod(self, caller, m) and
|
||||
singletonMethod(caller, _, _) and
|
||||
exact = false
|
||||
)
|
||||
@@ -1206,64 +1207,33 @@ private predicate mayBenefitFromCallContextSingleton(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(DataFlowCall call) {
|
||||
mayBenefitFromCallContextInitialize(_, call.asCall(), _, _, _)
|
||||
or
|
||||
mayBenefitFromCallContextInstance(_, call.asCall(), _, _, _, _)
|
||||
or
|
||||
mayBenefitFromCallContextSingleton(_, call.asCall(), _, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
mayBenefitFromCallContext(call) and
|
||||
(
|
||||
// `ctx` can provide a potentially better type bound
|
||||
exists(RelevantCall call0, Callable res |
|
||||
call0 = call.asCall() and
|
||||
res = result.asCallable() and
|
||||
exists(Module m, string name |
|
||||
mayBenefitFromCallContextInitialize(ctx.asCall(), pragma[only_bind_into](call0), _,
|
||||
pragma[only_bind_into](m), pragma[only_bind_into](name)) and
|
||||
res = getInitializeTarget(call0) and
|
||||
res = lookupMethod(m, name)
|
||||
or
|
||||
exists(boolean exact |
|
||||
mayBenefitFromCallContextInstance(ctx.asCall(), pragma[only_bind_into](call0), _,
|
||||
pragma[only_bind_into](m), pragma[only_bind_into](exact), pragma[only_bind_into](name)) and
|
||||
res = getTargetInstance(call0, name) and
|
||||
res = lookupMethod(m, name, exact)
|
||||
or
|
||||
mayBenefitFromCallContextSingleton(ctx.asCall(), pragma[only_bind_into](call0), _,
|
||||
pragma[only_bind_into](m), pragma[only_bind_into](exact), pragma[only_bind_into](name)) and
|
||||
res = getTargetSingleton(call0, name) and
|
||||
res = lookupSingletonMethod(m, name, exact)
|
||||
)
|
||||
)
|
||||
)
|
||||
// `ctx` can provide a potentially better type bound
|
||||
exists(CfgScope res | res = result.asCfgScope() |
|
||||
res = viableImplInCallContextInitialize(call.asCall(), ctx.asCall())
|
||||
or
|
||||
res = viableImplInCallContextInstance(call, ctx.asCall())
|
||||
or
|
||||
res = viableImplInCallContextSingleton(call, ctx.asCall())
|
||||
)
|
||||
or
|
||||
exists(ParameterPosition pos | mayBenefitFromCallContext(call, pos, ctx) |
|
||||
// `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, DataFlow::Node arg, string name |
|
||||
call0 = call.asCall() and
|
||||
ctx0 = ctx.asCall() and
|
||||
argMustFlowToReceiver(ctx0, _, arg, call0, name) and
|
||||
not mayBenefitFromCallContextInitialize(ctx0, call0, arg, _, _) and
|
||||
not mayBenefitFromCallContextInstance(ctx0, call0, arg, _, _, name) and
|
||||
not mayBenefitFromCallContextSingleton(ctx0, call0, arg, _, _, name) and
|
||||
result.asCallable() = viableSourceCallable(call0)
|
||||
pos.isSelf() and
|
||||
result = viableSourceCallable(call) and
|
||||
not exists(RelevantCall ctx0 | ctx0 = ctx.asCall() |
|
||||
exists(viableImplInCallContextInitialize(call.asCall(), ctx0)) or
|
||||
exists(viableImplInCallContextInstance(call, ctx0)) or
|
||||
exists(viableImplInCallContextSingleton(call, ctx0))
|
||||
)
|
||||
or
|
||||
// library calls should always be able to resolve
|
||||
argMustFlowToReceiver(ctx.asCall(), _, _, call.asCall(), _) and
|
||||
result = viableLibraryCallable(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -235,6 +235,30 @@ module LocalFlow {
|
||||
or
|
||||
nodeTo.(BlockArgumentNode).getParameterNode(true) = nodeFrom
|
||||
}
|
||||
|
||||
predicate localMustFlowStep(Node node1, Node node2) {
|
||||
LocalFlow::localFlowSsaParamInput(node1, node2)
|
||||
or
|
||||
exists(SsaImpl::Definition def |
|
||||
def.(Ssa::WriteDefinition).assigns(node1.asExpr()) and
|
||||
node2.(SsaDefinitionExtNode).getDefinitionExt() = def
|
||||
or
|
||||
def = node1.(SsaDefinitionExtNode).getDefinitionExt() and
|
||||
node2.asExpr() = SsaImpl::getARead(def)
|
||||
)
|
||||
or
|
||||
node1.asExpr() = node2.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs()
|
||||
or
|
||||
node1.asExpr() = node2.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue()
|
||||
or
|
||||
node1 = node2.(BlockArgumentNode).getParameterNode(true)
|
||||
or
|
||||
node1 =
|
||||
unique(FlowSummaryNode n1 |
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(n1.getSummaryNode(),
|
||||
node2.(FlowSummaryNode).getSummaryNode(), true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An argument of a call (including qualifier arguments and block arguments). */
|
||||
@@ -508,11 +532,11 @@ private module Cached {
|
||||
isParameterNode(_, c, any(ParameterPosition p | p.isKeyword(_)))
|
||||
} or
|
||||
TSynthSplatParameterNode(DataFlowCallable c) {
|
||||
exists(c.asCallable()) and // exclude library callables (for now)
|
||||
exists(c.asCfgScope()) and // exclude library callables (for now)
|
||||
isParameterNode(_, c, any(ParameterPosition p | p.isPositional(_)))
|
||||
} or
|
||||
TSynthSplatParameterShiftNode(DataFlowCallable c, int splatPos, int n) {
|
||||
splatPos = unique(int i | splatParameterAt(c.asCallable(), i) and i > 0) and
|
||||
splatPos = unique(int i | splatParameterAt(c.asCfgScope(), i) and i > 0) and
|
||||
n in [0 .. 10]
|
||||
} or
|
||||
TExprPostUpdateNode(CfgNodes::ExprCfgNode n) {
|
||||
@@ -715,7 +739,7 @@ private module Cached {
|
||||
cached
|
||||
predicate exprNodeReturnedFromCached(ExprNode e, Callable c) {
|
||||
exists(ReturnNode r |
|
||||
nodeGetEnclosingCallable(r).asCallable() = c and
|
||||
nodeGetEnclosingCallable(r).asCfgScope() = c and
|
||||
(
|
||||
r.(ExplicitReturnNode).getReturningNode().getReturnedValueNode() = e.asExpr() or
|
||||
r.(ExprReturnNode) = e
|
||||
@@ -881,10 +905,10 @@ private module ParameterNodes {
|
||||
|
||||
abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos);
|
||||
|
||||
final predicate isSourceParameterOf(Callable c, ParameterPosition pos) {
|
||||
final predicate isSourceParameterOf(CfgScope c, ParameterPosition pos) {
|
||||
exists(DataFlowCallable callable |
|
||||
this.isParameterOf(callable, pos) and
|
||||
c = callable.asCallable()
|
||||
c = callable.asCfgScope()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -901,7 +925,7 @@ private module ParameterNodes {
|
||||
override Parameter getParameter() { result = parameter }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
exists(Callable callable | callable = c.asCallable() |
|
||||
exists(Callable callable | callable = c.asCfgScope() |
|
||||
exists(int i | pos.isPositional(i) and callable.getParameter(i) = parameter |
|
||||
parameter instanceof SimpleParameter
|
||||
or
|
||||
@@ -955,7 +979,7 @@ private module ParameterNodes {
|
||||
override Parameter getParameter() { none() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
method = c.asCallable() and pos.isSelf()
|
||||
method = c.asCfgScope() and pos.isSelf()
|
||||
}
|
||||
|
||||
override CfgScope getCfgScope() { result = method }
|
||||
@@ -984,7 +1008,7 @@ private module ParameterNodes {
|
||||
override Parameter getParameter() { none() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
callable = c.asCallable() and pos.isLambdaSelf()
|
||||
callable = c.asCfgScope() and pos.isLambdaSelf()
|
||||
}
|
||||
|
||||
override CfgScope getCfgScope() { result = callable }
|
||||
@@ -1010,7 +1034,7 @@ private module ParameterNodes {
|
||||
}
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
||||
c.asCallable() = method and pos.isBlock()
|
||||
c.asCfgScope() = method and pos.isBlock()
|
||||
}
|
||||
|
||||
CfgNodes::ExprNodes::CallCfgNode getAYieldCall() {
|
||||
@@ -1088,7 +1112,7 @@ private module ParameterNodes {
|
||||
c = callable and pos.isSynthHashSplat()
|
||||
}
|
||||
|
||||
final override CfgScope getCfgScope() { result = callable.asCallable() }
|
||||
final override CfgScope getCfgScope() { result = callable.asCfgScope() }
|
||||
|
||||
final override DataFlowCallable getEnclosingCallable() { result = callable }
|
||||
|
||||
@@ -1144,7 +1168,7 @@ private module ParameterNodes {
|
||||
predicate readInto(ParameterNode p, ContentSet c) {
|
||||
exists(int n |
|
||||
isParameterNode(p, callable, any(ParameterPosition pos | pos.isPositional(n))) and
|
||||
not exists(int i | splatParameterAt(callable.asCallable(), i) and i < n)
|
||||
not exists(int i | splatParameterAt(callable.asCfgScope(), i) and i < n)
|
||||
|
|
||||
// Important: do not include `TSplatContent(_, false)` here, as normal parameter matching is possible
|
||||
c = getSplatContent(n, true)
|
||||
@@ -1161,7 +1185,7 @@ private module ParameterNodes {
|
||||
c = callable and pos.isSynthSplat()
|
||||
}
|
||||
|
||||
final override CfgScope getCfgScope() { result = callable.asCallable() }
|
||||
final override CfgScope getCfgScope() { result = callable.asCfgScope() }
|
||||
|
||||
final override DataFlowCallable getEnclosingCallable() { result = callable }
|
||||
|
||||
@@ -1212,7 +1236,7 @@ private module ParameterNodes {
|
||||
cs = getArrayContent(pos)
|
||||
}
|
||||
|
||||
final override CfgScope getCfgScope() { result = callable.asCallable() }
|
||||
final override CfgScope getCfgScope() { result = callable.asCfgScope() }
|
||||
|
||||
final override DataFlowCallable getEnclosingCallable() { result = callable }
|
||||
|
||||
@@ -1321,11 +1345,11 @@ module ArgumentNodes {
|
||||
}
|
||||
|
||||
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
||||
private SummaryCall call_;
|
||||
private FlowSummaryImpl::Private::SummaryNode receiver;
|
||||
private ArgumentPosition pos_;
|
||||
|
||||
SummaryArgumentNode() {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(receiver, this.getSummaryNode(), pos_)
|
||||
}
|
||||
|
||||
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
|
||||
@@ -1333,7 +1357,7 @@ module ArgumentNodes {
|
||||
}
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
call = call_ and pos = pos_
|
||||
call.(SummaryCall).getReceiver() = receiver and pos = pos_
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1543,7 +1567,7 @@ module ArgumentNodes {
|
||||
import ArgumentNodes
|
||||
|
||||
/** A call to `new`. */
|
||||
private class NewCall extends DataFlowCall {
|
||||
private class NewCall extends NormalCall {
|
||||
NewCall() { this.asCall().getExpr().(MethodCall).getMethodName() = "new" }
|
||||
}
|
||||
|
||||
@@ -1856,7 +1880,7 @@ predicate clearsContent(Node n, ContentSet c) {
|
||||
ParameterPosition keywordPos, ConstantValue::ConstantSymbolValue cv, string name
|
||||
|
|
||||
n = TNormalParameterNode(hashSplatParam) and
|
||||
callable.asCallable() = hashSplatParam.getCallable() and
|
||||
callable.asCfgScope() = hashSplatParam.getCallable() and
|
||||
keywordParam.isParameterOf(callable, keywordPos) and
|
||||
keywordPos.isKeyword(name) and
|
||||
c.isKnownOrUnknownElement(TKnownElementContent(cv)) and
|
||||
@@ -2042,7 +2066,7 @@ private predicate lambdaCreationExpr(CfgNodes::ExprCfgNode creation, LambdaCallK
|
||||
|
||||
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
|
||||
lambdaCreationExpr(creation.asExpr(), kind, c.asCallable())
|
||||
lambdaCreationExpr(creation.asExpr(), kind, c.asCfgScope())
|
||||
}
|
||||
|
||||
/** Holds if `call` is a call to `lambda`, `proc`, or `Proc.new` */
|
||||
|
||||
@@ -77,7 +77,7 @@ class Node extends TNode {
|
||||
or
|
||||
exists(DataFlowCallable c |
|
||||
lambdaCreation(this, _, c) and
|
||||
result.asCallableAstNode() = c.asCallable()
|
||||
result.asCallableAstNode() = c.asCfgScope()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ private predicate callStepNoInitialize(
|
||||
) {
|
||||
exists(DataFlowDispatch::ParameterPosition pos |
|
||||
argumentPositionMatch(call, arg, pos) and
|
||||
p.isSourceParameterOf(DataFlowDispatch::getTarget(call), pos)
|
||||
p.isSourceParameterOf(DataFlowDispatch::getTarget(DataFlowDispatch::TNormalCall(call)), pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ private predicate viableParam(
|
||||
DataFlowDispatch::ParameterPosition ppos
|
||||
) {
|
||||
exists(Cfg::CfgScope callable |
|
||||
DataFlowDispatch::getTarget(call) = callable or
|
||||
DataFlowDispatch::getTarget(DataFlowDispatch::TNormalCall(call)) = callable or
|
||||
DataFlowDispatch::getInitializeTarget(call) = callable
|
||||
|
|
||||
p.isSourceParameterOf(callable, ppos)
|
||||
@@ -296,7 +296,8 @@ module TypeTrackingInput implements Shared::TypeTrackingInput {
|
||||
exists(ExprNodes::CallCfgNode call |
|
||||
nodeFrom instanceof DataFlowPrivate::ReturnNode and
|
||||
not nodeFrom instanceof DataFlowPrivate::InitializeReturnNode and
|
||||
nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() = DataFlowDispatch::getTarget(call) and
|
||||
nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() =
|
||||
DataFlowDispatch::getTarget(DataFlowDispatch::TNormalCall(call)) and
|
||||
// deliberately do not include `getInitializeTarget`, since calls to `new` should not
|
||||
// get the return value from `initialize`. Any fields being set in the initializer
|
||||
// will reach all reads via `callStep` and `localFieldStep`.
|
||||
|
||||
@@ -219,11 +219,14 @@ subpaths
|
||||
| call_sensitivity.rb:200:8:200:8 | x | call_sensitivity.rb:199:16:199:23 | call to taint | call_sensitivity.rb:200:8:200:8 | x | $@ | call_sensitivity.rb:199:16:199:23 | call to taint | call to taint |
|
||||
| call_sensitivity.rb:204:8:204:8 | x | call_sensitivity.rb:199:16:199:23 | call to taint | call_sensitivity.rb:204:8:204:8 | x | $@ | call_sensitivity.rb:199:16:199:23 | call to taint | call to taint |
|
||||
mayBenefitFromCallContext
|
||||
| call_sensitivity.rb:6:5:6:21 | call to puts |
|
||||
| call_sensitivity.rb:22:5:22:18 | call to call |
|
||||
| call_sensitivity.rb:51:5:51:10 | call to sink |
|
||||
| call_sensitivity.rb:55:5:55:13 | call to method1 |
|
||||
| call_sensitivity.rb:59:5:59:18 | call to method2 |
|
||||
| call_sensitivity.rb:63:5:63:16 | call to method1 |
|
||||
| call_sensitivity.rb:67:5:67:25 | call to method3 |
|
||||
| call_sensitivity.rb:71:5:71:10 | call to sink |
|
||||
| call_sensitivity.rb:81:5:81:18 | call to method1 |
|
||||
| call_sensitivity.rb:89:5:89:23 | call to singleton_method1 |
|
||||
| call_sensitivity.rb:93:5:93:28 | call to singleton_method2 |
|
||||
@@ -232,11 +235,15 @@ mayBenefitFromCallContext
|
||||
| call_sensitivity.rb:105:5:105:10 | call to sink |
|
||||
| call_sensitivity.rb:106:5:106:13 | call to method1 |
|
||||
| call_sensitivity.rb:110:5:110:9 | call to new |
|
||||
| call_sensitivity.rb:129:5:129:25 | call to puts |
|
||||
| call_sensitivity.rb:133:5:133:25 | call to puts |
|
||||
| call_sensitivity.rb:137:5:137:18 | call to method2 |
|
||||
| call_sensitivity.rb:141:5:141:25 | call to method3 |
|
||||
| call_sensitivity.rb:149:5:149:28 | call to singleton_method2 |
|
||||
| call_sensitivity.rb:153:5:153:35 | call to singleton_method3 |
|
||||
| call_sensitivity.rb:157:5:157:25 | call to puts |
|
||||
| call_sensitivity.rb:175:3:175:12 | call to new |
|
||||
| call_sensitivity.rb:183:5:183:25 | call to puts |
|
||||
| call_sensitivity.rb:194:3:196:5 | call to invoke_block1 |
|
||||
viableImplInCallContext
|
||||
| call_sensitivity.rb:51:5:51:10 | call to sink | call_sensitivity.rb:55:5:55:13 | call to method1 | call_sensitivity.rb:5:1:7:3 | sink |
|
||||
|
||||
Reference in New Issue
Block a user