Skip to content

Commit

Permalink
Merged revisions 68560 via svnmerge from
Browse files Browse the repository at this point in the history
svn+ssh://[email protected]/python/trunk

........
  r68560 | amaury.forgeotdarc | 2009-01-13 00:36:55 +0100 (mar., 13 janv. 2009) | 6 lines

  python#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.
........
  • Loading branch information
amauryfa committed Jan 12, 2009
1 parent e5e298f commit f343e01
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 67 deletions.
3 changes: 2 additions & 1 deletion Include/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down
53 changes: 0 additions & 53 deletions Lib/test/crashers/iter.py

This file was deleted.

22 changes: 22 additions & 0 deletions Lib/test/test_iter.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ def test_seq_class_for(self):
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:
Expand Down Expand Up @@ -853,6 +860,21 @@ def test_sinkstate_enumerate(self):
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)
Expand Down
3 changes: 3 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 0 additions & 8 deletions Modules/itertoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion Objects/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -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() &&
Expand Down
14 changes: 14 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand Down
6 changes: 5 additions & 1 deletion Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 0 additions & 3 deletions Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit f343e01

Please sign in to comment.