From bb21e28fd08f894ceff2405544a2f257d42b1354 Mon Sep 17 00:00:00 2001 From: Ammar Askar Date: Tue, 24 Aug 2021 05:13:32 -0400 Subject: bpo-34990: Treat the pyc header's mtime in compileall as an unsigned int (GH-19708) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Stéphane Wirtel --- Lib/compileall.py | 4 ++-- Lib/test/test_compileall.py | 23 ++++++++++++++++++++-- Lib/test/test_zipimport.py | 17 +++++++++------- .../2020-04-24-20-39-38.bpo-34990.3SmL9M.rst | 2 ++ 4 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-04-24-20-39-38.bpo-34990.3SmL9M.rst diff --git a/Lib/compileall.py b/Lib/compileall.py index 454465b..3e00477 100644 --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -221,8 +221,8 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, if not force: try: mtime = int(os.stat(fullname).st_mtime) - expect = struct.pack('<4sll', importlib.util.MAGIC_NUMBER, - 0, mtime) + expect = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, + 0, mtime & 0xFFFF_FFFF) for cfile in opt_cfiles.values(): with open(cfile, 'rb') as chandle: actual = chandle.read(12) diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 4612953..cc51b8c 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -80,9 +80,28 @@ class CompileallTestsBase: with open(self.bc_path, 'rb') as file: data = file.read(12) mtime = int(os.stat(self.source_path).st_mtime) - compare = struct.pack('<4sll', importlib.util.MAGIC_NUMBER, 0, mtime) + compare = struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, 0, + mtime & 0xFFFF_FFFF) return data, compare + def test_year_2038_mtime_compilation(self): + # Test to make sure we can handle mtimes larger than what a 32-bit + # signed number can hold as part of bpo-34990 + try: + os.utime(self.source_path, (2**32 - 1, 2**32 - 1)) + except (OverflowError, OSError): + self.skipTest("filesystem doesn't support timestamps near 2**32") + self.assertTrue(compileall.compile_file(self.source_path)) + + def test_larger_than_32_bit_times(self): + # This is similar to the test above but we skip it if the OS doesn't + # support modification times larger than 32-bits. + try: + os.utime(self.source_path, (2**35, 2**35)) + except (OverflowError, OSError): + self.skipTest("filesystem doesn't support large timestamps") + self.assertTrue(compileall.compile_file(self.source_path)) + def recreation_check(self, metadata): """Check that compileall recreates bytecode when the new metadata is used.""" @@ -101,7 +120,7 @@ class CompileallTestsBase: def test_mtime(self): # Test a change in mtime leads to a new .pyc. - self.recreation_check(struct.pack('<4sll', importlib.util.MAGIC_NUMBER, + self.recreation_check(struct.pack('<4sLL', importlib.util.MAGIC_NUMBER, 0, 1)) def test_magic_number(self): diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index 4e88902..c57f861 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -37,14 +37,9 @@ raise_src = 'def do_raise(): raise TypeError\n' def make_pyc(co, mtime, size): data = marshal.dumps(co) - if type(mtime) is type(0.0): - # Mac mtimes need a bit of special casing - if mtime < 0x7fffffff: - mtime = int(mtime) - else: - mtime = int(-0x100000000 + int(mtime)) pyc = (importlib.util.MAGIC_NUMBER + - struct.pack("