Skip to content

Commit e011b4b

Browse files
committed
Python: split definitions into local and jumps
1 parent 9478c75 commit e011b4b

File tree

1 file changed

+58
-41
lines changed

1 file changed

+58
-41
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -288,42 +288,50 @@ class DataFlowExpr = Expr;
288288
* Some syntaxtic constructs are handled separately.
289289
*/
290290
module LocalFlow {
291-
/** Holds if `nodeFrom` is the expression defining the value for the variable `nodeTo`. */
291+
/**
292+
* Holds if `nodeFrom` is the expression defining the value for the variable `nodeTo`.
293+
* This includes cases like
294+
* `x = f(42)`
295+
* and
296+
* `with f(42) as x:`
297+
* where `nodeFrom` is `f(42)` and `nodeTo` is `x`.
298+
*/
292299
predicate definitionFlowStep(Node nodeFrom, Node nodeTo) {
293-
// Definition
294-
// `x = f(42)`
295-
// nodeFrom is `f(42)`
296-
// nodeTo is `x`
297-
exists(AssignmentDefinition def |
298-
nodeFrom.(CfgNode).getNode() = def.getValue() and
299-
nodeTo.(CfgNode).getNode() = def.getDefiningNode()
300-
)
301-
or
302-
// General definition
303-
// TODO: remove other cases that are now redundant
304-
nodeFrom.(CfgNode).getNode() = nodeTo.(CfgNode).getNode().(DefinitionNode).getValue()
305-
or
306-
// With definition
307-
// `with f(42) as x:`
308-
// nodeFrom is `f(42)`
309-
// nodeTo is `x`
310-
exists(With with, ControlFlowNode contextManager, WithDefinition withDef, ControlFlowNode var |
311-
var = withDef.getDefiningNode()
312-
|
313-
nodeFrom.(CfgNode).getNode() = contextManager and
314-
nodeTo.(CfgNode).getNode() = var and
315-
// see `with_flow` in `python/ql/src/semmle/python/dataflow/Implementation.qll`
316-
with.getContextExpr() = contextManager.getNode() and
317-
with.getOptionalVars() = var.getNode() and
318-
contextManager.strictlyDominates(var)
319-
// note: we allow this for both `with` and `async with`, since some
320-
// implementations do `async def __aenter__(self): return self`, so you can do
321-
// both:
322-
// * `foo = x.foo(); await foo.async_method(); foo.close()` and
323-
// * `async with x.foo() as foo: await foo.async_method()`.
324-
)
300+
nodeFrom.(CfgNode).getNode() = nodeTo.(CfgNode).getNode().(DefinitionNode).getValue() and
301+
// This excludes steps that should be jump steps,
302+
// such as assignment of a parameters default value.
303+
// (Since the parameter will be in the scope of the function,
304+
// while the default value itself will be in the scope that _defines_ the
305+
// function.)
306+
nodeFrom.getEnclosingCallable() = nodeTo.getEnclosingCallable()
325307
}
326308

309+
// predicate generalDefinitionFlowStep(Node nodeFrom, Node nodeTo) {
310+
// // General definition
311+
// // TODO: remove other cases that are now redundant
312+
// nodeFrom.(CfgNode).getNode() = nodeTo.(CfgNode).getNode().(DefinitionNode).getValue()
313+
// }
314+
// predicate localGeneral(
315+
// Node nodeFrom, DataFlowCallable callableFrom, Node nodeTo, DataFlowCallable callableTo
316+
// ) {
317+
// generalDefinitionFlowStep(nodeFrom, nodeTo) and
318+
// callableFrom = nodeFrom.getEnclosingCallable() and
319+
// callableTo = nodeTo.getEnclosingCallable() and
320+
// callableFrom != callableTo
321+
// }
322+
// predicate compareDef(Node nodeFrom, Node nodeTo, string message) {
323+
// definitionFlowStep(nodeFrom, nodeTo) and
324+
// not generalDefinitionFlowStep(nodeFrom, nodeTo) and
325+
// message = "only in def"
326+
// or
327+
// not definitionFlowStep(nodeFrom, nodeTo) and
328+
// generalDefinitionFlowStep(nodeFrom, nodeTo) and
329+
// message = "only in general"
330+
// or
331+
// definitionFlowStep(nodeFrom, nodeTo) and
332+
// generalDefinitionFlowStep(nodeFrom, nodeTo) and
333+
// message = "in both"
334+
// }
327335
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
328336
// If expressions
329337
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
@@ -341,6 +349,19 @@ module LocalFlow {
341349
matchFlowStep(nodeFrom, nodeTo)
342350
}
343351

352+
// predicate compareExpr(Node nodeFrom, Node nodeTo, string message) {
353+
// expressionFlowStep(nodeFrom, nodeTo) and
354+
// not generalDefinitionFlowStep(nodeFrom, nodeTo) and
355+
// message = "only in def"
356+
// or
357+
// not expressionFlowStep(nodeFrom, nodeTo) and
358+
// generalDefinitionFlowStep(nodeFrom, nodeTo) and
359+
// message = "only in general"
360+
// or
361+
// expressionFlowStep(nodeFrom, nodeTo) and
362+
// generalDefinitionFlowStep(nodeFrom, nodeTo) and
363+
// message = "in both"
364+
// }
344365
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
345366
AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
346367
}
@@ -509,14 +530,10 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
509530
// Setting the possible values of the variable at the end of import time
510531
nodeFrom = nodeTo.(ModuleVariableNode).getADefiningWrite()
511532
or
512-
// a parameter with a default value, since the parameter will be in the scope of the
513-
// function, while the default value itself will be in the scope that _defines_ the
514-
// function.
515-
exists(ParameterDefinition param |
516-
// note: we go to the _control-flow node_ of the parameter, and not the ESSA node of the parameter, since for type-tracking, the ESSA node is not a LocalSourceNode, so we would get in trouble.
517-
nodeFrom.asCfgNode() = param.getDefault() and
518-
nodeTo.asCfgNode() = param.getDefiningNode()
519-
)
533+
// Non-local definitions, such as assignment of a parameters default value.
534+
nodeFrom.(CfgNode).getNode() = nodeTo.(CfgNode).getNode().(DefinitionNode).getValue() and
535+
// This excludes steps that should be local.
536+
nodeFrom.getEnclosingCallable() != nodeTo.getEnclosingCallable()
520537
}
521538

522539
//--------

0 commit comments

Comments
 (0)