Skip to content

Commit 1e29dce

Browse files
bpo-30570: Use Py_EnterRecursiveCall() in issubclass() (GH-29048) (GH-29178)
* Use Py_EnterRecursiveCall() in issubclass() Reviewed-by: Gregory P. Smith <[email protected]> [Google] (cherry picked from commit 423fa1c) Co-authored-by: Dennis Sweeney <[email protected]>
1 parent effb72f commit 1e29dce

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

Lib/test/test_isinstance.py

+30
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,36 @@ def __bases__(self):
281281
self.assertRaises(RecursionError, issubclass, int, X())
282282
self.assertRaises(RecursionError, isinstance, 1, X())
283283

284+
def test_infinite_recursion_via_bases_tuple(self):
285+
"""Regression test for bpo-30570."""
286+
class Failure(object):
287+
def __getattr__(self, attr):
288+
return (self, None)
289+
290+
with self.assertRaises(RecursionError):
291+
issubclass(Failure(), int)
292+
293+
def test_infinite_cycle_in_bases(self):
294+
"""Regression test for bpo-30570."""
295+
class X:
296+
@property
297+
def __bases__(self):
298+
return (self, self, self)
299+
self.assertRaises(RecursionError, issubclass, X(), int)
300+
301+
def test_infinitely_many_bases(self):
302+
"""Regression test for bpo-30570."""
303+
class X:
304+
def __getattr__(self, attr):
305+
self.assertEqual(attr, "__bases__")
306+
class A:
307+
pass
308+
class B:
309+
pass
310+
A.__getattr__ = B.__getattr__ = X.__getattr__
311+
return (A(), B())
312+
self.assertRaises(RecursionError, issubclass, X(), int)
313+
284314

285315
def blowstack(fxn, arg, compare_to):
286316
# Make sure that calling isinstance with a deeply nested tuple for its
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a crash in ``issubclass()`` from infinite recursion when searching pathological ``__bases__`` tuples.

Objects/abstract.c

+14-6
Original file line numberDiff line numberDiff line change
@@ -2417,14 +2417,22 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
24172417
derived = PyTuple_GET_ITEM(bases, 0);
24182418
continue;
24192419
}
2420-
for (i = 0; i < n; i++) {
2421-
r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls);
2422-
if (r != 0)
2423-
break;
2424-
}
2420+
break;
2421+
}
2422+
assert(n >= 2);
2423+
if (Py_EnterRecursiveCall(" in __issubclass__")) {
24252424
Py_DECREF(bases);
2426-
return r;
2425+
return -1;
24272426
}
2427+
for (i = 0; i < n; i++) {
2428+
r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls);
2429+
if (r != 0) {
2430+
break;
2431+
}
2432+
}
2433+
Py_LeaveRecursiveCall();
2434+
Py_DECREF(bases);
2435+
return r;
24282436
}
24292437

24302438
static int

0 commit comments

Comments
 (0)