Skip to content

Commit 49b5d60

Browse files
committed
Python: Use AttrRead/AttrWrite for attr read/store steps
Note that this doesn't actually add the desired flow from setattr, due to missing post-update note. This will be fixed in later commit.
1 parent 5774459 commit 49b5d60

File tree

2 files changed

+19
-18
lines changed

2 files changed

+19
-18
lines changed

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,19 +1067,18 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
10671067
}
10681068

10691069
/**
1070-
* Holds if `nodeFrom` flows into an attribute (corresponding to `c`) of `nodeTo` via an attribute assignment.
1070+
* Holds if `nodeFrom` flows into the attribute `c` of `nodeTo` via an attribute assignment.
10711071
*
10721072
* For example, in
10731073
* ```python
10741074
* obj.foo = x
10751075
* ```
1076-
* data flows from `x` to (the post-update node for) `obj` via assignment to `foo`.
1076+
* data flows from `x` to the attribute `foo` of (the post-update node for) `obj`.
10771077
*/
1078-
predicate attributeStoreStep(CfgNode nodeFrom, AttributeContent c, PostUpdateNode nodeTo) {
1079-
exists(AttrNode attr |
1080-
nodeFrom.asCfgNode() = attr.(DefinitionNode).getValue() and
1081-
attr.getName() = c.getAttribute() and
1082-
attr.getObject() = nodeTo.getPreUpdateNode().(CfgNode).getNode()
1078+
predicate attributeStoreStep(Node nodeFrom, AttributeContent c, PostUpdateNode nodeTo) {
1079+
exists(AttrWrite write |
1080+
write.accesses(nodeTo.getPreUpdateNode(), c.getAttribute()) and
1081+
nodeFrom = write.getValue()
10831082
)
10841083
}
10851084

@@ -1923,21 +1922,16 @@ pragma[noinline]
19231922
TupleElementContent small_tuple() { result.getIndex() <= 7 }
19241923

19251924
/**
1926-
* Holds if `nodeTo` is a read of an attribute (corresponding to `c`) of the object in `nodeFrom`.
1925+
* Holds if `nodeTo` is a read of the attribute `c` of the object `nodeFrom`.
19271926
*
1928-
* For example, in
1927+
* For example
19291928
* ```python
19301929
* obj.foo
19311930
* ```
1932-
* data flows from `obj` to `obj.foo` via a read from `foo`.
1931+
* is a read of the attribute `foo` from the object `obj`.
19331932
*/
1934-
predicate attributeReadStep(CfgNode nodeFrom, AttributeContent c, CfgNode nodeTo) {
1935-
exists(AttrNode attr |
1936-
nodeFrom.asCfgNode() = attr.getObject() and
1937-
nodeTo.asCfgNode() = attr and
1938-
attr.getName() = c.getAttribute() and
1939-
attr.isLoad()
1940-
)
1933+
predicate attributeReadStep(Node nodeFrom, AttributeContent c, AttrRead nodeTo) {
1934+
nodeTo.accesses(nodeFrom, c.getAttribute())
19411935
}
19421936

19431937
/**

python/ql/test/experimental/dataflow/fieldflow/test.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def test_direct_if_always_assign(cond = True):
9595
def test_getattr():
9696
myobj = MyObj(NONSOURCE)
9797
myobj.foo = SOURCE
98-
SINK(getattr(myobj, "foo")) # $ MISSING: flow
98+
SINK(getattr(myobj, "foo")) # $ flow="SOURCE, l:-1 -> getattr(..)"
9999

100100

101101
def test_setattr():
@@ -110,6 +110,13 @@ def test_setattr_getattr():
110110
SINK(getattr(myobj, "foo")) # $ MISSING: flow
111111

112112

113+
def test_setattr_getattr_overwrite():
114+
myobj = MyObj(NONSOURCE)
115+
setattr(myobj, "foo", SOURCE)
116+
setattr(myobj, "foo", NONSOURCE)
117+
SINK_F(getattr(myobj, "foo"))
118+
119+
113120
def test_constructor_assign():
114121
obj = MyObj(SOURCE)
115122
SINK(obj.foo) # $ flow="SOURCE, l:-1 -> obj.foo"

0 commit comments

Comments
 (0)