Skip to content

Commit 9b0b726

Browse files
committed
gh-155452: Prevent recursion crash in dir()
1 parent 5107fd7 commit 9b0b726

3 files changed

Lines changed: 36 additions & 14 deletions

File tree

Lib/test/test_builtin.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,20 @@ def __init__(self):
700700
f = Foo()
701701
self.assertIn("y", dir(f))
702702

703+
# dir(obj) - cyclic __bases__ must raise RecursionError
704+
class Fake:
705+
pass
706+
707+
a = Fake()
708+
a.__bases__ = (a,)
709+
710+
class C:
711+
@property
712+
def __class__(self):
713+
return a
714+
715+
self.assertRaises(RecursionError, dir, C())
716+
703717
# dir(obj_no__dict__)
704718
class Foo(object):
705719
__slots__ = []
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash in :func:`dir` when an object's ``__class__`` has cyclic
2+
``__bases__``.

Objects/typeobject.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "Python.h"
44
#include "pycore_abstract.h" // _PySequence_IterSearch()
55
#include "pycore_call.h" // _PyObject_VectorcallTstate()
6+
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
67
#include "pycore_code.h" // CO_FAST_FREE
78
#include "pycore_descrobject.h" // _PyMember_GetOffset()
89
#include "pycore_dict.h" // _PyDict_KeysSize()
@@ -6949,22 +6950,27 @@ merge_class_dict(PyObject *dict, PyObject *aclass)
69496950
Py_DECREF(bases);
69506951
return -1;
69516952
}
6952-
else {
6953-
for (i = 0; i < n; i++) {
6954-
int status;
6955-
PyObject *base = PySequence_GetItem(bases, i);
6956-
if (base == NULL) {
6957-
Py_DECREF(bases);
6958-
return -1;
6959-
}
6960-
status = merge_class_dict(dict, base);
6961-
Py_DECREF(base);
6962-
if (status < 0) {
6963-
Py_DECREF(bases);
6964-
return -1;
6965-
}
6953+
if (_Py_EnterRecursiveCall(" in __bases__")) {
6954+
Py_DECREF(bases);
6955+
return -1;
6956+
}
6957+
for (i = 0; i < n; i++) {
6958+
int status;
6959+
PyObject *base = PySequence_GetItem(bases, i);
6960+
if (base == NULL) {
6961+
_Py_LeaveRecursiveCall();
6962+
Py_DECREF(bases);
6963+
return -1;
6964+
}
6965+
status = merge_class_dict(dict, base);
6966+
Py_DECREF(base);
6967+
if (status < 0) {
6968+
_Py_LeaveRecursiveCall();
6969+
Py_DECREF(bases);
6970+
return -1;
69666971
}
69676972
}
6973+
_Py_LeaveRecursiveCall();
69686974
Py_DECREF(bases);
69696975
}
69706976
return 0;

0 commit comments

Comments
 (0)