diff options
author | Amaury Forgeot d'Arc <amauryfa@gmail.com> | 2008-10-30 21:18:34 (GMT) |
---|---|---|
committer | Amaury Forgeot d'Arc <amauryfa@gmail.com> | 2008-10-30 21:18:34 (GMT) |
commit | 69a9c5b539dd848369efcd67574e447065f638e9 (patch) | |
tree | 8c1ad2e3831296822c6367a50e57e68a188edbc5 /Modules/cPickle.c | |
parent | 1fac5a450562c50a3b7121598da7f010a306e50f (diff) | |
download | cpython-69a9c5b539dd848369efcd67574e447065f638e9.zip cpython-69a9c5b539dd848369efcd67574e447065f638e9.tar.gz cpython-69a9c5b539dd848369efcd67574e447065f638e9.tar.bz2 |
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.
Diffstat (limited to 'Modules/cPickle.c')
-rw-r--r-- | Modules/cPickle.c | 59 |
1 files changed, 33 insertions, 26 deletions
diff --git a/Modules/cPickle.c b/Modules/cPickle.c index 95d619d..f777286 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -2206,13 +2206,14 @@ save_pers(Picklerobject *self, PyObject *args, PyObject *f) * appropriate __reduce__ method for ob. */ static int -save_reduce(Picklerobject *self, PyObject *args, PyObject *ob) +save_reduce(Picklerobject *self, PyObject *args, PyObject *fn, PyObject *ob) { PyObject *callable; PyObject *argtup; - PyObject *state = NULL; - PyObject *listitems = NULL; - PyObject *dictitems = NULL; + PyObject *state = NULL; + PyObject *listitems = Py_None; + PyObject *dictitems = Py_None; + Py_ssize_t size; int use_newobj = self->proto >= 2; @@ -2220,6 +2221,14 @@ save_reduce(Picklerobject *self, PyObject *args, PyObject *ob) static char build = BUILD; static char newobj = NEWOBJ; + size = PyTuple_Size(args); + if (size < 2 || size > 5) { + cPickle_ErrFormat(PicklingError, "tuple returned by " + "%s must contain 2 through 5 elements", + "O", fn); + return -1; + } + if (! PyArg_UnpackTuple(args, "save_reduce", 2, 5, &callable, &argtup, @@ -2229,17 +2238,32 @@ save_reduce(Picklerobject *self, PyObject *args, PyObject *ob) return -1; if (!PyTuple_Check(argtup)) { - PyErr_SetString(PicklingError, - "args from reduce() should be a tuple"); + cPickle_ErrFormat(PicklingError, "Second element of " + "tuple returned by %s must be a tuple", + "O", fn); return -1; } if (state == Py_None) state = NULL; + if (listitems == Py_None) listitems = NULL; + else if (!PyIter_Check(listitems)) { + cPickle_ErrFormat(PicklingError, "Fourth element of " + "tuple returned by %s must be an iterator, not %s", + "Os", fn, Py_TYPE(listitems)->tp_name); + return -1; + } + if (dictitems == Py_None) dictitems = NULL; + else if (!PyIter_Check(dictitems)) { + cPickle_ErrFormat(PicklingError, "Fifth element of " + "tuple returned by %s must be an iterator, not %s", + "Os", fn, Py_TYPE(dictitems)->tp_name); + return -1; + } /* Protocol 2 special case: if callable's name is __newobj__, use * NEWOBJ. This consumes a lot of code. @@ -2363,9 +2387,8 @@ save(Picklerobject *self, PyObject *args, int pers_save) { PyTypeObject *type; PyObject *py_ob_id = 0, *__reduce__ = 0, *t = 0; - PyObject *arg_tup; int res = -1; - int tmp, size; + int tmp; if (Py_EnterRecursiveCall(" while pickling an object")) return -1; @@ -2591,30 +2614,14 @@ save(Picklerobject *self, PyObject *args, int pers_save) goto finally; } - if (! PyTuple_Check(t)) { + if (!PyTuple_Check(t)) { cPickle_ErrFormat(PicklingError, "Value returned by " "%s must be string or tuple", "O", __reduce__); goto finally; } - size = PyTuple_Size(t); - if (size < 2 || size > 5) { - cPickle_ErrFormat(PicklingError, "tuple returned by " - "%s must contain 2 through 5 elements", - "O", __reduce__); - goto finally; - } - - arg_tup = PyTuple_GET_ITEM(t, 1); - if (!(PyTuple_Check(arg_tup) || arg_tup == Py_None)) { - cPickle_ErrFormat(PicklingError, "Second element of " - "tuple returned by %s must be a tuple", - "O", __reduce__); - goto finally; - } - - res = save_reduce(self, t, args); + res = save_reduce(self, t, __reduce__, args); finally: Py_LeaveRecursiveCall(); |