diff -r 57c6435ca03b Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Tue Mar 19 13:27:24 2013 +0200 +++ b/Lib/test/test_builtin.py Tue Mar 19 20:08:58 2013 +0200 @@ -636,6 +636,16 @@ f2 = filter(filter_char, "abcdeabcde") self.check_iter_pickle(f1, list(f2)) + def test_filter_deep_recursion(self): + # Issue #14010 + it = (0, 1) + for _ in range(1000000): + it = filter(bool, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + def test_getattr(self): self.assertTrue(getattr(sys, 'stdout') is sys.stdout) self.assertRaises(TypeError, getattr, sys, 1) @@ -834,6 +844,16 @@ m2 = map(map_char, "Is this the real life?") self.check_iter_pickle(m1, list(m2)) + def test_map_deep_recursion(self): + # Issue #14010 + it = (0, 1) + for _ in range(1000000): + it = map(int, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + def test_max(self): self.assertEqual(max('123123'), '3') self.assertEqual(max(1, 2, 3), 3) @@ -1407,6 +1427,16 @@ z1 = zip(a, b) self.check_iter_pickle(z1, t) + def test_zip_deep_recursion(self): + # Issue #14010 + it = (0, 1) + for _ in range(1000000): + it = zip(it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + def test_format(self): # Test the basic machinery of the format() builtin. Don't test # the specifics of the various formatters @@ -1564,8 +1594,40 @@ data = 'The quick Brown fox Jumped over The lazy Dog'.split() self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0) +class TestRecursionLimit(unittest.TestCase): + # Issue #14010 + recursionlimit = sys.getrecursionlimit() + + def test_filter(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = filter(bool, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_map(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = map(int, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_zip(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = zip(it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_main(verbose=None): - test_classes = (BuiltinTest, TestSorted) + test_classes = (BuiltinTest, TestSorted, TestRecursionLimit) run_unittest(*test_classes) diff -r 57c6435ca03b Lib/test/test_itertools.py --- a/Lib/test/test_itertools.py Tue Mar 19 13:27:24 2013 +0200 +++ b/Lib/test/test_itertools.py Tue Mar 19 20:08:58 2013 +0200 @@ -1808,6 +1808,121 @@ self.assertNotIn("does not take keyword arguments", err.args[0]) +class TestRecursionLimit(unittest.TestCase): + # Issue #14010 + recursionlimit = sys.getrecursionlimit() + + def test_accumulate(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = accumulate(it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_chain(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = chain(it, ()) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_compress(self): + data = (0, 1) + selectors = (True, True) + it = data + for _ in range(self.recursionlimit): + it = compress(it, selectors) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + it = selectors + for _ in range(self.recursionlimit): + it = compress(data, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_cycle(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = cycle(it) + with self.assertRaises(RuntimeError): + for _ in range(3): + next(it) + del it + + def test_dropwhile(self): + it = (0, 1, 0) + for _ in range(self.recursionlimit): + it = dropwhile(bool, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_filterfalse(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = filterfalse(bool, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_groupby(self): + key = operator.itemgetter(0) + it = ((0, []), (1, [])) + for _ in range(self.recursionlimit): + it = groupby(it, key) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_islice(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = islice(it, 2) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_starmap(self): + it = 'ab' + for _ in range(self.recursionlimit): + it = starmap(tuple, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_takewhile(self): + it = (1, 0) + for _ in range(self.recursionlimit): + it = takewhile(bool, it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + def test_zip_longest(self): + it = (0, 1) + for _ in range(self.recursionlimit): + it = zip_longest(it) + with self.assertRaises(RuntimeError): + for _ in it: + pass + del it + + libreftest = """ Doctest for examples in the library reference: libitertools.tex @@ -2042,7 +2157,7 @@ def test_main(verbose=None): test_classes = (TestBasicOps, TestVariousIteratorArgs, TestGC, RegressionTests, LengthTransparency, - SubclassWithKwargsTest, TestExamples) + SubclassWithKwargsTest, TestExamples, TestRecursionLimit) support.run_unittest(*test_classes) # verify reference counting diff -r 57c6435ca03b Modules/itertoolsmodule.c --- a/Modules/itertoolsmodule.c Tue Mar 19 13:27:24 2013 +0200 +++ b/Modules/itertoolsmodule.c Tue Mar 19 20:08:58 2013 +0200 @@ -54,12 +54,14 @@ groupby_dealloc(groupbyobject *gbo) { PyObject_GC_UnTrack(gbo); + Py_TRASHCAN_SAFE_BEGIN(gbo) Py_XDECREF(gbo->it); Py_XDECREF(gbo->keyfunc); Py_XDECREF(gbo->tgtkey); Py_XDECREF(gbo->currkey); Py_XDECREF(gbo->currvalue); Py_TYPE(gbo)->tp_free(gbo); + Py_TRASHCAN_SAFE_END(gbo) } static int @@ -911,9 +913,11 @@ cycle_dealloc(cycleobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->saved); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -1088,9 +1092,11 @@ dropwhile_dealloc(dropwhileobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->func); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -1111,7 +1117,10 @@ iternext = *Py_TYPE(it)->tp_iternext; for (;;) { + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; item = iternext(it); + Py_LeaveRecursiveCall(); if (item == NULL) return NULL; if (lz->start == 1) @@ -1257,9 +1266,11 @@ takewhile_dealloc(takewhileobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->func); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -1280,7 +1291,10 @@ if (lz->stop == 1) return NULL; + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; item = (*Py_TYPE(it)->tp_iternext)(it); + Py_LeaveRecursiveCall(); if (item == NULL) return NULL; @@ -1472,8 +1486,10 @@ islice_dealloc(isliceobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -1494,7 +1510,10 @@ iternext = *Py_TYPE(it)->tp_iternext; while (lz->cnt < lz->next) { + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; item = iternext(it); + Py_LeaveRecursiveCall(); if (item == NULL) return NULL; Py_DECREF(item); @@ -1502,7 +1521,10 @@ } if (stop != -1 && lz->cnt >= stop) return NULL; + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; item = iternext(it); + Py_LeaveRecursiveCall(); if (item == NULL) return NULL; lz->cnt++; @@ -1653,9 +1675,11 @@ starmap_dealloc(starmapobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->func); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -1673,7 +1697,10 @@ PyObject *result; PyObject *it = lz->it; + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; args = (*Py_TYPE(it)->tp_iternext)(it); + Py_LeaveRecursiveCall(); if (args == NULL) return NULL; if (!PyTuple_CheckExact(args)) { @@ -1809,9 +1836,11 @@ chain_dealloc(chainobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->active); Py_XDECREF(lz->source); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -3340,10 +3369,12 @@ accumulate_dealloc(accumulateobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->binop); Py_XDECREF(lz->total); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -3514,9 +3545,11 @@ compress_dealloc(compressobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->data); Py_XDECREF(lz->selectors); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -3543,11 +3576,19 @@ exception first). */ + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; datum = datanext(data); + Py_LeaveRecursiveCall(); if (datum == NULL) return NULL; + if (Py_EnterRecursiveCall(" while iterating")) { + Py_DECREF(datum); + return NULL; + } selector = selectornext(selectors); + Py_LeaveRecursiveCall(); if (selector == NULL) { Py_DECREF(datum); return NULL; @@ -3674,9 +3715,11 @@ filterfalse_dealloc(filterfalseobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->func); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -3697,7 +3740,10 @@ iternext = *Py_TYPE(it)->tp_iternext; for (;;) { + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; item = iternext(it); + Py_LeaveRecursiveCall(); if (item == NULL) return NULL; @@ -4261,10 +4307,12 @@ zip_longest_dealloc(ziplongestobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->ittuple); Py_XDECREF(lz->result); Py_XDECREF(lz->fillvalue); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int diff -r 57c6435ca03b Objects/abstract.c --- a/Objects/abstract.c Tue Mar 19 13:27:24 2013 +0200 +++ b/Objects/abstract.c Tue Mar 19 20:08:58 2013 +0200 @@ -1238,7 +1238,7 @@ to be an int or have an __int__ method. Steals integral's reference. error_format will be used to create the TypeError if integral isn't actually an Integral instance. error_format should be a format string - that can accept a char* naming integral's type. + that can accept a char* naming integral's type. */ static PyObject * convert_integral_to_int(PyObject *integral, const char *error_format) @@ -1257,7 +1257,7 @@ } PyErr_Format(PyExc_TypeError, error_format, Py_TYPE(integral)->tp_name); Py_DECREF(integral); - return NULL; + return NULL; } @@ -2702,7 +2702,10 @@ PyIter_Next(PyObject *iter) { PyObject *result; + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; result = (*iter->ob_type->tp_iternext)(iter); + Py_LeaveRecursiveCall(); if (result == NULL && PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration)) diff -r 57c6435ca03b Python/bltinmodule.c --- a/Python/bltinmodule.c Tue Mar 19 13:27:24 2013 +0200 +++ b/Python/bltinmodule.c Tue Mar 19 20:08:58 2013 +0200 @@ -391,9 +391,11 @@ filter_dealloc(filterobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->func); Py_XDECREF(lz->it); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -414,7 +416,10 @@ iternext = *Py_TYPE(it)->tp_iternext; for (;;) { + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; item = iternext(it); + Py_LeaveRecursiveCall(); if (item == NULL) return NULL; @@ -1031,9 +1036,11 @@ map_dealloc(mapobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->iters); Py_XDECREF(lz->func); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -2219,9 +2226,11 @@ zip_dealloc(zipobject *lz) { PyObject_GC_UnTrack(lz); + Py_TRASHCAN_SAFE_BEGIN(lz) Py_XDECREF(lz->ittuple); Py_XDECREF(lz->result); Py_TYPE(lz)->tp_free(lz); + Py_TRASHCAN_SAFE_END(lz) } static int @@ -2244,12 +2253,15 @@ if (tuplesize == 0) return NULL; + if (Py_EnterRecursiveCall(" while iterating")) + return NULL; if (Py_REFCNT(result) == 1) { Py_INCREF(result); for (i=0 ; i < tuplesize ; i++) { it = PyTuple_GET_ITEM(lz->ittuple, i); item = (*Py_TYPE(it)->tp_iternext)(it); if (item == NULL) { + Py_LeaveRecursiveCall(); Py_DECREF(result); return NULL; } @@ -2259,18 +2271,22 @@ } } else { result = PyTuple_New(tuplesize); - if (result == NULL) + if (result == NULL) { + Py_LeaveRecursiveCall(); return NULL; + } for (i=0 ; i < tuplesize ; i++) { it = PyTuple_GET_ITEM(lz->ittuple, i); item = (*Py_TYPE(it)->tp_iternext)(it); if (item == NULL) { + Py_LeaveRecursiveCall(); Py_DECREF(result); return NULL; } PyTuple_SET_ITEM(result, i, item); } } + Py_LeaveRecursiveCall(); return result; }