Skip to content

Commit 80c8464

Browse files
vstinnermiss-islingtonadang1345serhiy-storchaka
authored
[3.14] gh-133998: Fix gzip file creation when time is out of range (#159592)
[3.15] gh-133998: Fix gzip file creation when time is out of range (GH-134278) (GH-150221) (cherry picked from commit 1daad8a) (cherry picked from commit b039d1b) Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Co-authored-by: adang1345 <adang1345@gmail.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent ff81651 commit 80c8464

4 files changed

Lines changed: 41 additions & 6 deletions

File tree

‎Doc/library/gzip.rst‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,13 @@ The module defines the following items:
103103
is no compression. The default is ``9``.
104104

105105
The optional *mtime* argument is the timestamp requested by gzip. The time
106-
is in Unix format, i.e., seconds since 00:00:00 UTC, January 1, 1970.
107-
If *mtime* is omitted or ``None``, the current time is used. Use *mtime* = 0
108-
to generate a compressed stream that does not depend on creation time.
106+
is in Unix format, i.e., seconds since 00:00:00 UTC, January 1, 1970. Set
107+
*mtime* to ``0`` to generate a compressed stream that does not depend on
108+
creation time. If *mtime* is omitted or ``None``, the current time is used;
109+
however, if the current time is outside the range 00:00:00 UTC, January 1,
110+
1970 through 06:28:15 UTC, February 7, 2106, or explicitly passed *mtime*
111+
argument is outside the range ``0`` to ``2**32-1``, then the value ``0``
112+
is used instead.
109113

110114
See below for the :attr:`mtime` attribute that is set when decompressing.
111115

‎Lib/gzip.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,10 @@ def __init__(self, filename=None, mode=None,
189189
190190
The optional mtime argument is the timestamp requested by gzip.
191191
The time is in Unix format, i.e., seconds since 00:00:00 UTC,
192-
January 1, 1970. If mtime is omitted or None, the current time
193-
is used. Use mtime = 0 to generate a compressed stream that does
194-
not depend on creation time.
192+
January 1, 1970. Set mtime to 0 to generate a compressed stream
193+
that does not depend on creation time. If mtime is omitted or None,
194+
the current time is used. If the resulting mtime is outside the
195+
range 0 to 2**32-1, then the value 0 is used instead.
195196
196197
"""
197198

@@ -299,6 +300,8 @@ def _write_gzip_header(self, compresslevel):
299300
mtime = self._write_mtime
300301
if mtime is None:
301302
mtime = time.time()
303+
if not 0 <= mtime < 2**32:
304+
mtime = 0
302305
write32u(self.fileobj, int(mtime))
303306
if compresslevel == _COMPRESS_LEVEL_BEST:
304307
xfl = b'\002'
@@ -634,6 +637,8 @@ def compress(data, compresslevel=_COMPRESS_LEVEL_BEST, *, mtime=0):
634637
gzip_data = zlib.compress(data, level=compresslevel, wbits=31)
635638
if mtime is None:
636639
mtime = time.time()
640+
if not 0 <= mtime < 2**32:
641+
mtime = 0
637642
# Reuse gzip header created by zlib, replace mtime and OS byte for
638643
# consistency.
639644
header = struct.pack("<4sLBB", gzip_data, int(mtime), gzip_data[8], 255)

‎Lib/test/test_gzip.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import unittest
1212
import warnings
1313
from subprocess import PIPE, Popen
14+
from unittest import mock
1415
from test.support import catch_unraisable_exception
1516
from test.support import import_helper
1617
from test.support import os_helper
@@ -351,6 +352,26 @@ def test_mtime(self):
351352
self.assertEqual(dataRead, data1)
352353
self.assertEqual(fRead.mtime, mtime)
353354

355+
def test_mtime_out_of_range(self):
356+
for mtime in (-1, 2**32):
357+
with gzip.GzipFile(self.filename, 'w', mtime=mtime) as fWrite:
358+
fWrite.write(data1)
359+
with gzip.GzipFile(self.filename) as fRead:
360+
fRead.read(1)
361+
self.assertEqual(fRead.mtime, 0)
362+
datac = gzip.compress(data1, mtime=mtime)
363+
with gzip.GzipFile(fileobj=io.BytesIO(datac)) as fRead:
364+
fRead.read(1)
365+
self.assertEqual(fRead.mtime, 0)
366+
367+
for mtime in (-1, 2**32):
368+
with mock.patch('time.time', return_value=float(mtime)):
369+
with gzip.GzipFile(self.filename, 'w') as fWrite:
370+
fWrite.write(data1)
371+
with gzip.GzipFile(self.filename) as fRead:
372+
fRead.read(1)
373+
self.assertEqual(fRead.mtime, 0)
374+
354375
def test_metadata(self):
355376
mtime = 123456789
356377

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix :exc:`struct.error` exception when creating a file with
2+
:class:`gzip.GzipFile` or compressing data with :func:`gzip.compress`
3+
if the system time is outside the range 00:00:00 UTC, January 1, 1970
4+
through 06:28:15 UTC, February 7, 2106, or explicitly passed *mtime*
5+
argument is outside the range ``0`` to ``2**32-1``.

0 commit comments

Comments
 (0)