Skip to content

Commit 411c107

Browse files
committed
Python: Add tests for deepcopy FPs
There are two issues with `deepcopy` here. Firstly, the `deepcopy` function itself has a mutable default value in its parameter `_nil` (set to the empty list by default). Now, this value is never actually returned from `deepcopy`, as it is only used as a sentinel, but our analysis is not clever enough to see this. Thus, it thinks that this mutable default is returned, and hence the result of any call to `deepcopy` is a potential source. To remedy this, I opted to simply exclude all sources that originate from within the standard library. It is very unlikely for any of the sources in the standard library to be legit. Secondly, `deepcopy` -- by virtue of being a function that we model as preserving values -- admits data-flow through its calls, but this is not correct for the mutable default query, as it is here the _identity_ of the default value in question that is important. Thus, we get spurious flow through `deepcopy` for this specific query.
1 parent 4742481 commit 411c107

File tree

4 files changed

+61
-1
lines changed

4 files changed

+61
-1
lines changed

python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/ModificationOfParameterWithDefault.expected

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
edges
2+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x |
3+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y |
4+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil |
5+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y |
6+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y |
7+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() |
8+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | test.py:213:9:213:20 | ControlFlowNode for deepcopy() |
9+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | test.py:217:9:217:19 | ControlFlowNode for deepcopy() |
10+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y |
11+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y |
12+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() |
13+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x |
14+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y |
15+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x |
216
| test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l |
317
| test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l |
418
| test.py:12:14:12:14 | ControlFlowNode for l | test.py:13:9:13:9 | ControlFlowNode for l |
@@ -40,7 +54,27 @@ edges
4054
| test.py:195:28:195:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x |
4155
| test.py:197:18:197:18 | ControlFlowNode for x | test.py:198:28:198:28 | ControlFlowNode for x |
4256
| test.py:198:28:198:28 | ControlFlowNode for x | test.py:181:28:181:28 | ControlFlowNode for x |
57+
| test.py:213:5:213:5 | ControlFlowNode for y | test.py:214:5:214:5 | ControlFlowNode for y |
58+
| test.py:213:9:213:20 | ControlFlowNode for deepcopy() | test.py:213:5:213:5 | ControlFlowNode for y |
59+
| test.py:216:30:216:30 | ControlFlowNode for x | test.py:217:18:217:18 | ControlFlowNode for x |
60+
| test.py:217:5:217:5 | ControlFlowNode for y | test.py:218:5:218:5 | ControlFlowNode for y |
61+
| test.py:217:9:217:19 | ControlFlowNode for deepcopy() | test.py:217:5:217:5 | ControlFlowNode for y |
62+
| test.py:217:18:217:18 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x |
63+
| test.py:217:18:217:18 | ControlFlowNode for x | test.py:217:9:217:19 | ControlFlowNode for deepcopy() |
4364
nodes
65+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
66+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | semmle.label | ControlFlowNode for _nil |
67+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:5:138:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
68+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:9:138:25 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
69+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:138:21:138:24 | ControlFlowNode for _nil | semmle.label | ControlFlowNode for _nil |
70+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:140:16:140:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
71+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:13:149:13 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
72+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() | semmle.label | ControlFlowNode for _deepcopy_atomic() |
73+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
74+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:170:21:170:21 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
75+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
76+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
77+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
4478
| test.py:2:12:2:12 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
4579
| test.py:3:5:3:5 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
4680
| test.py:7:11:7:11 | ControlFlowNode for l | semmle.label | ControlFlowNode for l |
@@ -107,7 +141,17 @@ nodes
107141
| test.py:195:28:195:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
108142
| test.py:197:18:197:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
109143
| test.py:198:28:198:28 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
144+
| test.py:213:5:213:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
145+
| test.py:213:9:213:20 | ControlFlowNode for deepcopy() | semmle.label | ControlFlowNode for deepcopy() |
146+
| test.py:214:5:214:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
147+
| test.py:216:30:216:30 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
148+
| test.py:217:5:217:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
149+
| test.py:217:9:217:19 | ControlFlowNode for deepcopy() | semmle.label | ControlFlowNode for deepcopy() |
150+
| test.py:217:18:217:18 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
151+
| test.py:218:5:218:5 | ControlFlowNode for y | semmle.label | ControlFlowNode for y |
110152
subpaths
153+
| file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:34:149:34 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:182:22:182:22 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:183:12:183:12 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:149:17:149:41 | ControlFlowNode for _deepcopy_atomic() |
154+
| test.py:217:18:217:18 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:14:128:14 | ControlFlowNode for x | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:178:12:178:12 | ControlFlowNode for y | test.py:217:9:217:19 | ControlFlowNode for deepcopy() |
111155
#select
112156
| test.py:3:5:3:5 | ControlFlowNode for l | test.py:2:12:2:12 | ControlFlowNode for l | test.py:3:5:3:5 | ControlFlowNode for l | This expression mutates a $@. | test.py:2:12:2:12 | ControlFlowNode for l | default value |
113157
| test.py:8:5:8:5 | ControlFlowNode for l | test.py:7:11:7:11 | ControlFlowNode for l | test.py:8:5:8:5 | ControlFlowNode for l | This expression mutates a $@. | test.py:7:11:7:11 | ControlFlowNode for l | default value |
@@ -138,3 +182,6 @@ subpaths
138182
| test.py:185:9:185:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:185:9:185:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value |
139183
| test.py:187:9:187:9 | ControlFlowNode for x | test.py:194:18:194:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:194:18:194:18 | ControlFlowNode for x | default value |
140184
| test.py:187:9:187:9 | ControlFlowNode for x | test.py:197:18:197:18 | ControlFlowNode for x | test.py:187:9:187:9 | ControlFlowNode for x | This expression mutates a $@. | test.py:197:18:197:18 | ControlFlowNode for x | default value |
185+
| test.py:214:5:214:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | test.py:214:5:214:5 | ControlFlowNode for y | This expression mutates a $@. | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | default value |
186+
| test.py:218:5:218:5 | ControlFlowNode for y | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | test.py:218:5:218:5 | ControlFlowNode for y | This expression mutates a $@. | file:///usr/local/python/3.10.13/lib/python3.10/copy.py:128:28:128:31 | ControlFlowNode for _nil | default value |
187+
| test.py:218:5:218:5 | ControlFlowNode for y | test.py:216:30:216:30 | ControlFlowNode for x | test.py:218:5:218:5 | ControlFlowNode for y | This expression mutates a $@. | test.py:216:30:216:30 | ControlFlowNode for x | default value |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
semmle-extractor-options: --max-import-depth=2 --dont-split-graph

python/ql/test/query-tests/Functions/ModificationOfParameterWithDefault/test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,15 @@ def tuple_default(x=(1,2)):
204204

205205
def safe_method(x=[]):
206206
return x.count(42)
207+
208+
# Use of deepcopy:
209+
210+
from copy import deepcopy
211+
212+
def flow_from_within_deepcopy_fp():
213+
y = deepcopy([])
214+
y.append(1) #$ SPURIOUS: modification=y
215+
216+
def flow_through_deepcopy_fp(x=[]):
217+
y = deepcopy(x)
218+
y.append(1) #$ SPURIOUS: modification=y
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
semmle-extractor-options: --max-import-depth=1 --dont-split-graph
1+
semmle-extractor-options: --max-import-depth=2 --dont-split-graph

0 commit comments

Comments
 (0)