https://github.com/python/cpython/commit/ff81651c0864373a5dd6e0dcc7385e8656a1eefc commit: ff81651c0864373a5dd6e0dcc7385e8656a1eefc branch: 3.14 author: Victor Stinner <[email protected]> committer: vstinner <[email protected]> date: 2026-10-16T22:50:07Z summary:
[3.14] gh-133998: Fix tarfile error on out-of-range mtime in streaming gzip mode (#159589) [3.15] gh-133998: Fix tarfile error on out-of-range mtime in streaming gzip mode (GH-151828) (#159158) gh-133998: Fix tarfile error on out-of-range mtime in streaming gzip mode (GH-151828) tarfile.open(..., "w|gz", mtime=...) packed mtime into the gzip header's 32-bit field with no range check, so a value < 0 or >= 2**32 raised struct.error. Mirror the merged gzip fix: substitute 0 for out-of-range values and coerce to int (RFC 1952), so floats and out-of-range system clocks behave the same as in gzip and the non-streaming "w:gz" path (which already delegates to GzipFile and was unaffected). (cherry picked from commit c6c060843b7635b5a1f82e0983d803eb0d185b5d) (cherry picked from commit d4b8d6668a84b8f93b0d2de13f142d3721a5dafb) Co-authored-by: Miss Islington (bot) <[email protected]> Co-authored-by: Harjoth Khara <[email protected]> files: A Misc/NEWS.d/next/Library/2026-06-20-20-24-03.gh-issue-133998.nkqhMz.rst M Lib/tarfile.py M Lib/test/test_tarfile.py diff --git a/Lib/tarfile.py b/Lib/tarfile.py index e4a6d7b3a0f00b..81573ebc5e149a 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -457,7 +457,12 @@ def _init_write_gz(self, compresslevel): -self.zlib.MAX_WBITS, self.zlib.DEF_MEM_LEVEL, 0) - timestamp = struct.pack("<L", int(time.time())) + mtime = time.time() + # gh-133998: substitute 0 for an out-of-range mtime and coerce to int, + # mirroring gzip (RFC 1952), so struct.pack cannot raise struct.error. + if not 0 <= mtime < 2**32: + mtime = 0 + timestamp = struct.pack("<L", int(mtime)) self.__write(b"\037\213\010\010" + timestamp + b"\002\377") if self.name.endswith(".gz"): self.name = self.name[:-3] diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 0e278c69f5898b..713e9d35818fe0 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -10,6 +10,7 @@ import re import warnings import stat +import time import unittest import unittest.mock @@ -1960,6 +1961,25 @@ def test_source_directory_not_leaked(self): payload = pathlib.Path(tmpname).read_text(encoding='latin-1') assert os.path.dirname(tmpname) not in payload + def test_create_with_out_of_range_clock(self): + # gh-133998: an out-of-range system clock (mtime defaulting to + # time.time()) is stored as 0 rather than raising struct.error. + for clock in (-1, 2**32): + with self.subTest(clock=clock): + with unittest.mock.patch('time.time', return_value=float(clock)): + tarfile.open(tmpname, self.mode).close() + with self.open(tmpname, 'r') as fobj: + fobj.read() + self.assertEqual(fobj.mtime, 0) + os_helper.unlink(tmpname) + + def test_create_without_mtime(self): + before = int(time.time()) + tarfile.open(tmpname, self.mode).close() + after = int(time.time()) + with self.open(tmpname, 'r') as fobj: + fobj.read() + self.assertTrue(before <= fobj.mtime <= after) class Bz2StreamWriteTest(Bz2Test, StreamWriteTest): decompressor = bz2.BZ2Decompressor if bz2 else None diff --git a/Misc/NEWS.d/next/Library/2026-06-20-20-24-03.gh-issue-133998.nkqhMz.rst b/Misc/NEWS.d/next/Library/2026-06-20-20-24-03.gh-issue-133998.nkqhMz.rst new file mode 100644 index 00000000000000..e7c03c662ec44f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-20-20-24-03.gh-issue-133998.nkqhMz.rst @@ -0,0 +1,4 @@ +Fix :func:`tarfile.open` in streaming write mode (``w|gz``) raising +:exc:`struct.error` when given an *mtime* outside the range of the gzip +header's 32-bit timestamp field. Out-of-range values are now stored as ``0``, +matching :mod:`gzip`. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
