[Python-checkins] cpython (3.5): Issue #26974: Fix segfault in the presence of absurd subclassing. Proactively

stefan.krah python-checkins at python.org
Sun Jul 17 08:14:05 EDT 2016


https://hg.python.org/cpython/rev/f8cb955efd6a
changeset:   102384:f8cb955efd6a
branch:      3.5
parent:      102382:4c07faa33915
user:        Stefan Krah <skrah at bytereef.org>
date:        Sun Jul 17 14:01:42 2016 +0200
summary:
  Issue #26974: Fix segfault in the presence of absurd subclassing. Proactively
eliminate all internal uses of overridden methods.

files:
  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
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -5398,6 +5398,34 @@
             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
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -2208,6 +2208,14 @@
     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 @@
     }
 
     /* 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 @@
     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 @@
 #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 @@
     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;

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list