Skip to content

Commit 8909694

Browse files
committed
Add test for FP for py/file-not-closed
1 parent 9c65082 commit 8909694

2 files changed

Lines changed: 39 additions & 9 deletions

File tree

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
| resources_test.py:4:10:4:25 | ControlFlowNode for open() | File may not be closed if $@ raises an exception. | resources_test.py:5:5:5:33 | ControlFlowNode for Attribute() | this operation |
2-
| resources_test.py:9:10:9:25 | ControlFlowNode for open() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
3-
| resources_test.py:108:11:108:20 | ControlFlowNode for open() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
4-
| resources_test.py:112:11:112:28 | ControlFlowNode for opener_func2() | File may not be closed if $@ raises an exception. | resources_test.py:113:5:113:22 | ControlFlowNode for Attribute() | this operation |
5-
| resources_test.py:123:11:123:24 | ControlFlowNode for opener_func2() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
6-
| resources_test.py:129:15:129:24 | ControlFlowNode for open() | File may not be closed if $@ raises an exception. | resources_test.py:130:9:130:26 | ControlFlowNode for Attribute() | this operation |
7-
| resources_test.py:248:11:248:25 | ControlFlowNode for open() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
8-
| resources_test.py:269:10:269:27 | ControlFlowNode for Attribute() | File may not be closed if $@ raises an exception. | resources_test.py:271:5:271:19 | ControlFlowNode for Attribute() | this operation |
9-
| resources_test.py:285:11:285:20 | ControlFlowNode for open() | File may not be closed if $@ raises an exception. | resources_test.py:287:5:287:31 | ControlFlowNode for Attribute() | this operation |
1+
| resources_test.py:6:10:6:25 | ControlFlowNode for open() | File may not be closed if $@ raises an exception. | resources_test.py:7:5:7:33 | ControlFlowNode for Attribute() | this operation |
2+
| resources_test.py:11:10:11:25 | ControlFlowNode for open() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
3+
| resources_test.py:110:11:110:20 | ControlFlowNode for open() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
4+
| resources_test.py:114:11:114:28 | ControlFlowNode for opener_func2() | File may not be closed if $@ raises an exception. | resources_test.py:115:5:115:22 | ControlFlowNode for Attribute() | this operation |
5+
| resources_test.py:125:11:125:24 | ControlFlowNode for opener_func2() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
6+
| resources_test.py:131:15:131:24 | ControlFlowNode for open() | File may not be closed if $@ raises an exception. | resources_test.py:132:9:132:26 | ControlFlowNode for Attribute() | this operation |
7+
| resources_test.py:250:11:250:25 | ControlFlowNode for open() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |
8+
| resources_test.py:271:10:271:27 | ControlFlowNode for Attribute() | File may not be closed if $@ raises an exception. | resources_test.py:273:5:273:19 | ControlFlowNode for Attribute() | this operation |
9+
| resources_test.py:287:11:287:20 | ControlFlowNode for open() | File may not be closed if $@ raises an exception. | resources_test.py:289:5:289:31 | ControlFlowNode for Attribute() | this operation |
10+
| resources_test.py:361:13:361:27 | ControlFlowNode for Attribute() | File is opened but is not closed. | file://:0:0:0:0 | (none) | this operation |

python/ql/test/query-tests/Resources/FileNotAlwaysClosed/resources_test.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#File not always closed
22

3+
import os
4+
35
def not_close1():
46
f1 = open("filename") # $ Alert # not closed on exception
57
f1.write("Error could occur")
@@ -332,3 +334,30 @@ def closed32(path):
332334
# due to a check that an operation is lexically contained within a `with` block (with `expr.getParent*()`)
333335
# not detecting this case.
334336
return list(wrap.read())
337+
338+
339+
class FdHolder33():
340+
# Mirrors CPython's `_pyio.FileIO`: it opens a file descriptor with `os.open`,
341+
# stores it in an instance attribute, and exposes it again via `fileno()`.
342+
def __init__(self, path):
343+
fd = os.open(path, os.O_RDONLY)
344+
self._fd = fd
345+
346+
def fileno(self):
347+
return self._fd
348+
349+
def close(self):
350+
os.close(self._fd)
351+
352+
def closed33(path):
353+
# False positive mirroring CPython's `_pyio.open`.
354+
# `holder.fileno()` merely returns the existing file descriptor; it does not
355+
# open a new resource. With instance-attribute type tracking, the `os.open`
356+
# source flows through `self._fd` and back out of `fileno()`, so the call
357+
# `holder.fileno()` is wrongly treated as a fresh file-open whose result is
358+
# never closed. The descriptor is in fact owned and closed by `holder.close()`.
359+
holder = FdHolder33(path)
360+
try:
361+
n = holder.fileno() # $ SPURIOUS: Alert
362+
finally:
363+
holder.close()

0 commit comments

Comments
 (0)