unarchive: Clamp zip timestamps on 32-bit time_t (#84409)

Clamp zip timestamps to representible values when unpacking zip files on
platforms that use 32-bit time_t (e.g.  Debian i386). This is a
non-issue in practice (in 2024), but should allow the test suite to pass
on Debian i386.

We use a round value of 2038-01-01 00:00:00 for simplicity, and to avoid
running into timezone offsets closer to the actual limit.

MR #81520 introduced sanity-checking tests that used dates not
representable with a 32-bit time_t.
pull/82263/head
Stefano Rivera 1 year ago committed by GitHub
parent af2bb2c182
commit d500354798
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,3 @@
---
bugfixes:
- unarchive - Clamp timestamps from beyond y2038 to representible values when unpacking zip files on platforms that use 32-bit time_t (e.g. Debian i386).

@ -241,6 +241,7 @@ uid:
import binascii
import codecs
import ctypes
import fnmatch
import grp
import os
@ -262,6 +263,13 @@ from ansible.module_utils.urls import fetch_file
from shlex import quote
from zipfile import BadZipFile
try:
from functools import cache
except ImportError:
# Python < 3.9
from functools import lru_cache
cache = lru_cache(maxsize=None)
# String from tar that shows the tar contents are different from the
# filesystem
OWNER_DIFF_RE = re.compile(r': Uid differs$')
@ -279,6 +287,18 @@ CONTENT_DIFF_RE = re.compile(r': Contents differ$')
SIZE_DIFF_RE = re.compile(r': Size differs$')
@cache
def _y2038_impacted():
"""Determine if the system has 64-bit time_t."""
if hasattr(ctypes, "c_time_t"): # Python >= 3.12
return ctypes.sizeof(ctypes.c_time_t) < 8
try:
time.gmtime(2**31)
except OverflowError:
return True
return False
def crc32(path, buffer_size):
""" Return a CRC32 checksum of a file """
@ -414,6 +434,8 @@ class ZipArchive(object):
try:
if int(match.groups()[0]) < 1980:
date_time = epoch_date_time
elif int(match.groups()[0]) >= 2038 and _y2038_impacted():
date_time = (2038, 1, 1, 0, 0, 0, 0, 0, 0)
elif int(match.groups()[0]) > 2107:
date_time = (2107, 12, 31, 23, 59, 59, 0, 0, 0)
else:

@ -14,6 +14,14 @@ def fake_ansible_module():
return FakeAnsibleModule()
def max_zip_timestamp():
"""Return the max clamp value that will be selected."""
try:
return time.mktime(time.struct_time((2107, 12, 31, 23, 59, 59, 0, 0, 0)))
except OverflowError:
return time.mktime(time.struct_time((2038, 1, 1, 0, 0, 0, 0, 0, 0)))
class FakeAnsibleModule:
def __init__(self):
self.params = {}
@ -68,7 +76,7 @@ class TestCaseZipArchive:
),
pytest.param(
"21081231.000000",
time.mktime(time.struct_time((2107, 12, 31, 23, 59, 59, 0, 0, 0))),
max_zip_timestamp(),
id="invalid-year-2108",
),
pytest.param(

Loading…
Cancel
Save