[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