Merged revisions 67049 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r67049 | amaury.forgeotdarc | 2008-10-30 22:18:34 +0100 (jeu., 30 oct. 2008) | 8 lines

  Issue #4176: Pickle would crash the interpreter when a __reduce__ function
  does not return an iterator for the 4th and 5th items.
  (sequence-like and mapping-like state)

  A list is not an iterator...

  Will backport to 2.6 and 2.5.
........
This commit is contained in:
Amaury Forgeot d'Arc 2008-10-30 22:25:31 +00:00
parent 6a27efa2d3
commit 424b4819be
3 changed files with 47 additions and 16 deletions

View File

@ -876,6 +876,22 @@ class AbstractPickleTests(unittest.TestCase):
d = self.dumps(x, 2)
self.assertRaises(RuntimeError, self.loads, d)
def test_reduce_bad_iterator(self):
# Issue4176: crash when 4th and 5th items of __reduce__()
# are not iterators
class C(object):
def __reduce__(self):
# 4th item is not an iterator
return list, (), None, [], None
class D(object):
def __reduce__(self):
# 5th item is not an iterator
return dict, (), None, None, []
for proto in protocols:
self.assertRaises(pickle.PickleError, self.dumps, C(), proto)
self.assertRaises(pickle.PickleError, self.dumps, D(), proto)
# Test classes for reduce_ex
class REX_one(object):

View File

@ -15,6 +15,9 @@ What's New in Python 3.0 beta 5
Core and Builtins
-----------------
- Issue #4176: Fixed a crash when pickling an object which ``__reduce__``
method does not return iterators for the 4th and 5th items.
- Issue 3723: Fixed initialization of subinterpreters.
- Issue #4213: The file system encoding is now normalized by the

View File

@ -1961,8 +1961,9 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
PyObject *callable;
PyObject *argtup;
PyObject *state = NULL;
PyObject *listitems = NULL;
PyObject *dictitems = NULL;
PyObject *listitems = Py_None;
PyObject *dictitems = Py_None;
Py_ssize_t size;
int use_newobj = self->proto >= 2;
@ -1970,27 +1971,48 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
const char build_op = BUILD;
const char newobj_op = NEWOBJ;
size = PyTuple_Size(args);
if (size < 2 || size > 5) {
PyErr_SetString(PicklingError, "tuple returned by "
"__reduce__ must contain 2 through 5 elements");
return -1;
}
if (!PyArg_UnpackTuple(args, "save_reduce", 2, 5,
&callable, &argtup, &state, &listitems, &dictitems))
return -1;
if (!PyCallable_Check(callable)) {
PyErr_SetString(PicklingError,
"first argument of save_reduce() must be callable");
PyErr_SetString(PicklingError, "first item of the tuple "
"returned by __reduce__ must be callable");
return -1;
}
if (!PyTuple_Check(argtup)) {
PyErr_SetString(PicklingError,
"second argument of save_reduce() must be a tuple");
PyErr_SetString(PicklingError, "second item of the tuple "
"returned by __reduce__ must be a tuple");
return -1;
}
if (state == Py_None)
state = NULL;
if (listitems == Py_None)
listitems = NULL;
else if (!PyIter_Check(listitems)) {
PyErr_Format(PicklingError, "Fourth element of tuple"
"returned by __reduce__ must be an iterator, not %s",
Py_TYPE(listitems)->tp_name);
return -1;
}
if (dictitems == Py_None)
dictitems = NULL;
else if (!PyIter_Check(dictitems)) {
PyErr_Format(PicklingError, "Fifth element of tuple"
"returned by __reduce__ must be an iterator, not %s",
Py_TYPE(dictitems)->tp_name);
return -1;
}
/* Protocol 2 special case: if callable's name is __newobj__, use
NEWOBJ. */
@ -2309,16 +2331,6 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
"__reduce__ must return a string or tuple");
goto error;
}
if (Py_SIZE(reduce_value) < 2 || Py_SIZE(reduce_value) > 5) {
PyErr_SetString(PicklingError, "tuple returned by __reduce__ "
"must contain 2 through 5 elements");
goto error;
}
if (!PyTuple_Check(PyTuple_GET_ITEM(reduce_value, 1))) {
PyErr_SetString(PicklingError, "second item of the tuple "
"returned by __reduce__ must be a tuple");
goto error;
}
status = save_reduce(self, reduce_value, obj);