From f343e01c170b3f63eafac4568d905be91b676254 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Mon, 12 Jan 2009 23:58:21 +0000 Subject: Merged revisions 68560 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68560 | amaury.forgeotdarc | 2009-01-13 00:36:55 +0100 (mar., 13 janv. 2009) | 6 lines #3720: Interpreter crashes when an evil iterator removes its own next function. Now the slot is filled with a function that always raises. Will not backport: extensions compiled with 2.6.x would not run on 2.6.0. ........ --- Include/abstract.h | 3 ++- Include/object.h | 1 + Lib/test/crashers/iter.py | 53 ----------------------------------------------- Lib/test/test_iter.py | 22 ++++++++++++++++++++ Misc/NEWS | 3 +++ Modules/itertoolsmodule.c | 8 ------- Objects/abstract.c | 1 - Objects/object.c | 14 +++++++++++++ Objects/typeobject.c | 6 +++++- Python/bltinmodule.c | 3 --- 10 files changed, 47 insertions(+), 67 deletions(-) delete mode 100644 Lib/test/crashers/iter.py diff --git a/Include/abstract.h b/Include/abstract.h index 4fb16d0..b953308 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -612,7 +612,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ is an iterator, this returns itself. */ #define PyIter_Check(obj) \ - ((obj)->ob_type->tp_iternext != NULL) + ((obj)->ob_type->tp_iternext != NULL && \ + (obj)->ob_type->tp_iternext != &_PyObject_NextNotImplemented) PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *); /* Takes an iterator object and calls its tp_iternext slot, diff --git a/Include/object.h b/Include/object.h index 0d20ecc..f3fdbda 100644 --- a/Include/object.h +++ b/Include/object.h @@ -438,6 +438,7 @@ PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *); PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *); PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *); +PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *); PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyObject *, PyObject *); diff --git a/Lib/test/crashers/iter.py b/Lib/test/crashers/iter.py deleted file mode 100644 index 0d8540e..0000000 --- a/Lib/test/crashers/iter.py +++ /dev/null @@ -1,53 +0,0 @@ -# Calls to PyIter_Next, or direct calls to tp_iternext, on an object -# which might no longer be an iterable because its 'next' method was -# removed. These are all variants of Issue3720. - -""" -Run this script with an argument between 1 and to test for -different crashes. -""" -N = 8 - -import sys - -class Foo(object): - def __iter__(self): - return self - def next(self): - del Foo.next - return (1, 2) - -def case1(): - list(enumerate(Foo())) - -def case2(): - x, y = Foo() - -def case3(): - filter(None, Foo()) - -def case4(): - map(None, Foo(), Foo()) - -def case5(): - max(Foo()) - -def case6(): - sum(Foo(), ()) - -def case7(): - dict(Foo()) - -def case8(): - sys.stdout.writelines(Foo()) - -# etc... - - -if __name__ == '__main__': - if len(sys.argv) < 2: - print(__doc__.replace('', str(N))) - else: - n = int(sys.argv[1]) - func = globals()['case%d' % n] - func() diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index eba9728..7788502 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -120,6 +120,13 @@ class TestCase(unittest.TestCase): def test_seq_class_iter(self): self.check_iterator(iter(SequenceClass(10)), list(range(10))) + # Test a new_style class with __iter__ but no next() method + def test_new_style_iter_class(self): + class IterClass(object): + def __iter__(self): + return self + self.assertRaises(TypeError, iter, IterClass()) + # Test two-argument iter() with callable instance def test_iter_callable(self): class C: @@ -853,6 +860,21 @@ class TestCase(unittest.TestCase): self.assertEqual(list(b), list(zip(range(5), range(5)))) self.assertEqual(list(b), []) + def test_3720(self): + # Avoid a crash, when an iterator deletes its next() method. + class BadIterator(object): + def __iter__(self): + return self + def __next__(self): + del BadIterator.__next__ + return 1 + + try: + for i in BadIterator() : + pass + except TypeError: + pass + def test_main(): run_unittest(TestCase) diff --git a/Misc/NEWS b/Misc/NEWS index 9e7afee..6695a24 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 0 Core and Builtins ----------------- +- Issue #3720: Fix a crash when an iterator modifies its class and removes its + __next__ method. + - Issue #4910: Builtin int() function and PyNumber_Long/PyNumber_Int API function no longer attempt to call the __long__ slot to convert an object to an integer. Only the __int__ and __trunc__ slots are examined. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 8125dcb..dcf6aba 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -886,7 +886,6 @@ dropwhile_next(dropwhileobject *lz) long ok; PyObject *(*iternext)(PyObject *); - assert(PyIter_Check(it)); iternext = *Py_TYPE(it)->tp_iternext; for (;;) { item = iternext(it); @@ -1031,7 +1030,6 @@ takewhile_next(takewhileobject *lz) if (lz->stop == 1) return NULL; - assert(PyIter_Check(it)); item = (*Py_TYPE(it)->tp_iternext)(it); if (item == NULL) return NULL; @@ -1218,7 +1216,6 @@ islice_next(isliceobject *lz) Py_ssize_t oldnext; PyObject *(*iternext)(PyObject *); - assert(PyIter_Check(it)); iternext = *Py_TYPE(it)->tp_iternext; while (lz->cnt < lz->next) { item = iternext(it); @@ -1229,7 +1226,6 @@ islice_next(isliceobject *lz) } if (lz->stop != -1 && lz->cnt >= lz->stop) return NULL; - assert(PyIter_Check(it)); item = iternext(it); if (item == NULL) return NULL; @@ -1361,7 +1357,6 @@ starmap_next(starmapobject *lz) PyObject *result; PyObject *it = lz->it; - assert(PyIter_Check(it)); args = (*Py_TYPE(it)->tp_iternext)(it); if (args == NULL) return NULL; @@ -2403,7 +2398,6 @@ filterfalse_next(filterfalseobject *lz) long ok; PyObject *(*iternext)(PyObject *); - assert(PyIter_Check(it)); iternext = *Py_TYPE(it)->tp_iternext; for (;;) { item = iternext(it); @@ -2888,7 +2882,6 @@ zip_longest_next(ziplongestobject *lz) Py_INCREF(lz->fillvalue); item = lz->fillvalue; } else { - assert(PyIter_Check(it)); item = (*Py_TYPE(it)->tp_iternext)(it); if (item == NULL) { lz->numactive -= 1; @@ -2917,7 +2910,6 @@ zip_longest_next(ziplongestobject *lz) Py_INCREF(lz->fillvalue); item = lz->fillvalue; } else { - assert(PyIter_Check(it)); item = (*Py_TYPE(it)->tp_iternext)(it); if (item == NULL) { lz->numactive -= 1; diff --git a/Objects/abstract.c b/Objects/abstract.c index 0a0333c..1f988ec 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2736,7 +2736,6 @@ PyObject * PyIter_Next(PyObject *iter) { PyObject *result; - assert(PyIter_Check(iter)); result = (*iter->ob_type->tp_iternext)(iter); if (result == NULL && PyErr_Occurred() && diff --git a/Objects/object.c b/Objects/object.c index 15d26a7..00657de 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1020,6 +1020,20 @@ PyObject_SelfIter(PyObject *obj) return obj; } +/* Helper used when the __next__ method is removed from a type: + tp_iternext is never NULL and can be safely called without checking + on every iteration. + */ + +PyObject * +_PyObject_NextNotImplemented(PyObject *self) +{ + PyErr_Format(PyExc_TypeError, + "'%.200s' object is not iterable", + Py_TYPE(self)->tp_name); + return NULL; +} + /* Generic GetAttr functions - put these in your tp_[gs]etattro slot */ PyObject * diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 8f2e01e..8921b5f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5630,8 +5630,12 @@ update_one_slot(PyTypeObject *type, slotdef *p) } do { descr = _PyType_Lookup(type, p->name_strobj); - if (descr == NULL) + if (descr == NULL) { + if (ptr == (void**)&type->tp_iternext) { + specific = _PyObject_NextNotImplemented; + } continue; + } if (Py_TYPE(descr) == &PyWrapperDescr_Type) { void **tptr = resolve_slotdups(type, p->name_strobj); if (tptr == NULL || tptr == ptr) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 4e9ec85..f87fdd2 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -375,7 +375,6 @@ filter_next(filterobject *lz) long ok; PyObject *(*iternext)(PyObject *); - assert(PyIter_Check(it)); iternext = *Py_TYPE(it)->tp_iternext; for (;;) { item = iternext(it); @@ -2144,7 +2143,6 @@ zip_next(zipobject *lz) Py_INCREF(result); for (i=0 ; i < tuplesize ; i++) { it = PyTuple_GET_ITEM(lz->ittuple, i); - assert(PyIter_Check(it)); item = (*Py_TYPE(it)->tp_iternext)(it); if (item == NULL) { Py_DECREF(result); @@ -2160,7 +2158,6 @@ zip_next(zipobject *lz) return NULL; for (i=0 ; i < tuplesize ; i++) { it = PyTuple_GET_ITEM(lz->ittuple, i); - assert(PyIter_Check(it)); item = (*Py_TYPE(it)->tp_iternext)(it); if (item == NULL) { Py_DECREF(result); -- cgit v0.12