]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-142781: Fix type confusion in zoneinfo weak cache (GH-142925) (GH-145419)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 3 Mar 2026 11:48:05 +0000 (12:48 +0100)
committerGitHub <noreply@github.com>
Tue, 3 Mar 2026 11:48:05 +0000 (12:48 +0100)
(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
Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst [new file with mode: 0644]
Modules/_zoneinfo.c

index 88d79b258cdf7a76635300d008033946da06bf86..8a58c7d68acf820de1997a8b7f374208d3f4bc5c 100644 (file)
@@ -1577,6 +1577,44 @@ class ZoneInfoCacheTest(TzPathUserMixin, ZoneInfoTestBase):
 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 (file)
index 0000000..772e057
--- /dev/null
@@ -0,0 +1,2 @@
+:mod:`zoneinfo`: fix a crash when instantiating :class:`~zoneinfo.ZoneInfo`
+objects for which the internal class-level cache is inconsistent.
index bfb86bdbf1a3f7ee1ee6cdfbf55703e21788b5d9..25f7eb7a44d8271ec2fe8114d5b432c98317b001 100644 (file)
@@ -335,6 +335,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);
@@ -342,7 +343,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);