Skip to content

Commit

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

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

  Issue python#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.
........
  • Loading branch information
amauryfa committed Oct 30, 2008
1 parent 6a27efa commit 424b481
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 16 deletions.
16 changes: 16 additions & 0 deletions Lib/test/pickletester.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,22 @@ def test_bad_getattr(self):
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):
Expand Down
3 changes: 3 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 28 additions & 16 deletions Modules/_pickle.c
Original file line number Diff line number Diff line change
Expand Up @@ -1961,36 +1961,58 @@ 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;

const char reduce_op = REDUCE;
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. */
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 424b481

Please sign in to comment.