#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.
This commit is contained in:
Amaury Forgeot d'Arc 2009-01-12 23:36:55 +00:00
parent f94d7fa5fb
commit a40d573664
9 changed files with 47 additions and 67 deletions

View File

@ -636,7 +636,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
#define PyIter_Check(obj) \ #define PyIter_Check(obj) \
(PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_ITER) && \ (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 *); PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *);
/* Takes an iterator object and calls its tp_iternext slot, /* Takes an iterator object and calls its tp_iternext slot,

View File

@ -473,6 +473,7 @@ PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *); PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *); PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *);
PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *);
PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *,
PyObject *, PyObject *); PyObject *, PyObject *);

View File

@ -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()

View File

@ -120,6 +120,13 @@ class TestCase(unittest.TestCase):
def test_seq_class_iter(self): def test_seq_class_iter(self):
self.check_iterator(iter(SequenceClass(10)), range(10)) 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 # Test two-argument iter() with callable instance
def test_iter_callable(self): def test_iter_callable(self):
class C: class C:
@ -877,6 +884,21 @@ class TestCase(unittest.TestCase):
self.assertEqual(list(b), zip(range(5), range(5))) self.assertEqual(list(b), zip(range(5), range(5)))
self.assertEqual(list(b), []) 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(): def test_main():
run_unittest(TestCase) run_unittest(TestCase)

View File

@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 1
Core and Builtins 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 #4893: Use NT threading on CE.
- Issue #4915: Port sysmodule to Windows CE. - Issue #4915: Port sysmodule to Windows CE.

View File

@ -886,7 +886,6 @@ dropwhile_next(dropwhileobject *lz)
long ok; long ok;
PyObject *(*iternext)(PyObject *); PyObject *(*iternext)(PyObject *);
assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext; iternext = *Py_TYPE(it)->tp_iternext;
for (;;) { for (;;) {
item = iternext(it); item = iternext(it);
@ -1031,7 +1030,6 @@ takewhile_next(takewhileobject *lz)
if (lz->stop == 1) if (lz->stop == 1)
return NULL; return NULL;
assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it); item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) if (item == NULL)
return NULL; return NULL;
@ -1218,7 +1216,6 @@ islice_next(isliceobject *lz)
Py_ssize_t oldnext; Py_ssize_t oldnext;
PyObject *(*iternext)(PyObject *); PyObject *(*iternext)(PyObject *);
assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext; iternext = *Py_TYPE(it)->tp_iternext;
while (lz->cnt < lz->next) { while (lz->cnt < lz->next) {
item = iternext(it); item = iternext(it);
@ -1229,7 +1226,6 @@ islice_next(isliceobject *lz)
} }
if (lz->stop != -1 && lz->cnt >= lz->stop) if (lz->stop != -1 && lz->cnt >= lz->stop)
return NULL; return NULL;
assert(PyIter_Check(it));
item = iternext(it); item = iternext(it);
if (item == NULL) if (item == NULL)
return NULL; return NULL;
@ -1361,7 +1357,6 @@ starmap_next(starmapobject *lz)
PyObject *result; PyObject *result;
PyObject *it = lz->it; PyObject *it = lz->it;
assert(PyIter_Check(it));
args = (*Py_TYPE(it)->tp_iternext)(it); args = (*Py_TYPE(it)->tp_iternext)(it);
if (args == NULL) if (args == NULL)
return NULL; return NULL;
@ -2577,7 +2572,6 @@ ifilter_next(ifilterobject *lz)
long ok; long ok;
PyObject *(*iternext)(PyObject *); PyObject *(*iternext)(PyObject *);
assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext; iternext = *Py_TYPE(it)->tp_iternext;
for (;;) { for (;;) {
item = iternext(it); item = iternext(it);
@ -2721,7 +2715,6 @@ ifilterfalse_next(ifilterfalseobject *lz)
long ok; long ok;
PyObject *(*iternext)(PyObject *); PyObject *(*iternext)(PyObject *);
assert(PyIter_Check(it));
iternext = *Py_TYPE(it)->tp_iternext; iternext = *Py_TYPE(it)->tp_iternext;
for (;;) { for (;;) {
item = iternext(it); item = iternext(it);
@ -3059,7 +3052,6 @@ izip_next(izipobject *lz)
Py_INCREF(result); Py_INCREF(result);
for (i=0 ; i < tuplesize ; i++) { for (i=0 ; i < tuplesize ; i++) {
it = PyTuple_GET_ITEM(lz->ittuple, i); it = PyTuple_GET_ITEM(lz->ittuple, i);
assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it); item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) { if (item == NULL) {
Py_DECREF(result); Py_DECREF(result);
@ -3075,7 +3067,6 @@ izip_next(izipobject *lz)
return NULL; return NULL;
for (i=0 ; i < tuplesize ; i++) { for (i=0 ; i < tuplesize ; i++) {
it = PyTuple_GET_ITEM(lz->ittuple, i); it = PyTuple_GET_ITEM(lz->ittuple, i);
assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it); item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) { if (item == NULL) {
Py_DECREF(result); Py_DECREF(result);
@ -3411,7 +3402,6 @@ izip_longest_next(iziplongestobject *lz)
Py_INCREF(lz->fillvalue); Py_INCREF(lz->fillvalue);
item = lz->fillvalue; item = lz->fillvalue;
} else { } else {
assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it); item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) { if (item == NULL) {
lz->numactive -= 1; lz->numactive -= 1;
@ -3440,7 +3430,6 @@ izip_longest_next(iziplongestobject *lz)
Py_INCREF(lz->fillvalue); Py_INCREF(lz->fillvalue);
item = lz->fillvalue; item = lz->fillvalue;
} else { } else {
assert(PyIter_Check(it));
item = (*Py_TYPE(it)->tp_iternext)(it); item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) { if (item == NULL) {
lz->numactive -= 1; lz->numactive -= 1;

View File

@ -3067,7 +3067,6 @@ PyObject *
PyIter_Next(PyObject *iter) PyIter_Next(PyObject *iter)
{ {
PyObject *result; PyObject *result;
assert(PyIter_Check(iter));
result = (*iter->ob_type->tp_iternext)(iter); result = (*iter->ob_type->tp_iternext)(iter);
if (result == NULL && if (result == NULL &&
PyErr_Occurred() && PyErr_Occurred() &&

View File

@ -1305,6 +1305,20 @@ PyObject_SelfIter(PyObject *obj)
return 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 */ /* Generic GetAttr functions - put these in your tp_[gs]etattro slot */
PyObject * PyObject *

View File

@ -6090,8 +6090,12 @@ update_one_slot(PyTypeObject *type, slotdef *p)
} }
do { do {
descr = _PyType_Lookup(type, p->name_strobj); descr = _PyType_Lookup(type, p->name_strobj);
if (descr == NULL) if (descr == NULL) {
if (ptr == (void**)&type->tp_iternext) {
specific = _PyObject_NextNotImplemented;
}
continue; continue;
}
if (Py_TYPE(descr) == &PyWrapperDescr_Type) { if (Py_TYPE(descr) == &PyWrapperDescr_Type) {
void **tptr = resolve_slotdups(type, p->name_strobj); void **tptr = resolve_slotdups(type, p->name_strobj);
if (tptr == NULL || tptr == ptr) if (tptr == NULL || tptr == ptr)