From 29d35c2efff6ad1e3f4c1fbccf62ac24f7624dd3 Mon Sep 17 00:00:00 2001 From: zhong <60600792+superboy-zjc@users.noreply.github.com> Date: Mon, 2 Mar 2026 20:30:38 +0800 Subject: [PATCH] gh-142781: Fix type confusion in zoneinfo weak cache (GH-142925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit b611db491d16ebbb4c833e9a184bb987e41f9fbe) Co-authored-by: zhong <60600792+superboy-zjc@users.noreply.github.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Lib/test/test_zoneinfo/test_zoneinfo.py | 38 +++++++++++++++++++ ...-12-18-00-14-16.gh-issue-142781.gcOeYF.rst | 2 + Modules/_zoneinfo.c | 11 +++++- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 0f72050be4e92d..588244c8adc3fa 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1577,6 +1577,44 @@ class EvilZoneInfo(self.klass): class CZoneInfoCacheTest(ZoneInfoCacheTest): module = c_zoneinfo + def test_inconsistent_weak_cache_get(self): + class Cache: + def get(self, key, default=None): + return 1337 + + class ZI(self.klass): + pass + # Class attribute must be set after class creation + # to override zoneinfo.ZoneInfo.__init_subclass__. + ZI._weak_cache = Cache() + + with self.assertRaises(RuntimeError) as te: + ZI("America/Los_Angeles") + self.assertEqual( + str(te.exception), + "Unexpected instance of int in ZI weak cache for key 'America/Los_Angeles'" + ) + + def test_inconsistent_weak_cache_setdefault(self): + class Cache: + def get(self, key, default=None): + return default + def setdefault(self, key, value): + return 1337 + + class ZI(self.klass): + pass + # Class attribute must be set after class creation + # to override zoneinfo.ZoneInfo.__init_subclass__. + ZI._weak_cache = Cache() + + with self.assertRaises(RuntimeError) as te: + ZI("America/Los_Angeles") + self.assertEqual( + str(te.exception), + "Unexpected instance of int in ZI weak cache for key 'America/Los_Angeles'" + ) + class ZoneInfoPickleTest(TzPathUserMixin, ZoneInfoTestBase): module = py_zoneinfo diff --git a/Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst b/Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst new file mode 100644 index 00000000000000..772e05766c5c69 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst @@ -0,0 +1,2 @@ +:mod:`zoneinfo`: fix a crash when instantiating :class:`~zoneinfo.ZoneInfo` +objects for which the internal class-level cache is inconsistent. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 2cb48d91494957..a08c47e10d1e12 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -330,6 +330,7 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) return NULL; } + ((PyZoneInfo_ZoneInfo *)tmp)->source = SOURCE_CACHE; instance = PyObject_CallMethod(weak_cache, "setdefault", "OO", key, tmp); Py_DECREF(tmp); @@ -337,7 +338,15 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) Py_DECREF(weak_cache); return NULL; } - ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE; + } + + if (!PyObject_TypeCheck(instance, type)) { + PyErr_Format(PyExc_RuntimeError, + "Unexpected instance of %T in %s weak cache for key %R", + instance, _PyType_Name(type), key); + Py_DECREF(instance); + Py_DECREF(weak_cache); + return NULL; } update_strong_cache(state, type, key, instance);