]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-142781: Fix type confusion in zoneinfo weak cache (GH-142925)
authorzhong <60600792+superboy-zjc@users.noreply.github.com>
Mon, 2 Mar 2026 12:30:38 +0000 (20:30 +0800)
committerGitHub <noreply@github.com>
Mon, 2 Mar 2026 12:30:38 +0000 (13:30 +0100)
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 581072d0701d65feeabe0133bc8f794c4c599940..a5dea802a9898dd1437399c65d4a357d6fa0b454 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 e07dfd19efa06d605cce16599b3c333bf794099a..39671d1ab51dfa58711554f3dba387e478657c22 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);