Skip to content

bpo-34990: Treat the pyc header's mtime in compileall as an unsigned int #19708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Lib/compileall.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,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)
Expand Down
23 changes: 21 additions & 2 deletions Lib/test/test_compileall.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,28 @@ def timestamp_metadata(self):
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."""
Expand All @@ -76,7 +95,7 @@ def recreation_check(self, metadata):

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):
Expand Down
17 changes: 10 additions & 7 deletions Lib/test/test_zipimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,9 @@ def get_file():

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("<iii", 0, int(mtime), size & 0xFFFFFFFF) + data)
struct.pack("<iLL", 0,
int(mtime) & 0xFFFF_FFFF, size & 0xFFFF_FFFF) + data)
return pyc

def module_path_to_dotted_name(path):
Expand Down Expand Up @@ -253,6 +248,14 @@ def testBadMTime(self):
TESTMOD + pyc_ext: (NOW, badtime_pyc)}
self.doTest(".py", files, TESTMOD)

def test2038MTime(self):
# Make sure we can handle mtimes larger than what a 32-bit signed number
# can hold.
twenty_thirty_eight_pyc = make_pyc(test_co, 2**32 - 1, len(test_src))
files = {TESTMOD + ".py": (NOW, test_src),
TESTMOD + pyc_ext: (NOW, twenty_thirty_eight_pyc)}
self.doTest(".py", files, TESTMOD)

def testPackage(self):
packdir = TESTPACK + os.sep
files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed a Y2k38 bug in the compileall module where it would fail to compile
files with a modification time after the year 2038.