mirror of https://github.com/python/cpython
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. Will backport.
This commit is contained in:
parent
ab1f4674ad
commit
20f43d3018
|
@ -831,6 +831,24 @@ class AbstractPickleTests(unittest.TestCase):
|
||||||
y = self.loads(s)
|
y = self.loads(s)
|
||||||
self.assertEqual(y._proto, None)
|
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
|
# Test classes for reduce_ex
|
||||||
|
|
||||||
class REX_one(object):
|
class REX_one(object):
|
||||||
|
@ -855,6 +873,20 @@ class REX_three(object):
|
||||||
def __reduce__(self):
|
def __reduce__(self):
|
||||||
raise TestFailed, "This __reduce__ shouldn't be called"
|
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
|
# Test classes for newobj
|
||||||
|
|
||||||
class MyInt(int):
|
class MyInt(int):
|
||||||
|
|
|
@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1?
|
||||||
Core and builtins
|
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
|
- Patch #1680015: Don't modify __slots__ tuple if it contains an unicode
|
||||||
name.
|
name.
|
||||||
|
|
||||||
|
|
|
@ -2770,11 +2770,54 @@ reduce_2(PyObject *obj)
|
||||||
return res;
|
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 *
|
static PyObject *
|
||||||
object_reduce_ex(PyObject *self, PyObject *args)
|
object_reduce_ex(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
/* Call copy_reg._reduce_ex(self, proto) */
|
PyObject *reduce, *res;
|
||||||
PyObject *reduce, *copy_reg, *res;
|
|
||||||
int proto = 0;
|
int proto = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto))
|
if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto))
|
||||||
|
@ -2810,23 +2853,13 @@ object_reduce_ex(PyObject *self, PyObject *args)
|
||||||
Py_DECREF(reduce);
|
Py_DECREF(reduce);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proto >= 2)
|
return _common_reduce(self, proto);
|
||||||
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 PyMethodDef object_methods[] = {
|
static PyMethodDef object_methods[] = {
|
||||||
{"__reduce_ex__", object_reduce_ex, METH_VARARGS,
|
{"__reduce_ex__", object_reduce_ex, METH_VARARGS,
|
||||||
PyDoc_STR("helper for pickle")},
|
PyDoc_STR("helper for pickle")},
|
||||||
{"__reduce__", object_reduce_ex, METH_VARARGS,
|
{"__reduce__", object_reduce, METH_VARARGS,
|
||||||
PyDoc_STR("helper for pickle")},
|
PyDoc_STR("helper for pickle")},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue