Author serhiy.storchaka
Recipients serhiy.storchaka
Date 2017-11-26.08:53:30
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1511686410.56.0.213398074469.issue32137@psf.upfronthosting.co.za>
In-reply-to
Content
repr() of deeply nested dicts and dictviews can cause a stack overflow.

>>> x = {}
>>> for i in range(100000):
...     x = {1: x}
... 
>>> repr(x)
Segmentation fault

>>> x = {}
>>> for i in range(100000):
...     x = {1: x}
... 
>>> repr(x.values())
Segmentation fault

repr() of virtually all other deeply nested collections are guarded by using Py_EnterRecursiveCall().

>>> x = ()
>>> for i in range(100000):
...     x = (x,)
... 
>>> repr(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded while getting the repr of a tuple

>>> x = []
>>> for i in range(100000):
...     x = [x]
... 
>>> repr(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded while getting the repr of a list

>>> x = frozenset()
>>> for i in range(100000):
...     x = frozenset({x})
... 
>>> repr(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded while getting the repr of a list

>>> from collections import OrderedDict
>>> x = {}
>>> for i in range(100000):
...     x = OrderedDict({1: x})
... 
>>> repr(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded while getting the repr of a list

>>> from collections import deque
>>> x = deque()
>>> for i in range(100000):
...     x = deque([x])
... 
>>> repr(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RecursionError: maximum recursion depth exceeded while getting the repr of a list

But the case of infinite recursion already is handled:

>>> x = {}
>>> x[1] = x
>>> repr(x)
'{1: {...}}'

Only finite but very deep recursion causes a crash.

The one possible solution -- add Py_EnterRecursiveCall() in the implementation of dict.__repr__(), like it already is added in list.__repr__() and tuple.__repr__().

The more general solution -- add Py_EnterRecursiveCall() in PyObject_Repr(). In any case it is called before calling PyObject_Repr() for every item in the repr of most collections except dict. And it is already added in PyObject_Str(). Therefore adding it will not add much overhead, but can handle more corner cases.

See also issue18533 and issue25240.
History
Date User Action Args
2017-11-26 08:53:30serhiy.storchakasetrecipients: + serhiy.storchaka
2017-11-26 08:53:30serhiy.storchakasetmessageid: <1511686410.56.0.213398074469.issue32137@psf.upfronthosting.co.za>
2017-11-26 08:53:30serhiy.storchakalinkissue32137 messages
2017-11-26 08:53:30serhiy.storchakacreate