Skip to content

Commit e736c96

Browse files
authored
[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 dcbc453)
1 parent 5c4a4db commit e736c96

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
@@ -664,9 +664,12 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
664664
subclass);
665665
goto end;
666666
}
667+
/* A subclass check can change the instance's actual type. */
668+
Py_INCREF(subtype);
667669
result = PyObject_CallMethodOneArg(self, &_Py_ID(__subclasscheck__),
668670
subclass);
669671
if (result == NULL) {
672+
Py_DECREF(subtype);
670673
goto end;
671674
}
672675

@@ -684,6 +687,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
684687
default:
685688
Py_UNREACHABLE();
686689
}
690+
Py_DECREF(subtype);
687691

688692
end:
689693
Py_XDECREF(impl);

0 commit comments

Comments
 (0)