Skip to content

Commit 605d611

Browse files
authored
[3.14] gh-159739: Keep the instance type alive during ABC subclass checks (GH-160198) (#160488)
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 dcbc453)
1 parent 25dde93 commit 605d611

3 files changed

Lines changed: 34 additions & 0 deletions

File tree

‎Lib/test/test_abc.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
"""Unit tests for abc.py."""
88

99
import unittest
10+
import weakref
1011

1112
import abc
1213
import _py_abc
1314
from inspect import isabstract
15+
from test.support import gc_collect
1416

1517
def test_factory(abc_ABCMeta, abc_get_cache_token):
1618
class TestLegacyAPI(unittest.TestCase):
@@ -349,6 +351,33 @@ class C(str): pass
349351
self.assertIsSubclass(C, A)
350352
self.assertIsSubclass(C, (A,))
351353

354+
def test_instancecheck_class_changes_in_subclasscheck(self):
355+
class Original:
356+
def __getattribute__(self, name):
357+
if name == '__class__':
358+
return int
359+
return object.__getattribute__(self, name)
360+
361+
class Replacement:
362+
pass
363+
364+
instance = Original()
365+
original = weakref.ref(Original)
366+
del Original
367+
368+
class Meta(abc_ABCMeta):
369+
def __subclasscheck__(cls, candidate):
370+
if candidate is int:
371+
instance.__class__ = Replacement
372+
gc_collect()
373+
self.assertIsNotNone(original())
374+
return False
375+
376+
class Interface(metaclass=Meta):
377+
pass
378+
379+
self.assertNotIsInstance(instance, Interface)
380+
352381
def test_instancecheck_no_class(self):
353382
# gh-153772: __instancecheck__ must fall back to type(instance)
354383
# when the instance has no __class__, matching isinstance().
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a possible crash in :class:`abc.ABCMeta` instance checks when a subclass-checking callback changes the instance’s class.

‎Modules/_abc.c‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,9 +661,12 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
661661
subclass);
662662
goto end;
663663
}
664+
/* A subclass check can change the instance's actual type. */
665+
Py_INCREF(subtype);
664666
result = PyObject_CallMethodOneArg(self, &_Py_ID(__subclasscheck__),
665667
subclass);
666668
if (result == NULL) {
669+
Py_DECREF(subtype);
667670
goto end;
668671
}
669672

@@ -681,6 +684,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
681684
default:
682685
Py_UNREACHABLE();
683686
}
687+
Py_DECREF(subtype);
684688

685689
end:
686690
Py_XDECREF(impl);

0 commit comments

Comments
 (0)