From 8c126f17f09eeb75d3d3c9737150384cd1dd9c03 Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Sun, 17 Jul 2016 14:01:42 +0200 Subject: Issue #26974: Fix segfault in the presence of absurd subclassing. Proactively eliminate all internal uses of overridden methods. --- Lib/test/test_decimal.py | 28 +++++++++++++++++++++++++ Modules/_decimal/_decimal.c | 50 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index cde5d78..1aa0bf8 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -5398,6 +5398,34 @@ class CWhitebox(unittest.TestCase): y = Decimal(10**(9*25)).__sizeof__() self.assertEqual(y, x+4) + def test_internal_use_of_overridden_methods(self): + Decimal = C.Decimal + + # Unsound subtyping + class X(float): + def as_integer_ratio(self): + return 1 + def __abs__(self): + return self + + class Y(float): + def __abs__(self): + return [1]*200 + + class I(int): + def bit_length(self): + return [1]*200 + + class Z(float): + def as_integer_ratio(self): + return (I(1), I(1)) + def __abs__(self): + return self + + for cls in X, Y, Z: + self.assertEqual(Decimal.from_float(cls(101.1)), + Decimal.from_float(101.1)) + @requires_docstrings @unittest.skipUnless(C, "test requires C version") class SignatureTest(unittest.TestCase): diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 22053b4..e15941a 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -2208,6 +2208,14 @@ PyDecType_FromLongExact(PyTypeObject *type, const PyObject *pylong, return dec; } +/* External C-API functions */ +static binaryfunc _py_long_multiply; +static binaryfunc _py_long_floor_divide; +static ternaryfunc _py_long_power; +static unaryfunc _py_float_abs; +static PyCFunction _py_long_bit_length; +static PyCFunction _py_float_as_integer_ratio; + /* Return a PyDecObject or a subtype from a PyFloatObject. Conversion is exact. */ static PyObject * @@ -2258,13 +2266,13 @@ PyDecType_FromFloatExact(PyTypeObject *type, PyObject *v, } /* absolute value of the float */ - tmp = PyObject_CallMethod(v, "__abs__", NULL); + tmp = _py_float_abs(v); if (tmp == NULL) { return NULL; } /* float as integer ratio: numerator/denominator */ - n_d = PyObject_CallMethod(tmp, "as_integer_ratio", NULL); + n_d = _py_float_as_integer_ratio(tmp, NULL); Py_DECREF(tmp); if (n_d == NULL) { return NULL; @@ -2272,7 +2280,7 @@ PyDecType_FromFloatExact(PyTypeObject *type, PyObject *v, n = PyTuple_GET_ITEM(n_d, 0); d = PyTuple_GET_ITEM(n_d, 1); - tmp = PyObject_CallMethod(d, "bit_length", NULL); + tmp = _py_long_bit_length(d, NULL); if (tmp == NULL) { Py_DECREF(n_d); return NULL; @@ -5511,6 +5519,32 @@ static struct int_constmap int_constants [] = { #define CHECK_PTR(expr) \ do { if ((expr) == NULL) goto error; } while (0) + +static PyCFunction +cfunc_noargs(PyTypeObject *t, const char *name) +{ + struct PyMethodDef *m; + + if (t->tp_methods == NULL) { + goto error; + } + + for (m = t->tp_methods; m->ml_name != NULL; m++) { + if (strcmp(name, m->ml_name) == 0) { + if (!(m->ml_flags & METH_NOARGS)) { + goto error; + } + return m->ml_meth; + } + } + +error: + PyErr_Format(PyExc_RuntimeError, + "internal error: could not find method %s", name); + return NULL; +} + + PyMODINIT_FUNC PyInit__decimal(void) { @@ -5535,6 +5569,16 @@ PyInit__decimal(void) mpd_setminalloc(_Py_DEC_MINALLOC); + /* Init external C-API functions */ + _py_long_multiply = PyLong_Type.tp_as_number->nb_multiply; + _py_long_floor_divide = PyLong_Type.tp_as_number->nb_floor_divide; + _py_long_power = PyLong_Type.tp_as_number->nb_power; + _py_float_abs = PyFloat_Type.tp_as_number->nb_absolute; + ASSIGN_PTR(_py_float_as_integer_ratio, cfunc_noargs(&PyFloat_Type, + "as_integer_ratio")); + ASSIGN_PTR(_py_long_bit_length, cfunc_noargs(&PyLong_Type, "bit_length")); + + /* Init types */ PyDec_Type.tp_base = &PyBaseObject_Type; PyDecContext_Type.tp_base = &PyBaseObject_Type; -- cgit v0.12