Python: filter self steps from use-use flow

Factor out use-use flow in order to do this.
Also improve names and comments.

I also wanted to change the types in `difinitionFlowStep`, but
that broke the module instantiation.
This commit is contained in:
Rasmus Lerchedahl Petersen
2023-10-31 14:44:10 +01:00
parent 613831b2e1
commit 58bf70d61b
10 changed files with 56 additions and 89 deletions

View File

@@ -279,14 +279,16 @@ class NonSyntheticPostUpdateNode extends PostUpdateNodeImpl, CfgNode {
class DataFlowExpr = Expr;
/**
* Flow between ESSA variables.
* This includes both local and global variables.
* Flow comes from definitions, uses and refinements.
* A module to compute local flow.
*
* Flow will generally go from control flow nodes into essa variables at definitions,
* and from there via use-use flow to other control flow nodes.
*
* Some syntaxtic constructs are handled separately.
*/
// TODO: Consider constraining `nodeFrom` and `nodeTo` to be in the same scope.
// If they have different enclosing callables, we get consistency errors.
module EssaFlow {
predicate essaFlowStep(Node nodeFrom, Node nodeTo) {
module LocalFlow {
/** Holds if `nodeFrom` is the control flow node defining the essa variable `nodeTo`. */
predicate definitionFlowStep(Node nodeFrom, Node nodeTo) {
// Definition
// `x = f(42)`
// nodeFrom is `f(42)`, cfg node
@@ -336,10 +338,37 @@ module EssaFlow {
// nodeFrom is `x`, cfgNode
// nodeTo is `x`, essa var
exists(ParameterDefinition pd |
nodeFrom.asCfgNode() = pd.getDefiningNode() and
nodeTo.asVar() = pd.getVariable()
nodeFrom.(CfgNode).getNode() = pd.getDefiningNode() and
nodeTo.(EssaNode).getVar() = pd.getVariable()
)
}
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
// If expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
or
// Assignment expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
or
// boolean inline expressions such as `x or y` or `x and y`
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
or
// Flow inside an unpacking assignment
iterableUnpackingFlowStep(nodeFrom, nodeTo)
or
// Flow inside a match statement
matchFlowStep(nodeFrom, nodeTo)
}
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
}
predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
}
predicate useUseFlowStep(Node nodeFrom, Node nodeTo) {
// First use after definition
// `y = 42`
// `x = f(y)`
@@ -353,28 +382,18 @@ module EssaFlow {
// nodeFrom is 'y' on first line, cfg node
// nodeTo is `y` on second line, cfg node
useToNextUse(nodeFrom.asCfgNode(), nodeTo.asCfgNode())
or
// If expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
or
// Assignment expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
or
// boolean inline expressions such as `x or y` or `x and y`
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
or
// Flow inside an unpacking assignment
iterableUnpackingFlowStep(nodeFrom, nodeTo)
or
matchFlowStep(nodeFrom, nodeTo)
}
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
}
predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
IncludePostUpdateFlow<PhaseDependentFlow<definitionFlowStep/2>::step/2>::step(nodeFrom, nodeTo)
or
IncludePostUpdateFlow<PhaseDependentFlow<expressionFlowStep/2>::step/2>::step(nodeFrom, nodeTo)
or
// Use-use flow can generate self loops. We want to filter steps from `n` to `n`
// after we have included steps from `[post] n` to `n`, so after
// `IncludePostUpdateFlow` has ben applied.
IncludePostUpdateFlow<PhaseDependentFlow<useUseFlowStep/2>::step/2>::step(nodeFrom, nodeTo) and
nodeFrom != nodeTo
}
}
@@ -481,7 +500,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
* or at runtime when callables in the module are called.
*/
predicate simpleLocalFlowStepForTypetracking(Node nodeFrom, Node nodeTo) {
IncludePostUpdateFlow<PhaseDependentFlow<EssaFlow::essaFlowStep/2>::step/2>::step(nodeFrom, nodeTo)
IncludePostUpdateFlow<PhaseDependentFlow<LocalFlow::localFlowStep/2>::step/2>::step(nodeFrom,
nodeTo)
}
private predicate summaryLocalStep(Node nodeFrom, Node nodeTo) {

View File

@@ -111,13 +111,13 @@ module ImportResolution {
allowedEssaImportStep*(firstDef, lastUseVar) and
not allowedEssaImportStep(_, firstDef)
|
not EssaFlow::defToFirstUse(firstDef, _) and
not LocalFlow::defToFirstUse(firstDef, _) and
val.asVar() = firstDef
or
exists(ControlFlowNode mid, ControlFlowNode end |
EssaFlow::defToFirstUse(firstDef, mid) and
EssaFlow::useToNextUse*(mid, end) and
not EssaFlow::useToNextUse(end, _) and
LocalFlow::defToFirstUse(firstDef, mid) and
LocalFlow::useToNextUse*(mid, end) and
not LocalFlow::useToNextUse(end, _) and
val.asCfgNode() = end
)
)

View File

@@ -25,18 +25,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| datamodel.py:84:15:84:15 | ControlFlowNode for x | Node steps to itself |
| datamodel.py:166:11:166:11 | ControlFlowNode for x | Node steps to itself |
| test.py:103:10:103:15 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:130:10:130:15 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:162:13:162:18 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:167:13:167:18 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:216:10:216:15 | ControlFlowNode for SOURCE | Node steps to itself |
| test.py:242:9:242:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:673:9:673:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:674:9:674:14 | ControlFlowNode for SINK_F | Node steps to itself |
| test.py:682:9:682:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:690:9:690:12 | ControlFlowNode for SINK | Node steps to itself |
| test.py:696:5:696:8 | ControlFlowNode for SINK | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -12,7 +12,7 @@ module ImportTimeLocalFlowTest implements FlowTestSig {
// results are displayed next to `nodeTo`, so we need a line to write on
nodeTo.getLocation().getStartLine() > 0 and
nodeTo.asVar() instanceof GlobalSsaVariable and
DP::PhaseDependentFlow<DP::EssaFlow::essaFlowStep/2>::importTimeStep(nodeFrom, nodeTo)
DP::PhaseDependentFlow<DP::LocalFlow::localFlowStep/2>::importTimeStep(nodeFrom, nodeTo)
}
}

View File

@@ -33,5 +33,5 @@ query predicate jumpStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
query predicate essaFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
os_import(nodeFrom) and
DataFlowPrivate::EssaFlow::essaFlowStep(nodeFrom, nodeTo)
DataFlowPrivate::LocalFlow::localFlowStep(nodeFrom, nodeTo)
}

View File

@@ -23,7 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| test_collections.py:20:9:20:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_unpacking.py:31:9:31:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -23,20 +23,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| test_async.py:48:9:48:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:64:10:64:21 | ControlFlowNode for tainted_list | Node steps to itself |
| test_collections.py:71:9:71:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:73:9:73:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:88:10:88:21 | ControlFlowNode for tainted_list | Node steps to itself |
| test_collections.py:89:10:89:23 | ControlFlowNode for TAINTED_STRING | Node steps to itself |
| test_collections.py:97:9:97:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:99:9:99:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:112:9:112:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:114:9:114:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:147:9:147:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:149:9:149:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
| test_collections.py:246:9:246:15 | ControlFlowNode for my_dict | Node steps to itself |
| test_collections.py:246:22:246:33 | ControlFlowNode for tainted_dict | Node steps to itself |
| test_for.py:24:9:24:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -29,8 +29,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| test_collections.py:36:10:36:15 | ControlFlowNode for SOURCE | Node steps to itself |
| test_collections.py:45:19:45:21 | ControlFlowNode for mod | Node steps to itself |
| test_collections.py:52:13:52:21 | ControlFlowNode for mod_local | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -27,7 +27,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| test_captured.py:7:22:7:22 | ControlFlowNode for p | Node steps to itself |
| test_captured.py:14:26:14:27 | ControlFlowNode for pp | Node steps to itself |
missingArgumentCall
multipleArgumentCall

View File

@@ -106,23 +106,5 @@ uniqueParameterNodeAtPosition
uniqueParameterNodePosition
uniqueContentApprox
identityLocalStep
| testapp/orm_tests.py:217:24:217:29 | ControlFlowNode for SOURCE | Node steps to itself |
| testapp/orm_tests.py:244:24:244:29 | ControlFlowNode for SOURCE | Node steps to itself |
| testapp/orm_tests.py:283:20:283:25 | ControlFlowNode for SOURCE | Node steps to itself |
| testapp/orm_tests.py:299:15:299:22 | ControlFlowNode for TestLoad | Node steps to itself |
| testapp/orm_tests.py:300:20:300:25 | ControlFlowNode for SOURCE | Node steps to itself |
| testapp/orm_tests.py:310:9:310:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:316:9:316:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:326:9:326:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:333:9:333:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:339:9:339:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:346:9:346:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:352:9:352:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:358:9:358:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/orm_tests.py:365:9:365:12 | ControlFlowNode for SINK | Node steps to itself |
| testapp/tests.py:12:13:12:14 | ControlFlowNode for re | Node steps to itself |
| testapp/tests.py:16:9:16:18 | ControlFlowNode for test_names | Node steps to itself |
| testapp/tests.py:25:13:25:14 | ControlFlowNode for re | Node steps to itself |
| testapp/tests.py:31:9:31:18 | ControlFlowNode for test_names | Node steps to itself |
missingArgumentCall
multipleArgumentCall