changeset: 100802:cff06d875678 branch: 2.7 parent: 100778:f4cff2bf9903 user: Serhiy Storchaka date: Wed Mar 30 20:43:06 2016 +0300 files: Lib/test/seq_tests.py Lib/test/test_bytes.py Lib/test/test_deque.py Lib/test/test_dict.py Lib/test/test_iter.py Lib/test/test_ordered_dict.py Lib/test/test_set.py Lib/test/test_support.py Lib/test/test_unicode.py Misc/NEWS Objects/bytearrayobject.c Objects/dictobject.c Objects/iterobject.c Objects/listobject.c Objects/setobject.c Objects/tupleobject.c description: Issue #26494: Fixed crash on iterating exhausting iterators. Affected classes are generic sequence iterators, iterators of bytearray, list, tuple, set, frozenset, dict, OrderedDict and corresponding views. diff -r f4cff2bf9903 -r cff06d875678 Lib/test/seq_tests.py --- a/Lib/test/seq_tests.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/seq_tests.py Wed Mar 30 20:43:06 2016 +0300 @@ -4,6 +4,7 @@ import unittest import sys +from test import test_support as support # Various iterables # This is used for checking the constructor (here and in test_deque.py) @@ -402,3 +403,7 @@ self.assertEqual(a.index(0, -4*sys.maxint, 4*sys.maxint), 2) self.assertRaises(ValueError, a.index, 0, 4*sys.maxint,-4*sys.maxint) self.assertRaises(ValueError, a.index, 2, 0, -10) + + def test_free_after_iterating(self): + support.check_free_after_iterating(self, iter, self.type2test) + support.check_free_after_iterating(self, reversed, self.type2test) diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_bytes.py --- a/Lib/test/test_bytes.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_bytes.py Wed Mar 30 20:43:06 2016 +0300 @@ -518,6 +518,10 @@ self.assertRaisesRegexp(TypeError, r'\bendswith\b', b.endswith, x, None, None, None) + def test_free_after_iterating(self): + test.test_support.check_free_after_iterating(self, iter, self.type2test) + test.test_support.check_free_after_iterating(self, reversed, self.type2test) + class ByteArrayTest(BaseBytesTest): type2test = bytearray diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_deque.py --- a/Lib/test/test_deque.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_deque.py Wed Mar 30 20:43:06 2016 +0300 @@ -669,6 +669,10 @@ # SF bug #1486663 -- this used to erroneously raise a TypeError SubclassWithKwargs(newarg=1) + def test_free_after_iterating(self): + # For now, bypass tests that require slicing + self.skipTest("Exhausted deque iterator doesn't free a deque") + #============================================================================== libreftest = """ diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_dict.py --- a/Lib/test/test_dict.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_dict.py Wed Mar 30 20:43:06 2016 +0300 @@ -681,6 +681,15 @@ self._tracked(MyDict()) + def test_free_after_iterating(self): + test_support.check_free_after_iterating(self, iter, dict) + test_support.check_free_after_iterating(self, lambda d: d.iterkeys(), dict) + test_support.check_free_after_iterating(self, lambda d: d.itervalues(), dict) + test_support.check_free_after_iterating(self, lambda d: d.iteritems(), dict) + test_support.check_free_after_iterating(self, lambda d: iter(d.viewkeys()), dict) + test_support.check_free_after_iterating(self, lambda d: iter(d.viewvalues()), dict) + test_support.check_free_after_iterating(self, lambda d: iter(d.viewitems()), dict) + from test import mapping_tests class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_iter.py --- a/Lib/test/test_iter.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_iter.py Wed Mar 30 20:43:06 2016 +0300 @@ -2,7 +2,8 @@ import unittest from test.test_support import run_unittest, TESTFN, unlink, have_unicode, \ - check_py3k_warnings, cpython_only + check_py3k_warnings, cpython_only, \ + check_free_after_iterating # Test result of triple loop (too big to inline) TRIPLETS = [(0, 0, 0), (0, 0, 1), (0, 0, 2), @@ -921,6 +922,9 @@ lst.extend(gen()) self.assertEqual(len(lst), 760) + def test_free_after_iterating(self): + check_free_after_iterating(self, iter, SequenceClass, (0,)) + def test_main(): run_unittest(TestCase) diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_ordered_dict.py --- a/Lib/test/test_ordered_dict.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_ordered_dict.py Wed Mar 30 20:43:06 2016 +0300 @@ -267,6 +267,15 @@ items = [('a', 1), ('c', 3), ('b', 2)] self.assertEqual(list(MyOD(items).items()), items) + def test_free_after_iterating(self): + test_support.check_free_after_iterating(self, iter, OrderedDict) + test_support.check_free_after_iterating(self, lambda d: d.iterkeys(), OrderedDict) + test_support.check_free_after_iterating(self, lambda d: d.itervalues(), OrderedDict) + test_support.check_free_after_iterating(self, lambda d: d.iteritems(), OrderedDict) + test_support.check_free_after_iterating(self, lambda d: iter(d.viewkeys()), OrderedDict) + test_support.check_free_after_iterating(self, lambda d: iter(d.viewvalues()), OrderedDict) + test_support.check_free_after_iterating(self, lambda d: iter(d.viewitems()), OrderedDict) + class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = OrderedDict diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_set.py --- a/Lib/test/test_set.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_set.py Wed Mar 30 20:43:06 2016 +0300 @@ -340,6 +340,9 @@ gc.collect() self.assertTrue(ref() is None, "Cycle was not collected") + def test_free_after_iterating(self): + test_support.check_free_after_iterating(self, iter, self.thetype) + class TestSet(TestJointOps): thetype = set diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_support.py --- a/Lib/test/test_support.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_support.py Wed Mar 30 20:43:06 2016 +0300 @@ -1659,3 +1659,21 @@ """ stderr = re.sub(br"\[\d+ refs\]\r?\n?$", b"", stderr).strip() return stderr + + +def check_free_after_iterating(test, iter, cls, args=()): + class A(cls): + def __del__(self): + done[0] = True + try: + next(it) + except StopIteration: + pass + + done = [False] + it = iter(A(*args)) + # Issue 26494: Shouldn't crash + test.assertRaises(StopIteration, next, it) + # The sequence should be deallocated just after the end of iterating + gc_collect() + test.assertTrue(done[0]) diff -r f4cff2bf9903 -r cff06d875678 Lib/test/test_unicode.py --- a/Lib/test/test_unicode.py Mon Mar 28 00:22:09 2016 +0000 +++ b/Lib/test/test_unicode.py Wed Mar 30 20:43:06 2016 +0300 @@ -1857,6 +1857,11 @@ unicode_encodedecimal(u"123" + s, "xmlcharrefreplace"), '123' + exp) + def test_free_after_iterating(self): + test_support.check_free_after_iterating(self, iter, unicode) + test_support.check_free_after_iterating(self, reversed, unicode) + + def test_main(): test_support.run_unittest(__name__) diff -r f4cff2bf9903 -r cff06d875678 Misc/NEWS --- a/Misc/NEWS Mon Mar 28 00:22:09 2016 +0000 +++ b/Misc/NEWS Wed Mar 30 20:43:06 2016 +0300 @@ -10,6 +10,10 @@ Core and Builtins ----------------- +- Issue #26494: Fixed crash on iterating exhausting iterators. + Affected classes are generic sequence iterators, iterators of bytearray, + list, tuple, set, frozenset, dict, OrderedDict and corresponding views. + - Issue #26581: If coding cookie is specified multiple times on a line in Python source code file, only the first one is taken to account. diff -r f4cff2bf9903 -r cff06d875678 Objects/bytearrayobject.c --- a/Objects/bytearrayobject.c Mon Mar 28 00:22:09 2016 +0000 +++ b/Objects/bytearrayobject.c Wed Mar 30 20:43:06 2016 +0300 @@ -2982,8 +2982,8 @@ return item; } + it->it_seq = NULL; Py_DECREF(seq); - it->it_seq = NULL; return NULL; } diff -r f4cff2bf9903 -r cff06d875678 Objects/dictobject.c --- a/Objects/dictobject.c Mon Mar 28 00:22:09 2016 +0000 +++ b/Objects/dictobject.c Wed Mar 30 20:43:06 2016 +0300 @@ -2586,8 +2586,8 @@ return key; fail: + di->di_dict = NULL; Py_DECREF(d); - di->di_dict = NULL; return NULL; } @@ -2658,8 +2658,8 @@ return value; fail: + di->di_dict = NULL; Py_DECREF(d); - di->di_dict = NULL; return NULL; } @@ -2744,8 +2744,8 @@ return result; fail: + di->di_dict = NULL; Py_DECREF(d); - di->di_dict = NULL; return NULL; } diff -r f4cff2bf9903 -r cff06d875678 Objects/iterobject.c --- a/Objects/iterobject.c Mon Mar 28 00:22:09 2016 +0000 +++ b/Objects/iterobject.c Wed Mar 30 20:43:06 2016 +0300 @@ -69,8 +69,8 @@ PyErr_ExceptionMatches(PyExc_StopIteration)) { PyErr_Clear(); + it->it_seq = NULL; Py_DECREF(seq); - it->it_seq = NULL; } return NULL; } diff -r f4cff2bf9903 -r cff06d875678 Objects/listobject.c --- a/Objects/listobject.c Mon Mar 28 00:22:09 2016 +0000 +++ b/Objects/listobject.c Wed Mar 30 20:43:06 2016 +0300 @@ -2915,8 +2915,8 @@ return item; } + it->it_seq = NULL; Py_DECREF(seq); - it->it_seq = NULL; return NULL; } @@ -3018,9 +3018,17 @@ listreviter_next(listreviterobject *it) { PyObject *item; - Py_ssize_t index = it->it_index; - PyListObject *seq = it->it_seq; + Py_ssize_t index; + PyListObject *seq; + assert(it != NULL); + seq = it->it_seq; + if (seq == NULL) { + return NULL; + } + assert(PyList_Check(seq)); + + index = it->it_index; if (index>=0 && index < PyList_GET_SIZE(seq)) { item = PyList_GET_ITEM(seq, index); it->it_index--; @@ -3028,10 +3036,8 @@ return item; } it->it_index = -1; - if (seq != NULL) { - it->it_seq = NULL; - Py_DECREF(seq); - } + it->it_seq = NULL; + Py_DECREF(seq); return NULL; } diff -r f4cff2bf9903 -r cff06d875678 Objects/setobject.c --- a/Objects/setobject.c Mon Mar 28 00:22:09 2016 +0000 +++ b/Objects/setobject.c Wed Mar 30 20:43:06 2016 +0300 @@ -871,8 +871,8 @@ return key; fail: + si->si_set = NULL; Py_DECREF(so); - si->si_set = NULL; return NULL; } diff -r f4cff2bf9903 -r cff06d875678 Objects/tupleobject.c --- a/Objects/tupleobject.c Mon Mar 28 00:22:09 2016 +0000 +++ b/Objects/tupleobject.c Wed Mar 30 20:43:06 2016 +0300 @@ -966,8 +966,8 @@ return item; } + it->it_seq = NULL; Py_DECREF(seq); - it->it_seq = NULL; return NULL; }