Skip to content

Commit e978526

Browse files
fix: eliminate insecure temporary file in atomic_file_write
Replace the close-then-reopen pattern in atomic_file_write() with os.fdopen() to keep the file descriptor from tempfile.mkstemp() open throughout the operation. The previous code closed the fd immediately and reopened the temp file by name, creating a TOCTOU (time-of-check- time-of-use) race window where an attacker could replace the temporary file between close and reopen. The fix passes the mkstemp fd directly to os.fdopen(), ensuring the file handle is never released and re-acquired by name. For non- truncating modes (e.g. 'a'), existing file content is copied through the already-open fd rather than via shutil.copyfile on the path. Fixes: #54 Signed-off-by: Pierluigi Lenoci <pierluigilenoci@gmail.com>
1 parent cc9eca5 commit e978526

1 file changed

Lines changed: 16 additions & 4 deletions

File tree

lib/vdsm/common/fileutils.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,25 @@ def atomic_file_write(filename, flag):
5454
dir=os.path.dirname(os.path.abspath(filename)),
5555
prefix=os.path.basename(filename) + '.',
5656
suffix='.tmp')
57-
os.close(fd)
5857
try:
59-
if os.path.exists(filename):
60-
shutil.copyfile(filename, tmp_filename)
61-
with open(tmp_filename, flag) as f:
58+
if os.path.exists(filename) and 'w' not in flag:
59+
# Preserve existing content for non-truncating modes (e.g. 'a').
60+
# Write through the already-open fd to avoid a close-then-reopen
61+
# race condition (TOCTOU / insecure temporary file).
62+
with open(filename, 'rb') as src:
63+
while True:
64+
chunk = src.read(65536)
65+
if not chunk:
66+
break
67+
os.write(fd, chunk)
68+
os.lseek(fd, 0, os.SEEK_SET)
69+
with os.fdopen(fd, flag) as f:
6270
yield f
6371
except:
72+
try:
73+
os.close(fd)
74+
except OSError:
75+
pass # fd may already be closed by os.fdopen
6476
rm_file(tmp_filename)
6577
raise
6678
else:

0 commit comments

Comments
 (0)