]> granicus.if.org Git - python/commitdiff
Issue #26974: Fix segfault in the presence of absurd subclassing. Proactively
authorStefan Krah <skrah@bytereef.org>
Sun, 17 Jul 2016 12:01:42 +0000 (14:01 +0200)
committerStefan Krah <skrah@bytereef.org>
Sun, 17 Jul 2016 12:01:42 +0000 (14:01 +0200)
eliminate all internal uses of overridden methods.

Lib/test/test_decimal.py
Modules/_decimal/_decimal.c

index cde5d78bef4f388cf89c0dfc2faf4a00393f7f90..1aa0bf87d0e64fbce259d9e731db24b8f158760f 100644 (file)
@@ -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):
index 22053b4924896f5df9785869727ba93a233187e0..e15941aec8e175798538bbad948b422ae66067a0 100644 (file)
@@ -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;