Skip to content

Commit d1f0ccc

Browse files
miss-islingtonAlexey Izbyshev
andauthored
bpo-34441: Fix ABC.__subclasscheck__ crash on classes with invalid __subclasses__ (GH-8835)
The missing NULL check was reported by Svace static analyzer. (cherry picked from commit cdbf50c) Co-authored-by: Alexey Izbyshev <[email protected]>
1 parent 2011dd7 commit d1f0ccc

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

Lib/test/test_abc.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,36 @@ class C:
410410
with self.assertRaises(TypeError):
411411
issubclass(C(), A)
412412

413+
# bpo-34441: Check that issubclass() doesn't crash on bogus
414+
# classes.
415+
bogus_subclasses = [
416+
None,
417+
lambda x: [],
418+
lambda: 42,
419+
lambda: [42],
420+
]
421+
422+
for i, func in enumerate(bogus_subclasses):
423+
class S(metaclass=abc_ABCMeta):
424+
__subclasses__ = func
425+
426+
with self.subTest(i=i):
427+
with self.assertRaises(TypeError):
428+
issubclass(int, S)
429+
430+
# Also check that issubclass() propagates exceptions raised by
431+
# __subclasses__.
432+
exc_msg = "exception from __subclasses__"
433+
434+
def raise_exc():
435+
raise Exception(exc_msg)
436+
437+
class S(metaclass=abc_ABCMeta):
438+
__subclasses__ = raise_exc
439+
440+
with self.assertRaisesRegex(Exception, exc_msg):
441+
issubclass(int, S)
442+
413443
def test_all_new_methods_are_called(self):
414444
class A(metaclass=abc_ABCMeta):
415445
pass
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix crash when an ``ABC``-derived class with invalid ``__subclasses__`` is
2+
passed as the second argument to :func:`issubclass()`. Patch by Alexey
3+
Izbyshev.

Modules/_abc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,9 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
665665

666666
/* 6. Check if it's a subclass of a subclass (recursive). */
667667
subclasses = PyObject_CallMethod(self, "__subclasses__", NULL);
668+
if (subclasses == NULL) {
669+
goto end;
670+
}
668671
if (!PyList_Check(subclasses)) {
669672
PyErr_SetString(PyExc_TypeError, "__subclasses__() must return a list");
670673
goto end;

0 commit comments

Comments
 (0)