Skip to content

Commit 9503c5d

Browse files
committed
Python: Add post-update nodes
1 parent e7322d1 commit 9503c5d

File tree

12 files changed

+115
-62
lines changed

12 files changed

+115
-62
lines changed

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,18 @@ module Consistency {
123123
n.getEnclosingCallable() != call.getEnclosingCallable()
124124
}
125125

126+
// This predicate helps the compiler forget that in some languages
127+
// it is impossible for a result of `getPreUpdateNode` to be an
128+
// instance of `PostUpdateNode`.
129+
private Node getPre(PostUpdateNode n) {
130+
result = n.getPreUpdateNode()
131+
or
132+
none()
133+
}
134+
126135
query predicate postIsNotPre(PostUpdateNode n, string msg) {
127-
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
136+
getPre(n) = n and
137+
msg = "PostUpdateNode should not equal its pre-update node."
128138
}
129139

130140
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -152,12 +162,6 @@ module Consistency {
152162
msg = "Origin of readStep is missing a PostUpdateNode."
153163
}
154164

155-
query predicate storeIsPostUpdate(Node n, string msg) {
156-
storeStep(_, _, n) and
157-
not n instanceof PostUpdateNode and
158-
msg = "Store targets should be PostUpdateNodes."
159-
}
160-
161165
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
162166
not hasPost(n) and
163167
not isImmutableOrUnobservable(n) and

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,18 @@ module Consistency {
123123
n.getEnclosingCallable() != call.getEnclosingCallable()
124124
}
125125

126+
// This predicate helps the compiler forget that in some languages
127+
// it is impossible for a result of `getPreUpdateNode` to be an
128+
// instance of `PostUpdateNode`.
129+
private Node getPre(PostUpdateNode n) {
130+
result = n.getPreUpdateNode()
131+
or
132+
none()
133+
}
134+
126135
query predicate postIsNotPre(PostUpdateNode n, string msg) {
127-
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
136+
getPre(n) = n and
137+
msg = "PostUpdateNode should not equal its pre-update node."
128138
}
129139

130140
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -152,12 +162,6 @@ module Consistency {
152162
msg = "Origin of readStep is missing a PostUpdateNode."
153163
}
154164

155-
query predicate storeIsPostUpdate(Node n, string msg) {
156-
storeStep(_, _, n) and
157-
not n instanceof PostUpdateNode and
158-
msg = "Store targets should be PostUpdateNodes."
159-
}
160-
161165
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
162166
not hasPost(n) and
163167
not isImmutableOrUnobservable(n) and

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,18 @@ module Consistency {
123123
n.getEnclosingCallable() != call.getEnclosingCallable()
124124
}
125125

126+
// This predicate helps the compiler forget that in some languages
127+
// it is impossible for a result of `getPreUpdateNode` to be an
128+
// instance of `PostUpdateNode`.
129+
private Node getPre(PostUpdateNode n) {
130+
result = n.getPreUpdateNode()
131+
or
132+
none()
133+
}
134+
126135
query predicate postIsNotPre(PostUpdateNode n, string msg) {
127-
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
136+
getPre(n) = n and
137+
msg = "PostUpdateNode should not equal its pre-update node."
128138
}
129139

130140
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -152,12 +162,6 @@ module Consistency {
152162
msg = "Origin of readStep is missing a PostUpdateNode."
153163
}
154164

155-
query predicate storeIsPostUpdate(Node n, string msg) {
156-
storeStep(_, _, n) and
157-
not n instanceof PostUpdateNode and
158-
msg = "Store targets should be PostUpdateNodes."
159-
}
160-
161165
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
162166
not hasPost(n) and
163167
not isImmutableOrUnobservable(n) and

java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,18 @@ module Consistency {
123123
n.getEnclosingCallable() != call.getEnclosingCallable()
124124
}
125125

126+
// This predicate helps the compiler forget that in some languages
127+
// it is impossible for a result of `getPreUpdateNode` to be an
128+
// instance of `PostUpdateNode`.
129+
private Node getPre(PostUpdateNode n) {
130+
result = n.getPreUpdateNode()
131+
or
132+
none()
133+
}
134+
126135
query predicate postIsNotPre(PostUpdateNode n, string msg) {
127-
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
136+
getPre(n) = n and
137+
msg = "PostUpdateNode should not equal its pre-update node."
128138
}
129139

130140
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -152,12 +162,6 @@ module Consistency {
152162
msg = "Origin of readStep is missing a PostUpdateNode."
153163
}
154164

155-
query predicate storeIsPostUpdate(Node n, string msg) {
156-
storeStep(_, _, n) and
157-
not n instanceof PostUpdateNode and
158-
msg = "Store targets should be PostUpdateNodes."
159-
}
160-
161165
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
162166
not hasPost(n) and
163167
not isImmutableOrUnobservable(n) and

python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,18 @@ module Consistency {
123123
n.getEnclosingCallable() != call.getEnclosingCallable()
124124
}
125125

126+
// This predicate helps the compiler forget that in some languages
127+
// it is impossible for a result of `getPreUpdateNode` to be an
128+
// instance of `PostUpdateNode`.
129+
private Node getPre(PostUpdateNode n) {
130+
result = n.getPreUpdateNode()
131+
or
132+
none()
133+
}
134+
126135
query predicate postIsNotPre(PostUpdateNode n, string msg) {
127-
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
136+
getPre(n) = n and
137+
msg = "PostUpdateNode should not equal its pre-update node."
128138
}
129139

130140
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -152,12 +162,6 @@ module Consistency {
152162
msg = "Origin of readStep is missing a PostUpdateNode."
153163
}
154164

155-
query predicate storeIsPostUpdate(Node n, string msg) {
156-
storeStep(_, _, n) and
157-
not n instanceof PostUpdateNode and
158-
msg = "Store targets should be PostUpdateNodes."
159-
}
160-
161165
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
162166
not hasPost(n) and
163167
not isImmutableOrUnobservable(n) and

python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,32 @@ class DataFlowCfgNode extends ControlFlowNode {
1515
DataFlowCfgNode() { isExpressionNode(this) }
1616
}
1717

18+
/** A data flow node which should have an associated post-update node. */
19+
abstract class PreUpdateNode extends Node { }
20+
21+
/** An argument might have its value changed as a result of a call. */
22+
class ArgumentPreUpdateNode extends PreUpdateNode, ArgumentNode { }
23+
24+
/** An object might have its value changed after a store. */
25+
class StorePreUpdateNode extends PreUpdateNode, CfgNode {
26+
StorePreUpdateNode() {
27+
exists(Attribute a |
28+
node = a.getObject().getAFlowNode() and
29+
a.getCtx() instanceof Store
30+
)
31+
}
32+
}
33+
34+
/** A node marking the state change of an object after a read */
35+
class ReadPreUpdateNode extends PreUpdateNode, CfgNode {
36+
ReadPreUpdateNode() {
37+
exists(Attribute a |
38+
node = a.getObject().getAFlowNode() and
39+
a.getCtx() instanceof Load
40+
)
41+
}
42+
}
43+
1844
/**
1945
* A node associated with an object after an operation that might have
2046
* changed its state.
@@ -24,12 +50,21 @@ class DataFlowCfgNode extends ControlFlowNode {
2450
* an update to the field.
2551
*
2652
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
27-
* to the value before the update with the exception of `ObjectCreation`,
28-
* which represents the value after the constructor has run.
53+
* to the value before the update.
2954
*/
30-
abstract class PostUpdateNode extends Node {
55+
class PostUpdateNode extends Node, TPostUpdateNode {
56+
PreUpdateNode pre;
57+
58+
PostUpdateNode() { this = TPostUpdateNode(pre) }
59+
3160
/** Gets the node before the state update. */
32-
abstract Node getPreUpdateNode();
61+
Node getPreUpdateNode() { result = pre }
62+
63+
override string toString() { result = "[post] " + pre.toString() }
64+
65+
override Scope getScope() { result = pre.getScope() }
66+
67+
override Location getLocation() { result = pre.getLocation() }
3368
}
3469

3570
class DataFlowExpr = Expr;
@@ -98,7 +133,17 @@ module EssaFlow {
98133
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
99134
not nodeFrom.(EssaNode).getVar() instanceof GlobalSsaVariable and
100135
not nodeTo.(EssaNode).getVar() instanceof GlobalSsaVariable and
101-
EssaFlow::essaFlowStep(nodeFrom, nodeTo)
136+
EssaFlow::essaFlowStep(update(nodeFrom), nodeTo)
137+
}
138+
139+
private Node update(Node node) {
140+
exists(PostUpdateNode pun |
141+
node = pun.getPreUpdateNode() and
142+
result = pun
143+
)
144+
or
145+
not exists(PostUpdateNode pun | node = pun.getPreUpdateNode()) and
146+
result = node
102147
}
103148

104149
// TODO: Make modules for these headings

python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ newtype TNode =
2020
/** A node corresponding to an SSA variable. */
2121
TEssaNode(EssaVariable var) or
2222
/** A node corresponding to a control flow node. */
23-
TCfgNode(DataFlowCfgNode node)
23+
TCfgNode(DataFlowCfgNode node) or
24+
/** A node representing the value of an object after a state change */
25+
TPostUpdateNode(PreUpdateNode pre)
2426

2527
/**
2628
* An element, viewed as a node in a data flow graph. Either an SSA variable

python/ql/test/experimental/dataflow/basic/local.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@
3838
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
3939
| test.py:7:5:7:20 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a |
4040
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a |
41+
| test.py:7:19:7:19 | [post] ControlFlowNode for a | test.py:7:19:7:19 | [post] ControlFlowNode for a |

python/ql/test/experimental/dataflow/basic/sinks.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@
2323
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
2424
| test.py:7:5:7:20 | GSSA Variable a |
2525
| test.py:7:19:7:19 | ControlFlowNode for a |
26+
| test.py:7:19:7:19 | [post] ControlFlowNode for a |

python/ql/test/experimental/dataflow/basic/sources.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@
2323
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
2424
| test.py:7:5:7:20 | GSSA Variable a |
2525
| test.py:7:19:7:19 | ControlFlowNode for a |
26+
| test.py:7:19:7:19 | [post] ControlFlowNode for a |

0 commit comments

Comments
 (0)