Skip to content

Commit 6c3aabe

Browse files
authored
Python: Support flow through import *
Adds result for `ModuleVariableNode::getARead` corresponding to reads that go through (chains of) `import *`. This required a bit of a change to _which_ module variables we define. Previously, we only included variables that were accessed elsewhere in the same file, but now we must ensure to also include variables that may be accessed through `import *`.
1 parent c3e495e commit 6c3aabe

File tree

3 files changed

+28
-2
lines changed

3 files changed

+28
-2
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import semmle.python.dataflow.new.TypeTracker
88
import Attributes
99
import LocalSources
1010
private import semmle.python.essa.SsaCompute
11+
private import semmle.python.dataflow.new.internal.ImportStar
1112

1213
/**
1314
* IPA type for data flow nodes.
@@ -30,7 +31,15 @@ newtype TNode =
3031
/** A synthetic node representing the value of an object after a state change. */
3132
TSyntheticPostUpdateNode(NeedsSyntheticPostUpdateNode pre) or
3233
/** A node representing a global (module-level) variable in a specific module. */
33-
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m and v.escapes() } or
34+
TModuleVariableNode(Module m, GlobalVariable v) {
35+
v.getScope() = m and
36+
(
37+
v.escapes()
38+
or
39+
isAccessedThroughImportStar(m) and
40+
ImportStar::globalNameDefinedInModule(v.getId(), m)
41+
)
42+
} or
3443
/**
3544
* A node representing the overflow positional arguments to a call.
3645
* That is, `call` contains more positional arguments than there are
@@ -346,6 +355,8 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
346355
result.asCfgNode() = var.getALoad().getAFlowNode() and
347356
// Ignore reads that happen when the module is imported. These are only executed once.
348357
not result.getScope() = mod
358+
or
359+
this = import_star_read(result)
349360
}
350361

351362
/** Gets an `EssaNode` that corresponds to an assignment of this global variable. */
@@ -358,6 +369,13 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
358369
override Location getLocation() { result = mod.getLocation() }
359370
}
360371

372+
private predicate isAccessedThroughImportStar(Module m) { m = ImportStar::getStarImported(_) }
373+
374+
private ModuleVariableNode import_star_read(Node n) {
375+
ImportStar::importStarResolvesTo(n.asCfgNode(), result.getModule()) and
376+
n.asCfgNode().(NameNode).getId() = result.getVariable().getId()
377+
}
378+
361379
/**
362380
* The node holding the extra positional arguments to a call. This node is passed as a tuple
363381
* to the starred parameter of the callable.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
known_attr = [1000]
1+
known_attr = [1000] #$ writes=known_attr
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
moduleVariables
2+
| three.py:0:0:0:0 | ModuleVariableNode for three.foo |
3+
| trois.py:0:0:0:0 | ModuleVariableNode for trois.foo |
24
reads
5+
| three.py:0:0:0:0 | ModuleVariableNode for three.foo | test1.py:2:7:2:9 | ControlFlowNode for foo |
6+
| three.py:0:0:0:0 | ModuleVariableNode for three.foo | two.py:2:7:2:9 | ControlFlowNode for foo |
7+
| trois.py:0:0:0:0 | ModuleVariableNode for trois.foo | deux.py:2:7:2:9 | ControlFlowNode for foo |
8+
| trois.py:0:0:0:0 | ModuleVariableNode for trois.foo | test2.py:2:7:2:9 | ControlFlowNode for foo |
39
writes
10+
| three.py:1:1:1:3 | GSSA Variable foo | three.py:0:0:0:0 | ModuleVariableNode for three.foo |
11+
| trois.py:1:1:1:3 | GSSA Variable foo | trois.py:0:0:0:0 | ModuleVariableNode for trois.foo |

0 commit comments

Comments
 (0)