Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 424b481

Browse files
committed
Merged revisions 67049 via svnmerge from
svn+ssh://[email protected]/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. ........
1 parent 6a27efa commit 424b481

3 files changed

Lines changed: 47 additions & 16 deletions

File tree

Lib/test/pickletester.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,22 @@ def test_bad_getattr(self):
876876
d = self.dumps(x, 2)
877877
self.assertRaises(RuntimeError, self.loads, d)
878878

879+
def test_reduce_bad_iterator(self):
880+
# Issue4176: crash when 4th and 5th items of __reduce__()
881+
# are not iterators
882+
class C(object):
883+
def __reduce__(self):
884+
# 4th item is not an iterator
885+
return list, (), None, [], None
886+
class D(object):
887+
def __reduce__(self):
888+
# 5th item is not an iterator
889+
return dict, (), None, None, []
890+
891+
for proto in protocols:
892+
self.assertRaises(pickle.PickleError, self.dumps, C(), proto)
893+
self.assertRaises(pickle.PickleError, self.dumps, D(), proto)
894+
879895
# Test classes for reduce_ex
880896

881897
class REX_one(object):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ What's New in Python 3.0 beta 5
1515
Core and Builtins
1616
-----------------
1717

18+
- Issue #4176: Fixed a crash when pickling an object which ``__reduce__``
19+
method does not return iterators for the 4th and 5th items.
20+
1821
- Issue 3723: Fixed initialization of subinterpreters.
1922

2023
- Issue #4213: The file system encoding is now normalized by the

Modules/_pickle.c

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,36 +1961,58 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
19611961
PyObject *callable;
19621962
PyObject *argtup;
19631963
PyObject *state = NULL;
1964-
PyObject *listitems = NULL;
1965-
PyObject *dictitems = NULL;
1964+
PyObject *listitems = Py_None;
1965+
PyObject *dictitems = Py_None;
1966+
Py_ssize_t size;
19661967

19671968
int use_newobj = self->proto >= 2;
19681969

19691970
const char reduce_op = REDUCE;
19701971
const char build_op = BUILD;
19711972
const char newobj_op = NEWOBJ;
19721973

1974+
size = PyTuple_Size(args);
1975+
if (size < 2 || size > 5) {
1976+
PyErr_SetString(PicklingError, "tuple returned by "
1977+
"__reduce__ must contain 2 through 5 elements");
1978+
return -1;
1979+
}
1980+
19731981
if (!PyArg_UnpackTuple(args, "save_reduce", 2, 5,
19741982
&callable, &argtup, &state, &listitems, &dictitems))
19751983
return -1;
19761984

19771985
if (!PyCallable_Check(callable)) {
1978-
PyErr_SetString(PicklingError,
1979-
"first argument of save_reduce() must be callable");
1986+
PyErr_SetString(PicklingError, "first item of the tuple "
1987+
"returned by __reduce__ must be callable");
19801988
return -1;
19811989
}
19821990
if (!PyTuple_Check(argtup)) {
1983-
PyErr_SetString(PicklingError,
1984-
"second argument of save_reduce() must be a tuple");
1991+
PyErr_SetString(PicklingError, "second item of the tuple "
1992+
"returned by __reduce__ must be a tuple");
19851993
return -1;
19861994
}
19871995

19881996
if (state == Py_None)
19891997
state = NULL;
1998+
19901999
if (listitems == Py_None)
19912000
listitems = NULL;
2001+
else if (!PyIter_Check(listitems)) {
2002+
PyErr_Format(PicklingError, "Fourth element of tuple"
2003+
"returned by __reduce__ must be an iterator, not %s",
2004+
Py_TYPE(listitems)->tp_name);
2005+
return -1;
2006+
}
2007+
19922008
if (dictitems == Py_None)
19932009
dictitems = NULL;
2010+
else if (!PyIter_Check(dictitems)) {
2011+
PyErr_Format(PicklingError, "Fifth element of tuple"
2012+
"returned by __reduce__ must be an iterator, not %s",
2013+
Py_TYPE(dictitems)->tp_name);
2014+
return -1;
2015+
}
19942016

19952017
/* Protocol 2 special case: if callable's name is __newobj__, use
19962018
NEWOBJ. */
@@ -2309,16 +2331,6 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
23092331
"__reduce__ must return a string or tuple");
23102332
goto error;
23112333
}
2312-
if (Py_SIZE(reduce_value) < 2 || Py_SIZE(reduce_value) > 5) {
2313-
PyErr_SetString(PicklingError, "tuple returned by __reduce__ "
2314-
"must contain 2 through 5 elements");
2315-
goto error;
2316-
}
2317-
if (!PyTuple_Check(PyTuple_GET_ITEM(reduce_value, 1))) {
2318-
PyErr_SetString(PicklingError, "second item of the tuple "
2319-
"returned by __reduce__ must be a tuple");
2320-
goto error;
2321-
}
23222334

23232335
status = save_reduce(self, reduce_value, obj);
23242336

0 commit comments

Comments
 (0)