From bf0624160668e0593272866977896c93956b22e1 Mon Sep 17 00:00:00 2001 From: superboy-zjc <1826599908@qq.com> Date: Thu, 18 Dec 2025 01:17:47 -0800 Subject: [PATCH] gh-142781: Fix type confusion in zoneinfo weak cache Validate the types returned from _weak_cache.get() and _weak_cache.setdefault() to prevent type confusion when a ZoneInfo subclass provides a misbehaving cache implementation. --- Lib/test/test_zoneinfo/test_zoneinfo.py | 27 +++++++++++++++++++ ...-12-18-00-14-16.gh-issue-142781.gcOeYF.rst | 3 +++ Modules/_zoneinfo.c | 18 +++++++++++++ 3 files changed, 48 insertions(+) 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 8f3ca59c9ef5ed..fae7c6c264f28c 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1575,6 +1575,33 @@ class EvilZoneInfo(self.klass): class CZoneInfoCacheTest(ZoneInfoCacheTest): module = c_zoneinfo + def test_weak_cache_get_type_confusion(self): + class EvilCache: + def get(self, key, default=None): + return 1337 + + class EvilZoneInfo(self.klass): + pass + + EvilZoneInfo._weak_cache = EvilCache() + + with self.assertRaises(TypeError): + EvilZoneInfo("America/Los_Angeles") + + def test_weak_cache_setdefault_type_confusion(self): + class EvilCache: + def get(self, key, default=None): + return default + def setdefault(self, key, value): + return 1337 + + class EvilZoneInfo(self.klass): + pass + + EvilZoneInfo._weak_cache = EvilCache() + + with self.assertRaises(TypeError): + EvilZoneInfo("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..27b6423f7279a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst @@ -0,0 +1,3 @@ +Fix a type confusion in zoneinfo where a malicious override of _weak_cache +in a zoneinfo subclass can cause memory corruption and crash the +interpreter. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index e07dfd19efa06d..341b69602fccd9 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -327,6 +327,15 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) return NULL; } + if (instance != Py_None && !PyObject_TypeCheck(instance, type)) { + const char *e = "%s._weak_cache.get() returned %s, expected %s"; + PyErr_Format(PyExc_TypeError, e, + type->tp_name, Py_TYPE(instance)->tp_name, type->tp_name); + Py_DECREF(instance); + Py_DECREF(weak_cache); + return NULL; + } + if (instance == Py_None) { Py_DECREF(instance); PyObject *tmp = zoneinfo_new_instance(state, type, key); @@ -342,6 +351,15 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) Py_DECREF(weak_cache); return NULL; } + + if (!PyObject_TypeCheck(instance, type)) { + const char *e = "%s._weak_cache.setdefault() returned %s, expected %s"; + PyErr_Format(PyExc_TypeError, e, + type->tp_name, Py_TYPE(instance)->tp_name, type->tp_name); + Py_DECREF(instance); + Py_DECREF(weak_cache); + return NULL; + } ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE; }