Python: Support overflow positional arguments

Currently ignoring starred arguments
This commit is contained in:
Rasmus Lerchedahl Petersen
2020-09-29 13:14:45 +02:00
parent 8f2ef94b3e
commit 27af9bbae8
5 changed files with 95 additions and 22 deletions

View File

@@ -235,6 +235,61 @@ private Node update(Node node) {
// Global flow
//--------
//
/** Computes routing of arguments to parameters */
module ArgumentPassing {
NameNode getParameter(CallableValue callable, int n) {
// positional parameter
result = callable.getParameter(n)
or
// vararg
exists(Function f |
f = callable.getScope() and
n = f.getPositionalParameterCount() and
result = f.getVararg().getAFlowNode()
)
}
/**
* Gets the argument to `call` then is passed to the `n`th parameter of `callable`.
*/
Node getArg(CallNode call, CallableValue callable, int n) {
call = callable.getACall() and
(
// positional argument
result = TCfgNode(call.getArg(n))
or
// keyword argument
exists(Function f, string argName |
f = callable.getScope() and
f.getArgName(n) = argName and
result = TCfgNode(call.getArgByName(argName))
)
or
// vararg
exists(Function f |
f = callable.getScope() and
f.hasVarArg() and
n = f.getPositionalParameterCount() and
result = TPosOverflowNode(call, callable)
)
)
}
ControlFlowNode getPositionalOverflowArg(CallNode call, CallableValue callable, int n) {
call = callable.getACall() and
exists(Function f, int posCount, int argNr |
f = callable.getScope() and
f.hasVarArg() and
posCount = f.getPositionalParameterCount() and
result = call.getArg(argNr) and
argNr >= posCount and
argNr = posCount + n
)
}
}
import ArgumentPassing
/**
* IPA type for DataFlowCallable.
*
@@ -278,7 +333,7 @@ class DataFlowCallableValue extends DataFlowCallable, TCallableValue {
override Scope getScope() { result = callable.getScope() }
override NameNode getParameter(int n) { result = callable.getParameter(n) }
override NameNode getParameter(int n) { result = getParameter(callable, n) }
override string getName() { result = callable.getName() }
@@ -345,19 +400,6 @@ abstract class DataFlowCall extends TDataFlowCall {
Location getLocation() { result = this.getNode().getLocation() }
}
ControlFlowNode getArg(CallNode call, CallableValue callable, int n) {
call = callable.getACall() and
(
result = call.getArg(n)
or
exists(Function f, string argName |
f = callable.getScope() and
f.getArgName(n) = argName and
result = call.getArgByName(argName)
)
)
}
/** Represents a call to a callable (currently only callable values). */
class CallNodeCall extends DataFlowCall, TCallNode {
CallNode call;
@@ -370,7 +412,7 @@ class CallNodeCall extends DataFlowCall, TCallNode {
override string toString() { result = call.toString() }
override Node getArg(int n) { result = TCfgNode(getArg(call, callable.getCallableValue(), n)) }
override Node getArg(int n) { result = getArg(call, callable.getCallableValue(), n) }
override ControlFlowNode getNode() { result = call }
@@ -394,7 +436,7 @@ class ClassCall extends DataFlowCall, TClassCall {
override string toString() { result = call.toString() }
override Node getArg(int n) {
n > 0 and result = TCfgNode(getArg(call, this.getCallableValue(), n - 1))
n > 0 and result = getArg(call, this.getCallableValue(), n - 1)
or
n = 0 and result = TSyntheticPreUpdateNode(TCfgNode(call))
}
@@ -550,6 +592,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
comprehensionStoreStep(nodeFrom, c, nodeTo)
or
attributeStoreStep(nodeFrom, c, nodeTo)
or
posOverflowStoreStep(nodeFrom, c, nodeTo)
}
/** Data flows from an element of a list to the list. */
@@ -645,6 +689,14 @@ predicate attributeStoreStep(CfgNode nodeFrom, AttributeContent c, PostUpdateNod
)
}
predicate posOverflowStoreStep(CfgNode nodeFrom, TupleElementContent c, Node nodeTo) {
exists(CallNode call, CallableValue callable, int n |
nodeFrom.asCfgNode() = getPositionalOverflowArg(call, callable, n) and
nodeTo = TPosOverflowNode(call, callable) and
c.getIndex() = n
)
}
/**
* Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`.
*/

View File

@@ -23,12 +23,16 @@ newtype TNode =
TEssaNode(EssaVariable var) or
/** A node corresponding to a control flow node. */
TCfgNode(DataFlowCfgNode node) or
/** A synthetic node representing the value of an object before a state change */
/** A synthetic node representing the value of an object before a state change. */
TSyntheticPreUpdateNode(NeedsSyntheticPreUpdateNode post) or
/** A synthetic node representing the value of an object after a state change */
/** A synthetic node representing the value of an object after a state change. */
TSyntheticPostUpdateNode(NeedsSyntheticPostUpdateNode pre) or
/** A node representing a global (module-level) variable in a specific module */
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m and v.escapes() }
/** A node representing a global (module-level) variable in a specific module. */
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m and v.escapes() } or
/** A node representing the overflow positional arguments to a call. */
TPosOverflowNode(CallNode call, CallableValue callable) {
exists(getPositionalOverflowArg(call, callable, _))
}
/**
* An element, viewed as a node in a data flow graph. Either an SSA variable

View File

@@ -9,6 +9,7 @@
| argumentPassing.py:133:46:133:49 | ControlFlowNode for arg1 | argumentPassing.py:118:11:118:13 | ControlFlowNode for foo |
| argumentPassing.py:141:14:141:17 | ControlFlowNode for arg1 | argumentPassing.py:139:15:139:15 | ControlFlowNode for a |
| argumentPassing.py:148:19:148:22 | ControlFlowNode for arg1 | argumentPassing.py:146:15:146:15 | ControlFlowNode for a |
| argumentPassing.py:156:15:156:18 | ControlFlowNode for arg1 | argumentPassing.py:154:19:154:22 | ControlFlowNode for Subscript |
| argumentPassing.py:163:13:163:16 | ControlFlowNode for arg1 | argumentPassing.py:161:15:161:15 | ControlFlowNode for a |
| argumentPassing.py:170:16:170:19 | ControlFlowNode for arg1 | argumentPassing.py:168:15:168:15 | ControlFlowNode for a |
| argumentPassing.py:177:15:177:18 | ControlFlowNode for arg1 | argumentPassing.py:175:15:175:15 | ControlFlowNode for a |

View File

@@ -28,6 +28,8 @@ edges
| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] |
| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] |
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:55:9:55:14 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:9:87:14 | ControlFlowNode for SOURCE |
@@ -49,9 +51,11 @@ edges
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:344:16:344:21 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:365:28:365:33 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:373:30:373:35 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:389:33:389:38 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:442:12:442:17 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:449:28:449:33 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:463:30:463:35 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:482:33:482:38 | ControlFlowNode for SOURCE |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:499:9:499:14 | ControlFlowNode for SOURCE |
| test.py:20:1:20:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test |
| test.py:20:10:20:17 | ControlFlowNode for Str | test.py:20:1:20:6 | GSSA Variable SOURCE |
@@ -150,9 +154,11 @@ edges
| test.py:344:16:344:21 | ControlFlowNode for SOURCE | test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] |
| test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() |
| test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() |
| test.py:389:33:389:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] |
| test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() |
| test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() |
| test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() |
| test.py:482:33:482:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] |
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a |
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b |
nodes
@@ -175,6 +181,8 @@ nodes
| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] |
| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] |
| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] |
| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] |
| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test |
| test.py:20:1:20:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE |
| test.py:20:10:20:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
@@ -298,12 +306,16 @@ nodes
| test.py:365:28:365:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:373:10:373:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:373:30:373:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
| test.py:389:33:389:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() |
| test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:449:10:449:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:449:28:449:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:463:10:463:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() |
| test.py:463:30:463:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() |
| test.py:482:33:482:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a |
| test.py:506:10:506:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b |
@@ -378,12 +390,16 @@ nodes
| test.py:365:10:365:34 | ControlFlowNode for second() | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | Flow found |
| test.py:373:10:373:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found |
| test.py:373:10:373:36 | ControlFlowNode for second() | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found |
| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found |
| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:389:33:389:38 | ControlFlowNode for SOURCE | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found |
| test.py:442:10:442:18 | ControlFlowNode for f() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found |
| test.py:442:10:442:18 | ControlFlowNode for f() | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found |
| test.py:449:10:449:34 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found |
| test.py:449:10:449:34 | ControlFlowNode for second() | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found |
| test.py:463:10:463:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found |
| test.py:463:10:463:36 | ControlFlowNode for second() | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found |
| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found |
| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:482:33:482:38 | ControlFlowNode for SOURCE | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found |
| test.py:501:10:501:10 | ControlFlowNode for a | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:501:10:501:10 | ControlFlowNode for a | Flow found |
| test.py:501:10:501:10 | ControlFlowNode for a | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | Flow found |
| test.py:506:10:506:10 | ControlFlowNode for b | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:506:10:506:10 | ControlFlowNode for b | Flow found |

View File

@@ -386,7 +386,7 @@ def f_extra_pos(a, *b):
def test_call_extra_pos():
SINK(f_extra_pos(NONSOURCE, SOURCE)) # Flow missing
SINK(f_extra_pos(NONSOURCE, SOURCE))
def f_extra_keyword(a, **b):
@@ -479,7 +479,7 @@ def test_lambda_unpack_mapping():
def test_lambda_extra_pos():
f_extra_pos = lambda a, *b: b[0]
SINK(f_extra_pos(NONSOURCE, SOURCE)) # Flow missing
SINK(f_extra_pos(NONSOURCE, SOURCE))
def test_lambda_extra_keyword():