Merge pull request #15468 from hvitved/ruby/ctx-sensitivity-rework

This commit is contained in:
Tom Hvitved
2024-01-30 20:27:43 +01:00
committed by GitHub
7 changed files with 187 additions and 185 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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)
)
}

View File

@@ -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` */

View File

@@ -77,7 +77,7 @@ class Node extends TNode {
or
exists(DataFlowCallable c |
lambdaCreation(this, _, c) and
result.asCallableAstNode() = c.asCallable()
result.asCallableAstNode() = c.asCfgScope()
)
}

View File

@@ -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`.

View File

@@ -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 |