Patch #1462488: prevent a segfault in object_reduce_ex() by splitting
the implementation for __reduce__ and __reduce_ex__ into two separate functions. Fixes bug #931877. (backport from rev. 54397)
This commit is contained in:
parent
7cd6ef0913
commit
c1b4e8e6e2
|
@ -831,6 +831,24 @@ class AbstractPickleTests(unittest.TestCase):
|
|||
y = self.loads(s)
|
||||
self.assertEqual(y._proto, None)
|
||||
|
||||
def test_reduce_ex_calls_base(self):
|
||||
for proto in 0, 1, 2:
|
||||
x = REX_four()
|
||||
self.assertEqual(x._proto, None)
|
||||
s = self.dumps(x, proto)
|
||||
self.assertEqual(x._proto, proto)
|
||||
y = self.loads(s)
|
||||
self.assertEqual(y._proto, proto)
|
||||
|
||||
def test_reduce_calls_base(self):
|
||||
for proto in 0, 1, 2:
|
||||
x = REX_five()
|
||||
self.assertEqual(x._reduce_called, 0)
|
||||
s = self.dumps(x, proto)
|
||||
self.assertEqual(x._reduce_called, 1)
|
||||
y = self.loads(s)
|
||||
self.assertEqual(y._reduce_called, 1)
|
||||
|
||||
# Test classes for reduce_ex
|
||||
|
||||
class REX_one(object):
|
||||
|
@ -855,6 +873,20 @@ class REX_three(object):
|
|||
def __reduce__(self):
|
||||
raise TestFailed, "This __reduce__ shouldn't be called"
|
||||
|
||||
class REX_four(object):
|
||||
_proto = None
|
||||
def __reduce_ex__(self, proto):
|
||||
self._proto = proto
|
||||
return object.__reduce_ex__(self, proto)
|
||||
# Calling base class method should succeed
|
||||
|
||||
class REX_five(object):
|
||||
_reduce_called = 0
|
||||
def __reduce__(self):
|
||||
self._reduce_called = 1
|
||||
return object.__reduce__(self)
|
||||
# This one used to fail with infinite recursion
|
||||
|
||||
# Test classes for newobj
|
||||
|
||||
class MyInt(int):
|
||||
|
|
|
@ -12,6 +12,9 @@ What's New in Python 2.5.1c1?
|
|||
Core and builtins
|
||||
-----------------
|
||||
|
||||
- Patch #1462488: Python no longer segfaults when ``object.__reduce_ex__()``
|
||||
is called with an object that is faking its type.
|
||||
|
||||
- Patch #1680015: Don't modify __slots__ tuple if it contains an unicode
|
||||
name.
|
||||
|
||||
|
|
|
@ -2708,11 +2708,54 @@ reduce_2(PyObject *obj)
|
|||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* There were two problems when object.__reduce__ and object.__reduce_ex__
|
||||
* were implemented in the same function:
|
||||
* - trying to pickle an object with a custom __reduce__ method that
|
||||
* fell back to object.__reduce__ in certain circumstances led to
|
||||
* infinite recursion at Python level and eventual RuntimeError.
|
||||
* - Pickling objects that lied about their type by overwriting the
|
||||
* __class__ descriptor could lead to infinite recursion at C level
|
||||
* and eventual segfault.
|
||||
*
|
||||
* Because of backwards compatibility, the two methods still have to
|
||||
* behave in the same way, even if this is not required by the pickle
|
||||
* protocol. This common functionality was moved to the _common_reduce
|
||||
* function.
|
||||
*/
|
||||
static PyObject *
|
||||
_common_reduce(PyObject *self, int proto)
|
||||
{
|
||||
PyObject *copy_reg, *res;
|
||||
|
||||
if (proto >= 2)
|
||||
return reduce_2(self);
|
||||
|
||||
copy_reg = import_copy_reg();
|
||||
if (!copy_reg)
|
||||
return NULL;
|
||||
|
||||
res = PyEval_CallMethod(copy_reg, "_reduce_ex", "(Oi)", self, proto);
|
||||
Py_DECREF(copy_reg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
object_reduce(PyObject *self, PyObject *args)
|
||||
{
|
||||
int proto = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|i:__reduce__", &proto))
|
||||
return NULL;
|
||||
|
||||
return _common_reduce(self, proto);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
object_reduce_ex(PyObject *self, PyObject *args)
|
||||
{
|
||||
/* Call copy_reg._reduce_ex(self, proto) */
|
||||
PyObject *reduce, *copy_reg, *res;
|
||||
PyObject *reduce, *res;
|
||||
int proto = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto))
|
||||
|
@ -2748,23 +2791,13 @@ object_reduce_ex(PyObject *self, PyObject *args)
|
|||
Py_DECREF(reduce);
|
||||
}
|
||||
|
||||
if (proto >= 2)
|
||||
return reduce_2(self);
|
||||
|
||||
copy_reg = import_copy_reg();
|
||||
if (!copy_reg)
|
||||
return NULL;
|
||||
|
||||
res = PyEval_CallMethod(copy_reg, "_reduce_ex", "(Oi)", self, proto);
|
||||
Py_DECREF(copy_reg);
|
||||
|
||||
return res;
|
||||
return _common_reduce(self, proto);
|
||||
}
|
||||
|
||||
static PyMethodDef object_methods[] = {
|
||||
{"__reduce_ex__", object_reduce_ex, METH_VARARGS,
|
||||
PyDoc_STR("helper for pickle")},
|
||||
{"__reduce__", object_reduce_ex, METH_VARARGS,
|
||||
{"__reduce__", object_reduce, METH_VARARGS,
|
||||
PyDoc_STR("helper for pickle")},
|
||||
{0}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue