From e8a6d1680bc9436bf220b9514421ac4cdfee8759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20=C3=87elik?= Date: Sun, 28 Dec 2025 17:48:43 +0300 Subject: [PATCH] gh-143241: Fix infinite loop in `zoneinfo._common.load_data` (GH-143243) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correctly reject truncated TZif files in `ZoneInfo.from_file`. --------- (cherry picked from commit 3ca1f2a370e44874d0dc8c82a01465e0171bec5c) Co-authored-by: Fatih Çelik Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/zoneinfo.rst | 3 +++ Lib/test/test_zoneinfo/test_zoneinfo.py | 2 ++ Lib/zoneinfo/_common.py | 9 ++++----- .../2025-12-28-13-49-06.gh-issue-143241.5H4b8d.rst | 2 ++ 4 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-28-13-49-06.gh-issue-143241.5H4b8d.rst diff --git a/Doc/library/zoneinfo.rst b/Doc/library/zoneinfo.rst index d95260896f75e1..efd28b31ef2a98 100644 --- a/Doc/library/zoneinfo.rst +++ b/Doc/library/zoneinfo.rst @@ -206,6 +206,9 @@ The ``ZoneInfo`` class has two alternate constructors: Objects created via this constructor cannot be pickled (see `pickling`_). + :exc:`ValueError` is raised if the data read from *file_obj* is not a valid + TZif file. + .. classmethod:: ZoneInfo.no_cache(key) An alternate constructor that bypasses the constructor's cache. It is diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index df7f0d82a2eac9..88d79b258cdf7a 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -252,6 +252,8 @@ def test_bad_zones(self): bad_zones = [ b"", # Empty file b"AAAA3" + b" " * 15, # Bad magic + # Truncated V2 file (should not loop indefinitely) + b"TZif2" + (b"\x00" * 39) + b"TZif2" + (b"\x00" * 39) + b"\n" + b"Part", ] for bad_zone in bad_zones: diff --git a/Lib/zoneinfo/_common.py b/Lib/zoneinfo/_common.py index 03cc42149f9b74..59f3f0ce853f74 100644 --- a/Lib/zoneinfo/_common.py +++ b/Lib/zoneinfo/_common.py @@ -118,11 +118,10 @@ def get_abbr(idx): c = fobj.read(1) # Should be \n assert c == b"\n", c - tz_bytes = b"" - while (c := fobj.read(1)) != b"\n": - tz_bytes += c - - tz_str = tz_bytes + line = fobj.readline() + if not line.endswith(b"\n"): + raise ValueError("Invalid TZif file: unexpected end of file") + tz_str = line.rstrip(b"\n") else: tz_str = None diff --git a/Misc/NEWS.d/next/Library/2025-12-28-13-49-06.gh-issue-143241.5H4b8d.rst b/Misc/NEWS.d/next/Library/2025-12-28-13-49-06.gh-issue-143241.5H4b8d.rst new file mode 100644 index 00000000000000..7170a06015ee7c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-28-13-49-06.gh-issue-143241.5H4b8d.rst @@ -0,0 +1,2 @@ +:mod:`zoneinfo`: fix infinite loop in :meth:`ZoneInfo.from_file +` when parsing a malformed TZif file. Patch by Fatih Celik.