[Python-checkins] cpython (2.7): Issue #26494: Fixed crash on iterating exhausting iterators.

serhiy.storchaka python-checkins at python.org
Wed Mar 30 13:44:11 EDT 2016


https://hg.python.org/cpython/rev/cff06d875678
changeset:   100802:cff06d875678
branch:      2.7
parent:      100778:f4cff2bf9903
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Wed Mar 30 20:43:06 2016 +0300
summary:
  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.

files:
  Lib/test/seq_tests.py         |   5 +++++
  Lib/test/test_bytes.py        |   4 ++++
  Lib/test/test_deque.py        |   4 ++++
  Lib/test/test_dict.py         |   9 +++++++++
  Lib/test/test_iter.py         |   6 +++++-
  Lib/test/test_ordered_dict.py |   9 +++++++++
  Lib/test/test_set.py          |   3 +++
  Lib/test/test_support.py      |  18 ++++++++++++++++++
  Lib/test/test_unicode.py      |   5 +++++
  Misc/NEWS                     |   4 ++++
  Objects/bytearrayobject.c     |   2 +-
  Objects/dictobject.c          |   6 +++---
  Objects/iterobject.c          |   2 +-
  Objects/listobject.c          |  20 +++++++++++++-------
  Objects/setobject.c           |   2 +-
  Objects/tupleobject.c         |   2 +-
  16 files changed, 86 insertions(+), 15 deletions(-)


diff --git a/Lib/test/seq_tests.py b/Lib/test/seq_tests.py
--- a/Lib/test/seq_tests.py
+++ b/Lib/test/seq_tests.py
@@ -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 --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py
--- a/Lib/test/test_bytes.py
+++ b/Lib/test/test_bytes.py
@@ -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 --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py
--- a/Lib/test/test_deque.py
+++ b/Lib/test/test_deque.py
@@ -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 --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -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 --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py
--- a/Lib/test/test_iter.py
+++ b/Lib/test/test_iter.py
@@ -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 --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py
--- a/Lib/test/test_ordered_dict.py
+++ b/Lib/test/test_ordered_dict.py
@@ -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 --git a/Lib/test/test_set.py b/Lib/test/test_set.py
--- a/Lib/test/test_set.py
+++ b/Lib/test/test_set.py
@@ -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 --git a/Lib/test/test_support.py b/Lib/test/test_support.py
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -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 --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py
--- a/Lib/test/test_unicode.py
+++ b/Lib/test/test_unicode.py
@@ -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 --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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 --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c
--- a/Objects/bytearrayobject.c
+++ b/Objects/bytearrayobject.c
@@ -2982,8 +2982,8 @@
         return item;
     }
 
+    it->it_seq = NULL;
     Py_DECREF(seq);
-    it->it_seq = NULL;
     return NULL;
 }
 
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -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 --git a/Objects/iterobject.c b/Objects/iterobject.c
--- a/Objects/iterobject.c
+++ b/Objects/iterobject.c
@@ -69,8 +69,8 @@
         PyErr_ExceptionMatches(PyExc_StopIteration))
     {
         PyErr_Clear();
+        it->it_seq = NULL;
         Py_DECREF(seq);
-        it->it_seq = NULL;
     }
     return NULL;
 }
diff --git a/Objects/listobject.c b/Objects/listobject.c
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -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 --git a/Objects/setobject.c b/Objects/setobject.c
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -871,8 +871,8 @@
     return key;
 
 fail:
+    si->si_set = NULL;
     Py_DECREF(so);
-    si->si_set = NULL;
     return NULL;
 }
 
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -966,8 +966,8 @@
         return item;
     }
 
+    it->it_seq = NULL;
     Py_DECREF(seq);
-    it->it_seq = NULL;
     return NULL;
 }
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list