]> granicus.if.org Git - python/commitdiff
#3720: Interpreter crashes when an evil iterator removes its own next function.
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>
Mon, 12 Jan 2009 23:36:55 +0000 (23:36 +0000)
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>
Mon, 12 Jan 2009 23:36:55 +0000 (23:36 +0000)
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
Include/object.h
Lib/test/crashers/iter.py [deleted file]
Lib/test/test_iter.py
Misc/NEWS
Modules/itertoolsmodule.c
Objects/abstract.c
Objects/object.c
Objects/typeobject.c

index f3bc8bcd0fe9f5de553a0f144589a2c85cb95be8..87afe9a6402d29c0f534aaf7a2513ccfb52346c8 100644 (file)
@@ -636,7 +636,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
 
 #define PyIter_Check(obj) \
     (PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_ITER) && \
-     (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,
index b02689cc4a6ed3f4e2def65d9f17eb7a2bb75e7a..c40d36201d4256974d3f0e3162564933f897a49a 100644 (file)
@@ -473,6 +473,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 (file)
index 081dcbc..0000000
+++ /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 <N> 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('<N>', str(N))
-    else:
-        n = int(sys.argv[1])
-        func = globals()['case%d' % n]
-        func()
index f83de78ad67955ebc24029b9681aaf19b65e73fd..e84701825a0f45450521f731ec9222564c8894e7 100644 (file)
@@ -120,6 +120,13 @@ class TestCase(unittest.TestCase):
     def test_seq_class_iter(self):
         self.check_iterator(iter(SequenceClass(10)), 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:
@@ -877,6 +884,21 @@ class TestCase(unittest.TestCase):
         self.assertEqual(list(b), 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)
index 6a6f1e5d43afe96d401c348350861b698d89a736..e39fe8aa9236a87fb595b8b26b5cec5d63a85c76 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 1
 Core and Builtins
 -----------------
 
+- Issue #3720: Fix a crash when an iterator modifies its class and removes its
+  __next__ method.
+
 - Issue #4893: Use NT threading on CE.
 
 - Issue #4915: Port sysmodule to Windows CE.
index 5875d1045800602a245cedde662f7a2ae6dc52b6..9d6dbd9a6e88b83f548dbf77b60676f432588a15 100644 (file)
@@ -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;
@@ -2577,7 +2572,6 @@ ifilter_next(ifilterobject *lz)
        long ok;
        PyObject *(*iternext)(PyObject *);
 
-       assert(PyIter_Check(it));
        iternext = *Py_TYPE(it)->tp_iternext;
        for (;;) {
                item = iternext(it);
@@ -2721,7 +2715,6 @@ ifilterfalse_next(ifilterfalseobject *lz)
        long ok;
        PyObject *(*iternext)(PyObject *);
 
-       assert(PyIter_Check(it));
        iternext = *Py_TYPE(it)->tp_iternext;
        for (;;) {
                item = iternext(it);
@@ -3059,7 +3052,6 @@ izip_next(izipobject *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);
@@ -3075,7 +3067,6 @@ izip_next(izipobject *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);
@@ -3411,7 +3402,6 @@ izip_longest_next(iziplongestobject *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;      
@@ -3440,7 +3430,6 @@ izip_longest_next(iziplongestobject *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;      
index 956c4f4996933928cf5e52d57ab78ab02a466af3..80a12891386034371b61a00b00de311396e3d9f6 100644 (file)
@@ -3067,7 +3067,6 @@ PyObject *
 PyIter_Next(PyObject *iter)
 {
        PyObject *result;
-       assert(PyIter_Check(iter));
        result = (*iter->ob_type->tp_iternext)(iter);
        if (result == NULL &&
            PyErr_Occurred() &&
index 1e0db4ac69fff9e5af547ea54a02275c0faddaa5..ba736a9d93d552f6f868137f6109fc3b13f15777 100644 (file)
@@ -1305,6 +1305,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 *
index 3f790e88242253db508660018ffdcd574d4c233a..8242242d1976c62627619493ff46e35b33f524b9 100644 (file)
@@ -6090,8 +6090,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)