Python: refactor Node class

This commit is contained in:
Rasmus Lerchedahl Petersen
2020-07-03 08:01:44 +02:00
parent 5f18fb427a
commit bdc68ce6b6
5 changed files with 92 additions and 106 deletions

View File

@@ -40,15 +40,15 @@ module EssaFlow {
// `x = f(42)`
// nodeFrom is `f(42)`, cfg node
// nodeTo is `x`, essa var
nodeFrom.asCfgNode() = nodeTo.asEssaNode().getDefinition().(AssignmentDefinition).getValue()
nodeFrom.(CfgNode).getNode() = nodeTo.(EssaNode).getVar().getDefinition().(AssignmentDefinition).getValue()
or
// With definition
// `with f(42) as x:`
// nodeFrom is `f(42)`, cfg node
// nodeTo is `x`, essa var
exists(With with, ControlFlowNode contextManager, ControlFlowNode var |
nodeFrom.asCfgNode() = contextManager and
nodeTo.asEssaNode().getDefinition().(WithDefinition).getDefiningNode() = var and
nodeFrom.(CfgNode).getNode() = contextManager and
nodeTo.(EssaNode).getVar().getDefinition().(WithDefinition).getDefiningNode() = var and
// see `with_flow`
with.getContextExpr() = contextManager.getNode() and
with.getOptionalVars() = var.getNode() and
@@ -60,22 +60,22 @@ module EssaFlow {
// `x = f(y)`
// nodeFrom is `y` on first line, essa var
// nodeTo is `y` on second line, cfg node
nodeFrom.asEssaNode().getAUse() = nodeTo.asCfgNode()
nodeFrom.(EssaNode).getVar().getAUse() = nodeTo.(CfgNode).getNode()
or
// Refinements
exists(EssaEdgeRefinement r |
nodeTo.asEssaNode() = r.getVariable() and
nodeFrom.asEssaNode() = r.getInput()
nodeTo.(EssaNode).getVar() = r.getVariable() and
nodeFrom.(EssaNode).getVar() = r.getInput()
)
or
exists(EssaNodeRefinement r |
nodeTo.asEssaNode() = r.getVariable() and
nodeFrom.asEssaNode() = r.getInput()
nodeTo.(EssaNode).getVar() = r.getVariable() and
nodeFrom.(EssaNode).getVar() = r.getInput()
)
or
exists(PhiFunction p |
nodeTo.asEssaNode() = p.getVariable() and
nodeFrom.asEssaNode() = p.getAnInput()
nodeTo.(EssaNode).getVar() = p.getVariable() and
nodeFrom.(EssaNode).getVar() = p.getAnInput()
)
}
}
@@ -119,19 +119,19 @@ class DataFlowCall extends CallNode {
}
/** A data flow node that represents a call argument. */
class ArgumentNode extends Node {
class ArgumentNode extends CfgNode {
ArgumentNode() {
exists(DataFlowCall call, int pos |
this.asCfgNode() = call.getArg(pos)
node = call.getArg(pos)
)
}
/** Holds if this argument occurs at the given position in the given call. */
/** Holds if this argument occurs at the given position in the given call. */
predicate argumentOf(DataFlowCall call, int pos) {
this.asCfgNode() = call.getArg(pos)
node = call.getArg(pos)
}
/** Gets the call in which this node is an argument. */
/** Gets the call in which this node is an argument. */
final DataFlowCall getCall() { this.argumentOf(result, _) }
}
@@ -152,31 +152,31 @@ class ReturnKind extends TReturnKind {
}
/** A data flow node that represents a value returned by a callable. */
class ReturnNode extends Node {
class ReturnNode extends CfgNode {
Return ret;
// See `TaintTrackingImplementation::returnFlowStep`
// See `TaintTrackingImplementation::returnFlowStep`
ReturnNode() {
this.asCfgNode() = ret.getValue().getAFlowNode()
node = ret.getValue().getAFlowNode()
}
/** Gets the kind of this return node. */
/** Gets the kind of this return node. */
ReturnKind getKind() { result = TNormalReturnKind() }
override DataFlowCallable getEnclosingCallable() {
override DataFlowCallable getEnclosingCallable() {
result.getScope().getAStmt() = ret // TODO: check nested function definitions
}
}
/** A data flow node that represents the output of a call. */
class OutNode extends Node {
OutNode() { this.asCfgNode() instanceof CallNode }
class OutNode extends CfgNode {
OutNode() { node instanceof CallNode }
/** Gets the underlying call, where this node is a corresponding output of kind `kind`. */
/** Gets the underlying call, where this node is a corresponding output of kind `kind`. */
cached
DataFlowCall getCall(ReturnKind kind) {
kind = TNormalReturnKind() and
result = this.asCfgNode()
result = node
}
}
@@ -231,13 +231,13 @@ string ppReprType(DataFlowType t) { result = t.toString() }
* another. Additional steps specified by the configuration are *not*
* taken into account.
*/
predicate jumpStep(ExprNode pred, ExprNode succ) {
predicate jumpStep(Node pred, Node succ) {
// As we have ESSA variables for global variables,
// we include ESSA flow steps involving global variables.
(
pred.asEssaNode() instanceof GlobalSsaVariable
pred.(EssaNode).getVar() instanceof GlobalSsaVariable
or
succ.asEssaNode() instanceof GlobalSsaVariable
succ.(EssaNode).getVar() instanceof GlobalSsaVariable
) and
EssaFlow::essaFlowStep(pred, succ)
}

View File

@@ -27,34 +27,23 @@ newtype TNode =
* (`ExprNode`) or a parameter (`ParameterNode`).
*/
class Node extends TNode {
/**
* Get the underlying SSA variable if this is such a node.
*/
EssaVariable asEssaNode() { this = TEssaNode(result) }
/**
* Get the underlying ControlFlowNode if this is such a node.
*/
ControlFlowNode asCfgNode() { this = TCfgNode(result) }
/**
* Get a string representation of this data flow node.
*/
string toString() {
result = this.asEssaNode().toString()
or
result = this.asCfgNode().toString()
}
string toString() { result = "Data flow node" }
/** Gets the enclosing callable of this node. */
/** Gets the scope of this node. */
Scope getScope() { none() }
/** Gets the enclosing callable of this node. */
DataFlowCallable getEnclosingCallable() {
result.getScope() = this.asCfgNode().getNode().getScope() // this allows Cfg -> ESSA def
or
result.getScope() = this.asEssaNode().getScope() // this allows ESSA var -> Cfg use
result.getScope() = this.getScope()
}
/**
/** Gets the location of this node */
Location getLocation() { none() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
@@ -64,12 +53,47 @@ class Node extends TNode {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.asEssaNode().getDefinition().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
or
this.asCfgNode().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
this.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
class EssaNode extends Node, TEssaNode {
EssaVariable var;
EssaNode() { this = TEssaNode(var) }
EssaVariable getVar() { result = var }
/**
* Get a string representation of this data flow node.
*/
override string toString() {
result = var.toString()
}
override Scope getScope() { result = var.getScope() }
override Location getLocation() { result = var.getDefinition().getLocation() }
}
class CfgNode extends Node, TCfgNode {
ControlFlowNode node;
CfgNode() { this = TCfgNode(node) }
ControlFlowNode getNode() { result = node }
/**
* Get a string representation of this data flow node.
*/
override string toString() {
result = node.toString()
}
override Scope getScope() { result = node.getScope() }
override Location getLocation() { result = node.getLocation() }
}
/**
@@ -89,22 +113,18 @@ ExprNode exprNode(DataFlowExpr e) { none() }
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends Node {
ParameterNode() {
this.asEssaNode() instanceof ParameterDefinition
}
class ParameterNode extends EssaNode {
ParameterNode() { var instanceof ParameterDefinition }
/**
/**
* Holds if this node is the parameter of callable `c` at the specified
* (zero-based) position.
*/
predicate isParameterOf(DataFlowCallable c, int i) {
this.asEssaNode().(ParameterDefinition).getDefiningNode() = c.getParameter(i)
var.(ParameterDefinition).getDefiningNode() = c.getParameter(i)
}
override DataFlowCallable getEnclosingCallable() {
this.isParameterOf(result, _)
}
override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) }
}
/**

View File

@@ -10,9 +10,8 @@ class MaximalFlowsConfig extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node node) {
node instanceof DataFlow::ParameterNode
or
node = DataFlow::TEssaNode(_) and
not exists(DataFlow::Node pred |
pred = DataFlow::TEssaNode(_) and
node instanceof DataFlow::EssaNode and
not exists(DataFlow::EssaNode pred |
DataFlow::localFlowStep(pred, node)
)
}
@@ -20,7 +19,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration {
override predicate isSink(DataFlow::Node node) {
node instanceof DataFlow::ReturnNode
or
node = DataFlow::TEssaNode(_) and
not exists(node.asEssaNode().getASourceUse())
node instanceof DataFlow::EssaNode and
not exists(node.(DataFlow::EssaNode).getVar().getASourceUse())
}
}

View File

@@ -10,82 +10,54 @@ uniqueEnclosingCallable
| test.py:6:1:6:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:6:5:6:9 | GSSA Variable test1 | Node should have one enclosing callable but has 0. |
| test.py:9:1:9:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:9:1:9:12 | Exit node for Function test2 | Node should have one enclosing callable but has 0. |
| test.py:9:5:9:9 | GSSA Variable test2 | Node should have one enclosing callable but has 0. |
| test.py:13:1:13:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:13:5:13:10 | GSSA Variable source | Node should have one enclosing callable but has 0. |
| test.py:16:1:16:14 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:16:1:16:14 | Exit node for Function sink | Node should have one enclosing callable but has 0. |
| test.py:16:5:16:8 | GSSA Variable sink | Node should have one enclosing callable but has 0. |
| test.py:19:1:19:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:19:1:19:12 | Exit node for Function test3 | Node should have one enclosing callable but has 0. |
| test.py:19:5:19:9 | GSSA Variable test3 | Node should have one enclosing callable but has 0. |
| test.py:23:1:23:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:23:1:23:12 | Exit node for Function test4 | Node should have one enclosing callable but has 0. |
| test.py:23:5:23:9 | GSSA Variable test4 | Node should have one enclosing callable but has 0. |
| test.py:27:1:27:12 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:27:1:27:12 | Exit node for Function test5 | Node should have one enclosing callable but has 0. |
| test.py:27:5:27:9 | GSSA Variable test5 | Node should have one enclosing callable but has 0. |
| test.py:31:1:31:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:31:1:31:16 | Exit node for Function test6 | Node should have one enclosing callable but has 0. |
| test.py:31:1:31:16 | Exit node for Function test6 | Node should have one enclosing callable but has 0. |
| test.py:31:5:31:9 | GSSA Variable test6 | Node should have one enclosing callable but has 0. |
| test.py:39:1:39:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:39:1:39:16 | Exit node for Function test7 | Node should have one enclosing callable but has 0. |
| test.py:39:1:39:16 | Exit node for Function test7 | Node should have one enclosing callable but has 0. |
| test.py:39:5:39:9 | GSSA Variable test7 | Node should have one enclosing callable but has 0. |
| test.py:47:1:47:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:47:1:47:17 | Exit node for Function source2 | Node should have one enclosing callable but has 0. |
| test.py:47:5:47:11 | GSSA Variable source2 | Node should have one enclosing callable but has 0. |
| test.py:50:1:50:15 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:50:1:50:15 | Exit node for Function sink2 | Node should have one enclosing callable but has 0. |
| test.py:50:5:50:9 | GSSA Variable sink2 | Node should have one enclosing callable but has 0. |
| test.py:53:1:53:21 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:53:1:53:21 | Exit node for Function sink3 | Node should have one enclosing callable but has 0. |
| test.py:53:5:53:9 | GSSA Variable sink3 | Node should have one enclosing callable but has 0. |
| test.py:57:1:57:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:57:1:57:16 | Exit node for Function test8 | Node should have one enclosing callable but has 0. |
| test.py:57:5:57:9 | GSSA Variable test8 | Node should have one enclosing callable but has 0. |
| test.py:62:1:62:16 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:62:1:62:16 | Exit node for Function test9 | Node should have one enclosing callable but has 0. |
| test.py:62:5:62:9 | GSSA Variable test9 | Node should have one enclosing callable but has 0. |
| test.py:69:1:69:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:69:1:69:17 | Exit node for Function test10 | Node should have one enclosing callable but has 0. |
| test.py:69:5:69:10 | GSSA Variable test10 | Node should have one enclosing callable but has 0. |
| test.py:76:1:76:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:76:1:76:13 | Exit node for Function hub | Node should have one enclosing callable but has 0. |
| test.py:76:5:76:7 | GSSA Variable hub | Node should have one enclosing callable but has 0. |
| test.py:79:1:79:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:79:1:79:13 | Exit node for Function test11 | Node should have one enclosing callable but has 0. |
| test.py:79:5:79:10 | GSSA Variable test11 | Node should have one enclosing callable but has 0. |
| test.py:84:1:84:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:84:1:84:13 | Exit node for Function test12 | Node should have one enclosing callable but has 0. |
| test.py:84:5:84:10 | GSSA Variable test12 | Node should have one enclosing callable but has 0. |
| test.py:89:8:89:13 | ControlFlowNode for ImportExpr | Node should have one enclosing callable but has 0. |
| test.py:89:8:89:13 | GSSA Variable module | Node should have one enclosing callable but has 0. |
| test.py:91:1:91:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:91:1:91:13 | Exit node for Function test13 | Node should have one enclosing callable but has 0. |
| test.py:91:5:91:10 | GSSA Variable test13 | Node should have one enclosing callable but has 0. |
| test.py:95:1:95:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:95:1:95:13 | Exit node for Function test14 | Node should have one enclosing callable but has 0. |
| test.py:95:5:95:10 | GSSA Variable test14 | Node should have one enclosing callable but has 0. |
| test.py:99:1:99:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:99:1:99:13 | Exit node for Function test15 | Node should have one enclosing callable but has 0. |
| test.py:99:5:99:10 | GSSA Variable test15 | Node should have one enclosing callable but has 0. |
| test.py:103:1:103:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:103:1:103:13 | Exit node for Function test16 | Node should have one enclosing callable but has 0. |
| test.py:103:5:103:10 | GSSA Variable test16 | Node should have one enclosing callable but has 0. |
| test.py:108:1:108:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:108:1:108:17 | Exit node for Function test20 | Node should have one enclosing callable but has 0. |
| test.py:108:1:108:17 | Exit node for Function test20 | Node should have one enclosing callable but has 0. |
| test.py:108:5:108:10 | GSSA Variable test20 | Node should have one enclosing callable but has 0. |
| test.py:118:1:118:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:118:1:118:17 | Exit node for Function test21 | Node should have one enclosing callable but has 0. |
| test.py:118:1:118:17 | Exit node for Function test21 | Node should have one enclosing callable but has 0. |
| test.py:118:5:118:10 | GSSA Variable test21 | Node should have one enclosing callable but has 0. |
| test.py:128:1:128:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:128:1:128:17 | Exit node for Function test22 | Node should have one enclosing callable but has 0. |
| test.py:128:1:128:17 | Exit node for Function test22 | Node should have one enclosing callable but has 0. |
| test.py:128:5:128:10 | GSSA Variable test22 | Node should have one enclosing callable but has 0. |
| test.py:139:20:139:38 | ControlFlowNode for ImportMember | Node should have one enclosing callable but has 0. |
| test.py:139:33:139:38 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. |
@@ -93,17 +65,12 @@ uniqueEnclosingCallable
| test.py:140:1:140:12 | GSSA Variable unsafe | Node should have one enclosing callable but has 0. |
| test.py:140:6:140:11 | ControlFlowNode for unsafe | Node should have one enclosing callable but has 0. |
| test.py:142:1:142:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:142:1:142:13 | Exit node for Function test23 | Node should have one enclosing callable but has 0. |
| test.py:142:5:142:10 | GSSA Variable test23 | Node should have one enclosing callable but has 0. |
| test.py:146:1:146:13 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:146:1:146:13 | Exit node for Function test24 | Node should have one enclosing callable but has 0. |
| test.py:146:5:146:10 | GSSA Variable test24 | Node should have one enclosing callable but has 0. |
| test.py:151:1:151:29 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:151:1:151:29 | Exit node for Function test_update_extend | Node should have one enclosing callable but has 0. |
| test.py:151:5:151:22 | GSSA Variable test_update_extend | Node should have one enclosing callable but has 0. |
| test.py:161:1:161:17 | ControlFlowNode for FunctionExpr | Node should have one enclosing callable but has 0. |
| test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. |
| test.py:161:1:161:17 | Exit node for Function test_truth | Node should have one enclosing callable but has 0. |
| test.py:161:5:161:14 | GSSA Variable test_truth | Node should have one enclosing callable but has 0. |
uniqueType
uniqueNodeLocation

View File

@@ -25,21 +25,21 @@ import experimental.dataflow.DataFlow
class TestConfiguration extends DataFlow::Configuration {
TestConfiguration() { this = "TestConfiguration" }
override predicate isSource(DataFlow::Node node) {
node.asCfgNode().(NameNode).getId() = "SOURCE"
override predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
or
node.asCfgNode().getNode().(StrConst).getS() = "source"
node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source"
or
node.asCfgNode().getNode().(IntegerLiteral).getN() = "42"
node.(DataFlow::CfgNode).getNode().getNode().(IntegerLiteral).getN() = "42"
or
node.asCfgNode().getNode().(FloatLiteral).getN() = "42.0"
node.(DataFlow::CfgNode).getNode().getNode().(FloatLiteral).getN() = "42.0"
// No support for complex numbers
}
override predicate isSink(DataFlow::Node node) {
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK" and
node.asCfgNode() = call.getAnArg()
call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}