Commit 1fb72d2a authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by GitHub

bpo-32137: The repr of deeply nested dict now raises a RecursionError (#4570)

instead of crashing due to a stack overflow.

This perhaps will fix similar problems in other extension types.
parent eea3cc1e
...@@ -53,10 +53,11 @@ class CommonTest(seq_tests.CommonTest): ...@@ -53,10 +53,11 @@ class CommonTest(seq_tests.CommonTest):
self.assertEqual(str(a2), "[0, 1, 2, [...], 3]") self.assertEqual(str(a2), "[0, 1, 2, [...], 3]")
self.assertEqual(repr(a2), "[0, 1, 2, [...], 3]") self.assertEqual(repr(a2), "[0, 1, 2, [...], 3]")
l0 = [] def test_repr_deep(self):
a = self.type2test([])
for i in range(sys.getrecursionlimit() + 100): for i in range(sys.getrecursionlimit() + 100):
l0 = [l0] a = self.type2test([a])
self.assertRaises(RecursionError, repr, l0) self.assertRaises(RecursionError, repr, a)
def test_print(self): def test_print(self):
d = self.type2test(range(200)) d = self.type2test(range(200))
......
# tests common to dict and UserDict # tests common to dict and UserDict
import unittest import unittest
import collections import collections
import sys
class BasicTestMappingProtocol(unittest.TestCase): class BasicTestMappingProtocol(unittest.TestCase):
...@@ -619,6 +620,14 @@ class TestHashMappingProtocol(TestMappingProtocol): ...@@ -619,6 +620,14 @@ class TestHashMappingProtocol(TestMappingProtocol):
d = self._full_mapping({1: BadRepr()}) d = self._full_mapping({1: BadRepr()})
self.assertRaises(Exc, repr, d) self.assertRaises(Exc, repr, d)
def test_repr_deep(self):
d = self._empty_mapping()
for i in range(sys.getrecursionlimit() + 100):
d0 = d
d = self._empty_mapping()
d[1] = d0
self.assertRaises(RecursionError, repr, d)
def test_eq(self): def test_eq(self):
self.assertEqual(self._empty_mapping(), self._empty_mapping()) self.assertEqual(self._empty_mapping(), self._empty_mapping())
self.assertEqual(self._full_mapping({1: 2}), self.assertEqual(self._full_mapping({1: 2}),
......
...@@ -468,6 +468,12 @@ class DictTest(unittest.TestCase): ...@@ -468,6 +468,12 @@ class DictTest(unittest.TestCase):
d = {1: BadRepr()} d = {1: BadRepr()}
self.assertRaises(Exc, repr, d) self.assertRaises(Exc, repr, d)
def test_repr_deep(self):
d = {}
for i in range(sys.getrecursionlimit() + 100):
d = {1: d}
self.assertRaises(RecursionError, repr, d)
def test_eq(self): def test_eq(self):
self.assertEqual({}, {}) self.assertEqual({}, {})
self.assertEqual({1: 2}, {1: 2}) self.assertEqual({1: 2}, {1: 2})
......
The repr of deeply nested dict now raises a RecursionError instead of
crashing due to a stack overflow.
...@@ -364,10 +364,7 @@ list_repr(PyListObject *v) ...@@ -364,10 +364,7 @@ list_repr(PyListObject *v)
goto error; goto error;
} }
if (Py_EnterRecursiveCall(" while getting the repr of a list"))
goto error;
s = PyObject_Repr(v->ob_item[i]); s = PyObject_Repr(v->ob_item[i]);
Py_LeaveRecursiveCall();
if (s == NULL) if (s == NULL)
goto error; goto error;
......
...@@ -463,7 +463,12 @@ PyObject_Repr(PyObject *v) ...@@ -463,7 +463,12 @@ PyObject_Repr(PyObject *v)
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
#endif #endif
/* It is possible for a type to have a tp_repr representation that loops
infinitely. */
if (Py_EnterRecursiveCall(" while getting the repr of an object"))
return NULL;
res = (*v->ob_type->tp_repr)(v); res = (*v->ob_type->tp_repr)(v);
Py_LeaveRecursiveCall();
if (res == NULL) if (res == NULL)
return NULL; return NULL;
if (!PyUnicode_Check(res)) { if (!PyUnicode_Check(res)) {
......
...@@ -303,10 +303,7 @@ tuplerepr(PyTupleObject *v) ...@@ -303,10 +303,7 @@ tuplerepr(PyTupleObject *v)
goto error; goto error;
} }
if (Py_EnterRecursiveCall(" while getting the repr of a tuple"))
goto error;
s = PyObject_Repr(v->ob_item[i]); s = PyObject_Repr(v->ob_item[i]);
Py_LeaveRecursiveCall();
if (s == NULL) if (s == NULL)
goto error; goto error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment