diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index 958bca246cb..895ef74b41e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -38,6 +38,7 @@ private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl private import FlowSummaryImplSpecific as FlowSummaryImplSpecific private import semmle.python.internal.CachedStages +private import semmle.python.dataflow.new.internal.TypeTracker::CallGraphConstruction as CallGraphConstruction newtype TParameterPosition = /** Used for `self` in methods, and `cls` in classmethods. */ @@ -464,189 +465,105 @@ private predicate ignoreForCallGraph(File f) { f.getAbsolutePath().matches("%/site-packages/sympy/%") } -bindingset[n] -pragma[inline_late] -private predicate includeInCallGraph(TypeTrackingNode n) { - not ignoreForCallGraph(n.getLocation().getFile()) -} +private module TrackFunctionInput implements CallGraphConstruction::Simple::InputSig { + class State = Function; -pragma[nomagic] -private predicate stepCallProj(TypeTrackingNode nodeFrom, StepSummary summary) { - StepSummary::stepCall(nodeFrom, _, summary) -} - -bindingset[t, summary] -pragma[inline_late] -private TypeTracker append(TypeTracker t, StepSummary summary) { result = t.append(summary) } - -/** - * Gets a reference to the function `func`. - */ -private TypeTrackingNode functionTracker(TypeTracker t, Function func) { - includeInCallGraph(result) and - ( - t.start() and - ( - result.asExpr() = func.getDefinition() - or - // when a function is decorated, it's the result of the (last) decorator call that - // is used - result.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall() - ) + predicate start(Node start, Function func) { + start.asExpr() = func.getDefinition() or - ( - exists(TypeTracker t2 | t = t2.stepNoCall(functionTracker(t2, func), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(functionTrackerCall(t, func, summary), result, summary) - ) - ) - ) -} + // when a function is decorated, it's the result of the (last) decorator call that + // is used + start.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall() + } -pragma[nomagic] -private TypeTrackingNode functionTrackerCall(TypeTracker t, Function func, StepSummary summary) { - exists(TypeTracker t2 | - // non-linear recursion - result = functionTracker(t2, func) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + predicate filter(Node n) { ignoreForCallGraph(n.getLocation().getFile()) } } /** * Gets a reference to the function `func`. */ -Node functionTracker(Function func) { functionTracker(TypeTracker::end(), func).flowsTo(result) } +Node functionTracker(Function func) { + CallGraphConstruction::Simple::Make::track(func) + .(LocalSourceNode) + .flowsTo(result) +} -/** - * Gets a reference to the class `cls`. - */ -private TypeTrackingNode classTracker(TypeTracker t, Class cls) { - includeInCallGraph(result) and - ( - t.start() and - ( - result.asExpr() = cls.getParent() - or - // when a class is decorated, it's the result of the (last) decorator call that - // is used - result.asExpr() = cls.getParent().getADecoratorCall() - or - // `type(obj)`, where obj is an instance of this class - result = getTypeCall() and - result.(CallCfgNode).getArg(0) = classInstanceTracker(cls) - ) +private module TrackClassInput implements CallGraphConstruction::Simple::InputSig { + class State = Class; + + predicate start(Node start, Class cls) { + start.asExpr() = cls.getParent() or - not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) and - ( - exists(TypeTracker t2 | t = t2.stepNoCall(classTracker(t2, cls), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(classTrackerCall(t, cls, summary), result, summary) - ) - ) - ) -} + // when a class is decorated, it's the result of the (last) decorator call that + // is used + start.asExpr() = cls.getParent().getADecoratorCall() + or + // `type(obj)`, where obj is an instance of this class + start = getTypeCall() and + start.(CallCfgNode).getArg(0) = classInstanceTracker(cls) + } -pragma[nomagic] -private TypeTrackingNode classTrackerCall(TypeTracker t, Class cls, StepSummary summary) { - exists(TypeTracker t2 | - // non-linear recursion - result = classTracker(t2, cls) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + predicate filter(Node n) { + ignoreForCallGraph(n.getLocation().getFile()) + or + n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) + } } /** * Gets a reference to the class `cls`. */ -Node classTracker(Class cls) { classTracker(TypeTracker::end(), cls).flowsTo(result) } +Node classTracker(Class cls) { + CallGraphConstruction::Simple::Make::track(cls).(LocalSourceNode).flowsTo(result) +} -/** - * Gets a reference to an instance of the class `cls`. - */ -private TypeTrackingNode classInstanceTracker(TypeTracker t, Class cls) { - includeInCallGraph(result) and - ( - t.start() and - resolveClassCall(result.(CallCfgNode).asCfgNode(), cls) +private module TrackClassInstanceInput implements CallGraphConstruction::Simple::InputSig { + class State = Class; + + predicate start(Node start, Class cls) { + resolveClassCall(start.(CallCfgNode).asCfgNode(), cls) or // result of `super().__new__` as used in a `__new__` method implementation - t.start() and exists(Class classUsedInSuper | - fromSuperNewCall(result.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and + fromSuperNewCall(start.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and classUsedInSuper = getADirectSuperclass*(cls) ) - or - not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) and - ( - exists(TypeTracker t2 | t = t2.stepNoCall(classInstanceTracker(t2, cls), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(classInstanceTrackerCall(t, cls, summary), result, summary) - ) - ) - ) -} + } -pragma[nomagic] -private TypeTrackingNode classInstanceTrackerCall(TypeTracker t, Class cls, StepSummary summary) { - exists(TypeTracker t2 | - // non-linear recursion - result = classInstanceTracker(t2, cls) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + predicate filter(Node n) { + ignoreForCallGraph(n.getLocation().getFile()) + or + n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) + } } /** * Gets a reference to an instance of the class `cls`. */ Node classInstanceTracker(Class cls) { - classInstanceTracker(TypeTracker::end(), cls).flowsTo(result) + CallGraphConstruction::Simple::Make::track(cls) + .(LocalSourceNode) + .flowsTo(result) } -/** - * Gets a reference to the `self` argument of a method on class `classWithMethod`. - * The method cannot be a `staticmethod` or `classmethod`. - */ -private TypeTrackingNode selfTracker(TypeTracker t, Class classWithMethod) { - includeInCallGraph(result) and - ( - t.start() and +private module TrackSelfInput implements CallGraphConstruction::Simple::InputSig { + class State = Class; + + predicate start(Node start, Class classWithMethod) { exists(Function func | func = classWithMethod.getAMethod() and not isStaticmethod(func) and not isClassmethod(func) | - result.asExpr() = func.getArg(0) + start.asExpr() = func.getArg(0) ) - or - not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) and - ( - exists(TypeTracker t2 | t = t2.stepNoCall(selfTracker(t2, classWithMethod), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(selfTrackerCall(t, classWithMethod, summary), result, summary) - ) - ) - ) -} + } -pragma[nomagic] -private TypeTrackingNode selfTrackerCall(TypeTracker t, Class classWithMethod, StepSummary summary) { - exists(TypeTracker t2 | - // non-linear recursion - result = selfTracker(t2, classWithMethod) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + predicate filter(Node n) { + ignoreForCallGraph(n.getLocation().getFile()) + or + n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) + } } /** @@ -654,53 +571,32 @@ private TypeTrackingNode selfTrackerCall(TypeTracker t, Class classWithMethod, S * The method cannot be a `staticmethod` or `classmethod`. */ Node selfTracker(Class classWithMethod) { - selfTracker(TypeTracker::end(), classWithMethod).flowsTo(result) + CallGraphConstruction::Simple::Make::track(classWithMethod) + .(LocalSourceNode) + .flowsTo(result) } -/** - * Gets a reference to the enclosing class `classWithMethod` from within one of its - * methods, either through the `cls` argument from a `classmethod` or from `type(self)` - * from a normal method. - */ -private TypeTrackingNode clsArgumentTracker(TypeTracker t, Class classWithMethod) { - includeInCallGraph(result) and - ( - t.start() and - ( - exists(Function func | - func = classWithMethod.getAMethod() and - isClassmethod(func) - | - result.asExpr() = func.getArg(0) - ) - or - // type(self) - result = getTypeCall() and - result.(CallCfgNode).getArg(0) = selfTracker(classWithMethod) +private module TrackClsArgumentInput implements CallGraphConstruction::Simple::InputSig { + class State = Class; + + predicate start(Node start, Class classWithMethod) { + exists(Function func | + func = classWithMethod.getAMethod() and + isClassmethod(func) + | + start.asExpr() = func.getArg(0) ) or - not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) and - ( - exists(TypeTracker t2 | t = t2.stepNoCall(clsArgumentTracker(t2, classWithMethod), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(clsArgumentTrackerCall(t, classWithMethod, summary), result, summary) - ) - ) - ) -} + // type(self) + start = getTypeCall() and + start.(CallCfgNode).getArg(0) = selfTracker(classWithMethod) + } -pragma[nomagic] -private TypeTrackingNode clsArgumentTrackerCall( - TypeTracker t, Class classWithMethod, StepSummary summary -) { - exists(TypeTracker t2 | - // non-linear recursion - result = clsArgumentTracker(t2, classWithMethod) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + predicate filter(Node n) { + ignoreForCallGraph(n.getLocation().getFile()) + or + n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) + } } /** @@ -709,46 +605,28 @@ private TypeTrackingNode clsArgumentTrackerCall( * from a normal method. */ Node clsArgumentTracker(Class classWithMethod) { - clsArgumentTracker(TypeTracker::end(), classWithMethod).flowsTo(result) + CallGraphConstruction::Simple::Make::track(classWithMethod) + .(LocalSourceNode) + .flowsTo(result) } -/** - * Gets a reference to the result of calling `super` without any argument, where the - * call happened in the method `func` (either a method or a classmethod). - */ -private TypeTrackingNode superCallNoArgumentTracker(TypeTracker t, Function func) { - includeInCallGraph(result) and - ( - t.start() and +private module TrackSuperCallNoArgumentInput implements CallGraphConstruction::Simple::InputSig { + class State = Function; + + predicate start(Node start, Function func) { not isStaticmethod(func) and - exists(CallCfgNode call | result = call | + exists(CallCfgNode call | start = call | call = getSuperCall() and not exists(call.getArg(_)) and call.getScope() = func ) - or - not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) and - ( - exists(TypeTracker t2 | t = t2.stepNoCall(superCallNoArgumentTracker(t2, func), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(superCallNoArgumentTrackerCall(t, func, summary), result, summary) - ) - ) - ) -} + } -pragma[nomagic] -private TypeTrackingNode superCallNoArgumentTrackerCall( - TypeTracker t, Function func, StepSummary summary -) { - exists(TypeTracker t2 | - // non-linear recursion - result = functionTracker(t2, func) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + predicate filter(Node n) { + ignoreForCallGraph(n.getLocation().getFile()) + or + n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) + } } /** @@ -756,45 +634,30 @@ private TypeTrackingNode superCallNoArgumentTrackerCall( * call happened in the method `func` (either a method or a classmethod). */ Node superCallNoArgumentTracker(Function func) { - superCallNoArgumentTracker(TypeTracker::end(), func).flowsTo(result) + CallGraphConstruction::Simple::Make::track(func) + .(LocalSourceNode) + .flowsTo(result) } -/** - * Gets a reference to the result of calling `super` with 2 arguments, where the - * first is a reference to the class `cls`, and the second argument is `obj`. - */ -private TypeTrackingNode superCallTwoArgumentTracker(TypeTracker t, Class cls, Node obj) { - includeInCallGraph(result) and - ( - t.start() and - exists(CallCfgNode call | result = call | - call = getSuperCall() and - call.getArg(0) = classTracker(cls) and - call.getArg(1) = obj - ) +private module TrackSuperCallTwoArgumentInput implements CallGraphConstruction::Simple::InputSig { + additional predicate superCall(CallCfgNode call, Class cls, Node obj) { + call = getSuperCall() and + call.getArg(0) = classTracker(cls) and + call.getArg(1) = obj + } + + class State = CallCfgNode; + + predicate start(Node start, CallCfgNode call) { + superCall(call, _, _) and + start = call + } + + predicate filter(Node n) { + ignoreForCallGraph(n.getLocation().getFile()) or - not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) and - ( - exists(TypeTracker t2 | t = t2.stepNoCall(superCallTwoArgumentTracker(t2, cls, obj), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(superCallTwoArgumentTrackerCall(t, cls, obj, summary), result, summary) - ) - ) - ) -} - -pragma[nomagic] -private TypeTrackingNode superCallTwoArgumentTrackerCall( - TypeTracker t, Class cls, Node obj, StepSummary summary -) { - exists(TypeTracker t2 | - // non-linear recursion - result = superCallTwoArgumentTracker(t2, cls, obj) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) + } } /** @@ -802,7 +665,12 @@ private TypeTrackingNode superCallTwoArgumentTrackerCall( * first is a reference to the class `cls`, and the second argument is `obj`. */ Node superCallTwoArgumentTracker(Class cls, Node obj) { - superCallTwoArgumentTracker(TypeTracker::end(), cls, obj).flowsTo(result) + exists(CallCfgNode call | + TrackSuperCallTwoArgumentInput::superCall(call, cls, obj) and + CallGraphConstruction::Simple::Make::track(call) + .(LocalSourceNode) + .flowsTo(result) + ) } // ============================================================================= @@ -946,35 +814,30 @@ Function findFunctionAccordingToMroKnownStartingClass(Class startingClass, strin // ============================================================================= // attribute trackers // ============================================================================= -/** Gets a reference to the attribute read `attr` */ -private TypeTrackingNode attrReadTracker(TypeTracker t, AttrRead attr) { - t.start() and - result = attr and - attr.getObject() in [ - classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_), - superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _) - ] - or - exists(TypeTracker t2 | t = t2.stepNoCall(attrReadTracker(t2, attr), result)) - or - exists(StepSummary summary | - // non-linear recursion - StepSummary::stepCall(attrReadTrackerCall(t, attr, summary), result, summary) - ) -} +private module TrackAttrReadInput implements CallGraphConstruction::Simple::InputSig { + class State = AttrRead; -pragma[nomagic] -private TypeTrackingNode attrReadTrackerCall(TypeTracker t, AttrRead attr, StepSummary summary) { - exists(TypeTracker t2 | - // non-linear recursion - result = attrReadTracker(t2, attr) and - stepCallProj(result, summary) and - t = append(t2, summary) - ) + predicate start(Node start, AttrRead attr) { + start = attr and + attr.getObject() in [ + classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_), + superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _) + ] + } + + predicate filter(Node n) { + ignoreForCallGraph(n.getLocation().getFile()) + or + n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf())) + } } /** Gets a reference to the attribute read `attr` */ -Node attrReadTracker(AttrRead attr) { attrReadTracker(TypeTracker::end(), attr).flowsTo(result) } +Node attrReadTracker(AttrRead attr) { + CallGraphConstruction::Simple::Make::track(attr) + .(LocalSourceNode) + .flowsTo(result) +} // ============================================================================= // call and argument resolution