Skip to content

Commit 423fa1c

Browse files
authored
bpo-30570: Use Py_EnterRecursiveCall() in issubclass() (GH-29048)
* Use Py_EnterRecursiveCall() in issubclass() Reviewed-by: Gregory P. Smith <[email protected]> [Google]
1 parent f6e8b80 commit 423fa1c

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

Lib/test/test_isinstance.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,36 @@ def __bases__(self):
313313
self.assertRaises(RecursionError, issubclass, int, X())
314314
self.assertRaises(RecursionError, isinstance, 1, X())
315315

316+
def test_infinite_recursion_via_bases_tuple(self):
317+
"""Regression test for bpo-30570."""
318+
class Failure(object):
319+
def __getattr__(self, attr):
320+
return (self, None)
321+
322+
with self.assertRaises(RecursionError):
323+
issubclass(Failure(), int)
324+
325+
def test_infinite_cycle_in_bases(self):
326+
"""Regression test for bpo-30570."""
327+
class X:
328+
@property
329+
def __bases__(self):
330+
return (self, self, self)
331+
self.assertRaises(RecursionError, issubclass, X(), int)
332+
333+
def test_infinitely_many_bases(self):
334+
"""Regression test for bpo-30570."""
335+
class X:
336+
def __getattr__(self, attr):
337+
self.assertEqual(attr, "__bases__")
338+
class A:
339+
pass
340+
class B:
341+
pass
342+
A.__getattr__ = B.__getattr__ = X.__getattr__
343+
return (A(), B())
344+
self.assertRaises(RecursionError, issubclass, X(), int)
345+
316346

317347
def blowstack(fxn, arg, compare_to):
318348
# Make sure that calling isinstance with a deeply nested tuple for its
Lines changed: 1 addition & 0 deletions
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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2557,14 +2557,22 @@ abstract_issubclass(PyObject *derived, PyObject *cls)
25572557
derived = PyTuple_GET_ITEM(bases, 0);
25582558
continue;
25592559
}
2560-
for (i = 0; i < n; i++) {
2561-
r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls);
2562-
if (r != 0)
2563-
break;
2564-
}
2560+
break;
2561+
}
2562+
assert(n >= 2);
2563+
if (Py_EnterRecursiveCall(" in __issubclass__")) {
25652564
Py_DECREF(bases);
2566-
return r;
2565+
return -1;
25672566
}
2567+
for (i = 0; i < n; i++) {
2568+
r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls);
2569+
if (r != 0) {
2570+
break;
2571+
}
2572+
}
2573+
Py_LeaveRecursiveCall();
2574+
Py_DECREF(bases);
2575+
return r;
25682576
}
25692577

25702578
static int

0 commit comments

Comments
 (0)