diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll index b70b43a69c2..47b7d0f049b 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatchPointsTo.qll @@ -588,6 +588,54 @@ class SummaryCall extends DataFlowCall, TSummaryCall { override Location getLocation() { result = c.getLocation() } } +/** A parameter for a library callable with a flow summary. */ +class SummaryParameterNode extends ParameterNode, TSummaryParameterNode { + private FlowSummaryImpl::Public::SummarizedCallable sc; + private int pos; + + SummaryParameterNode() { this = TSummaryParameterNode(sc, pos) } + + override predicate isParameterOf(DataFlowCallable c, int i) { sc = c and i = pos } + + override DataFlowCallable getEnclosingCallable() { result = sc } +} + +/** A data-flow node used to model flow summaries. */ +private class SummaryNode extends Node, TSummaryNode { + private FlowSummaryImpl::Public::SummarizedCallable c; + private FlowSummaryImpl::Private::SummaryNodeState state; + + SummaryNode() { this = TSummaryNode(c, state) } + + override DataFlowCallable getEnclosingCallable() { result = c } + + override string toString() { result = "[summary] " + state + " in " + c } +} + +private class SummaryReturnNode extends SummaryNode, ReturnNode { + private ReturnKind rk; + + SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this, rk) } + + override ReturnKind getKind() { result = rk } +} + +private class SummaryArgumentNode extends SummaryNode, ArgumentNode { + SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) } + + override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { + FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos) + } +} + +private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { + private Node pre; + + SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) } + + override Node getPreUpdateNode() { result = pre } +} + /** Gets a viable run-time target for the call `call`. */ DataFlowCallable viableCallable(DataFlowSourceCall call) { result = call.getCallable() } @@ -603,26 +651,53 @@ class ReturnKind extends TReturnKind { } /** A data flow node that represents a value returned by a callable. */ -class ReturnNode extends CfgNode { - Return ret; - - // See `TaintTrackingImplementation::returnFlowStep` - ReturnNode() { node = ret.getValue().getAFlowNode() } - +abstract class ReturnNode extends Node { /** Gets the kind of this return node. */ ReturnKind getKind() { any() } } -/** A data flow node that represents the output of a call. */ -class OutNode extends CfgNode { - OutNode() { node instanceof CallNode } +/** A data flow node that represents a value returned by a callable. */ +class ReturnSourceNode extends ReturnNode, CfgNode { + Return ret; + + // See `TaintTrackingImplementation::returnFlowStep` + ReturnSourceNode() { node = ret.getValue().getAFlowNode() } + + override ReturnKind getKind() { any() } +} + +/** A data-flow node that represents the output of a call. */ +abstract class OutNode extends Node { + /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ + abstract DataFlowCall getCall(ReturnKind kind); +} + +private module OutNodes { + /** + * A data-flow node that reads a value returned directly by a callable. + */ + class ExprOutNode extends OutNode, ExprNode { + private DataFlowCall call; + + ExprOutNode() { call.(DataFlowSourceCall).getNode().getNode() = this.asExpr() } + + override DataFlowCall getCall(ReturnKind kind) { + result = call and + kind = kind + } + } + + private class SummaryOutNode extends SummaryNode, OutNode { + SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this, _) } + + override DataFlowCall getCall(ReturnKind kind) { + FlowSummaryImpl::Private::summaryOutNode(result, this, kind) + } + } } /** * Gets a node that can read the value returned from `call` with return kind * `kind`. */ -OutNode getAnOutNode(DataFlowSourceCall call, ReturnKind kind) { - call.getNode() = result.getNode() and - kind = TNormalReturnKind() -} +OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 472e7635ad5..d416b6cacc3 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -317,11 +317,22 @@ class SourceParameterNode extends ParameterNode, CfgNode { SourceParameterNode parameterNode(Parameter p) { result.getParameter() = p } /** A data flow node that represents a call argument. */ -class ArgumentNode extends Node { - ArgumentNode() { this = any(DataFlowSourceCall c).getArg(_) } - +abstract class ArgumentNode extends Node { /** Holds if this argument occurs at the given position in the given call. */ - predicate argumentOf(DataFlowSourceCall call, int pos) { this = call.getArg(pos) } + abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos); +} + +/** A data flow node that represents a call argument. */ +class ArgumentSourceNode extends ArgumentNode { + ArgumentSourceNode() { this = any(DataFlowSourceCall c).getArg(_) } + + final override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { + this.sourceArgumentOf(call, pos) + } + + predicate sourceArgumentOf(DataFlowSourceCall call, ArgumentPosition pos) { + this = call.getArg(pos) + } /** Gets the call in which this node is an argument. */ final DataFlowSourceCall getCall() { this.argumentOf(result, _) } diff --git a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll index a56653dc3d7..7fee23085f7 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll @@ -35,7 +35,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { override predicate isSink(DataFlow::Node node) { exists(node.getLocation().getFile().getRelativePath()) and not any(CallNode c).getArg(_) = node.asCfgNode() and - not node instanceof DataFlow::ArgumentNode and + not node instanceof DataFlow::ArgumentSourceNode and not node.asCfgNode().(NameNode).getId().matches("SINK%") and not exists(DataFlow::Node succ | DataFlow::localFlowStep(node, succ)) } diff --git a/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll b/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll index 311b87a61c2..340ec7feee3 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll @@ -42,7 +42,7 @@ abstract class RoutingTest extends InlineExpectationsTest { } pragma[inline] - private string fromFunc(DataFlow::ArgumentNode fromNode) { + private string fromFunc(DataFlow::ArgumentSourceNode fromNode) { result = fromNode.getCall().getNode().(CallNode).getFunction().getNode().(Name).getId() } diff --git a/python/ql/test/experimental/dataflow/callGraphConfig.qll b/python/ql/test/experimental/dataflow/callGraphConfig.qll index 897f38cf76d..6bc196dff1d 100644 --- a/python/ql/test/experimental/dataflow/callGraphConfig.qll +++ b/python/ql/test/experimental/dataflow/callGraphConfig.qll @@ -11,7 +11,7 @@ class CallGraphConfig extends DataFlow::Configuration { override predicate isSource(DataFlow::Node node) { node instanceof DataFlowPrivate::ReturnNode or - node instanceof DataFlow::ArgumentNode + node instanceof DataFlow::ArgumentSourceNode } override predicate isSink(DataFlow::Node node) { diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index a5477d5667e..b72e24d96d4 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -1,8 +1,8 @@ -| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() | | classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | ControlFlowNode for self | | classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() | | classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() | | classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() | +| classes.py:559:16:559:17 | ControlFlowNode for Str | classes.py:565:5:565:22 | ControlFlowNode for Subscript | | classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | ControlFlowNode for self | | classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | ControlFlowNode for key | | classes.py:581:5:581:16 | ControlFlowNode for with_setitem | classes.py:570:21:570:24 | ControlFlowNode for self | @@ -11,29 +11,68 @@ | classes.py:595:9:595:20 | ControlFlowNode for with_delitem | classes.py:586:21:586:24 | ControlFlowNode for self | | classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:586:27:586:29 | ControlFlowNode for key | | classes.py:618:16:618:28 | ControlFlowNode for Attribute() | classes.py:618:16:618:28 | ControlFlowNode for Attribute() | +| classes.py:659:15:659:18 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | +| classes.py:661:16:661:19 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | | classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:657:17:657:20 | ControlFlowNode for self | +| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | | classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:657:23:657:27 | ControlFlowNode for other | +| classes.py:674:15:674:18 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | +| classes.py:676:16:676:19 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | | classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:672:17:672:20 | ControlFlowNode for self | +| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | | classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:672:23:672:27 | ControlFlowNode for other | +| classes.py:689:15:689:18 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | +| classes.py:691:16:691:19 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | | classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:687:17:687:20 | ControlFlowNode for self | +| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | | classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:687:23:687:27 | ControlFlowNode for other | +| classes.py:704:15:704:18 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | +| classes.py:706:16:706:19 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | | classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:702:20:702:23 | ControlFlowNode for self | +| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | | classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:702:26:702:30 | ControlFlowNode for other | +| classes.py:719:15:719:18 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | +| classes.py:721:16:721:19 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | | classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:717:21:717:24 | ControlFlowNode for self | +| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | | classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:717:27:717:31 | ControlFlowNode for other | +| classes.py:734:15:734:18 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | +| classes.py:736:16:736:19 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | | classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:732:22:732:25 | ControlFlowNode for self | +| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | | classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:732:28:732:32 | ControlFlowNode for other | +| classes.py:749:15:749:18 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | +| classes.py:751:16:751:19 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | | classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:747:17:747:20 | ControlFlowNode for self | +| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | | classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:747:23:747:27 | ControlFlowNode for other | +| classes.py:779:15:779:18 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | +| classes.py:781:16:781:19 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | | classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:777:17:777:20 | ControlFlowNode for self | +| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | | classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:777:23:777:27 | ControlFlowNode for other | +| classes.py:800:15:800:18 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | +| classes.py:802:16:802:19 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | | classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:798:20:798:23 | ControlFlowNode for self | +| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | | classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:798:26:798:30 | ControlFlowNode for other | +| classes.py:815:15:815:18 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | +| classes.py:817:16:817:19 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | | classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:813:20:813:23 | ControlFlowNode for self | +| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | | classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:813:26:813:30 | ControlFlowNode for other | +| classes.py:830:15:830:18 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | +| classes.py:832:16:832:19 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | | classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:828:17:828:20 | ControlFlowNode for self | +| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | | classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:828:23:828:27 | ControlFlowNode for other | +| classes.py:845:15:845:18 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | +| classes.py:847:16:847:19 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | | classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:843:17:843:20 | ControlFlowNode for self | +| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | | classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:843:23:843:27 | ControlFlowNode for other | +| classes.py:860:15:860:18 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | +| classes.py:862:16:862:19 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | | classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:858:16:858:19 | ControlFlowNode for self | +| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | | classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:858:22:858:26 | ControlFlowNode for other | diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql index e47ddc9429e..002678dfd0c 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql @@ -11,7 +11,7 @@ class CallGraphConfig extends DataFlow::Configuration { node instanceof DataFlowPrivate::ReturnNode or // These sources should allow for the non-standard call syntax - node instanceof DataFlow::ArgumentNode + node instanceof DataFlow::ArgumentSourceNode } override predicate isSink(DataFlow::Node node) {