https://github.com/python/cpython/commit/e736c9602b0cc43d121fa4624d1fc6adb4f0a08f
commit: e736c9602b0cc43d121fa4624d1fc6adb4f0a08f
branch: 3.15
author: Jelle Zijlstra <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-10-24T13:44:26-07:00
summary:

[3.15] gh-159739: Keep the instance type alive during ABC subclass checks 
(GH-160198) (#160489)

When an instance advertises a __class__ different from its actual type, 
ABCMeta.__instancecheck__ performs two subclass checks. The first callback can 
change the instance's actual type and allow its old type to be collected before 
the second check, causing a crash. Keep the original type alive through both 
checks. Backport to 3.15 and 3.14.
(cherry picked from commit dcbc45356ea7ccc8038f40dd293f0e338f5ff977)

files:
A Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.yBc6Ei.rst
M Lib/test/test_abc.py
M Modules/_abc.c

diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py
index bdfad263db6fc0..f3e040ff051f21 100644
--- a/Lib/test/test_abc.py
+++ b/Lib/test/test_abc.py
@@ -7,10 +7,12 @@
 """Unit tests for abc.py."""
 
 import unittest
+import weakref
 
 import abc
 import _py_abc
 from inspect import isabstract
+from test.support import gc_collect
 
 def test_factory(abc_ABCMeta, abc_get_cache_token):
     class TestLegacyAPI(unittest.TestCase):
@@ -349,6 +351,33 @@ class C(str): pass
             self.assertIsSubclass(C, A)
             self.assertIsSubclass(C, (A,))
 
+        def test_instancecheck_class_changes_in_subclasscheck(self):
+            class Original:
+                def __getattribute__(self, name):
+                    if name == '__class__':
+                        return int
+                    return object.__getattribute__(self, name)
+
+            class Replacement:
+                pass
+
+            instance = Original()
+            original = weakref.ref(Original)
+            del Original
+
+            class Meta(abc_ABCMeta):
+                def __subclasscheck__(cls, candidate):
+                    if candidate is int:
+                        instance.__class__ = Replacement
+                        gc_collect()
+                        self.assertIsNotNone(original())
+                    return False
+
+            class Interface(metaclass=Meta):
+                pass
+
+            self.assertNotIsInstance(instance, Interface)
+
         def test_instancecheck_no_class(self):
             # gh-153772: __instancecheck__ must fall back to type(instance)
             # when the instance has no __class__, matching isinstance().
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.yBc6Ei.rst 
b/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.yBc6Ei.rst
new file mode 100644
index 00000000000000..e299599e9763f0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.yBc6Ei.rst
@@ -0,0 +1 @@
+Fix a possible crash in :class:`abc.ABCMeta` instance checks when a 
subclass-checking callback changes the instance’s class.
diff --git a/Modules/_abc.c b/Modules/_abc.c
index bca9066d340ee4..054cff517115f5 100644
--- a/Modules/_abc.c
+++ b/Modules/_abc.c
@@ -664,9 +664,12 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject 
*self,
                                            subclass);
         goto end;
     }
+    /* A subclass check can change the instance's actual type. */
+    Py_INCREF(subtype);
     result = PyObject_CallMethodOneArg(self, &_Py_ID(__subclasscheck__),
                                        subclass);
     if (result == NULL) {
+        Py_DECREF(subtype);
         goto end;
     }
 
@@ -684,6 +687,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject 
*self,
     default:
         Py_UNREACHABLE();
     }
+    Py_DECREF(subtype);
 
 end:
     Py_XDECREF(impl);

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to