]> granicus.if.org Git - python/commitdiff
bpo-31293: Fix crashes in truediv and mul of a timedelta by a float with a bad as_int...
authorOren Milman <orenmn@gmail.com>
Tue, 19 Sep 2017 12:58:11 +0000 (15:58 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Tue, 19 Sep 2017 12:58:11 +0000 (15:58 +0300)
Lib/test/datetimetester.py
Misc/NEWS.d/next/Core and Builtins/2017-08-28-17-51-42.bpo-31293.eMYZXj.rst [new file with mode: 0644]
Modules/_datetimemodule.c

index 29b70e1a8a0c408d9ca4950859cab96ea11dbb6e..a042efd878b341661c3cc0b9219ce0ef2915692d 100644 (file)
@@ -866,6 +866,26 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
 
         self.assertRaises(TypeError, divmod, t, 10)
 
+    def test_issue31293(self):
+        # The interpreter shouldn't crash in case a timedelta is divided or
+        # multiplied by a float with a bad as_integer_ratio() method.
+        def get_bad_float(bad_ratio):
+            class BadFloat(float):
+                def as_integer_ratio(self):
+                    return bad_ratio
+            return BadFloat()
+
+        with self.assertRaises(TypeError):
+            timedelta() / get_bad_float(1 << 1000)
+        with self.assertRaises(TypeError):
+            timedelta() * get_bad_float(1 << 1000)
+
+        for bad_ratio in [(), (42, ), (1, 2, 3)]:
+            with self.assertRaises(ValueError):
+                timedelta() / get_bad_float(bad_ratio)
+            with self.assertRaises(ValueError):
+                timedelta() * get_bad_float(bad_ratio)
+
 
 #############################################################################
 # date tests
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-08-28-17-51-42.bpo-31293.eMYZXj.rst b/Misc/NEWS.d/next/Core and Builtins/2017-08-28-17-51-42.bpo-31293.eMYZXj.rst
new file mode 100644 (file)
index 0000000..28b7bfb
--- /dev/null
@@ -0,0 +1,2 @@
+Fix crashes in true division and multiplication of a timedelta object by a
+float with a bad as_integer_ratio() method. Patch by Oren Milman.
index 619ac84bf747dfe8bb0b6909d6bcbb18e92ae30d..3dd7f827509afcdb385d68e452ccccf6c5cd3e3e 100644 (file)
@@ -1650,6 +1650,33 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
     return result;
 }
 
+static PyObject *
+get_float_as_integer_ratio(PyObject *floatobj)
+{
+    PyObject *ratio;
+
+    assert(floatobj && PyFloat_Check(floatobj));
+    ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL);
+    if (ratio == NULL) {
+        return NULL;
+    }
+    if (!PyTuple_Check(ratio)) {
+        PyErr_Format(PyExc_TypeError,
+                     "unexpected return type from as_integer_ratio(): "
+                     "expected tuple, got '%.200s'",
+                     Py_TYPE(ratio)->tp_name);
+        Py_DECREF(ratio);
+        return NULL;
+    }
+    if (PyTuple_Size(ratio) != 2) {
+        PyErr_SetString(PyExc_ValueError,
+                        "as_integer_ratio() must return a 2-tuple");
+        Py_DECREF(ratio);
+        return NULL;
+    }
+    return ratio;
+}
+
 static PyObject *
 multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta)
 {
@@ -1660,9 +1687,10 @@ multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta)
     pyus_in = delta_to_microseconds(delta);
     if (pyus_in == NULL)
         return NULL;
-    ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL);
-    if (ratio == NULL)
+    ratio = get_float_as_integer_ratio(floatobj);
+    if (ratio == NULL) {
         goto error;
+    }
     temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 0));
     Py_DECREF(pyus_in);
     pyus_in = NULL;
@@ -1758,9 +1786,10 @@ truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *f)
     pyus_in = delta_to_microseconds(delta);
     if (pyus_in == NULL)
         return NULL;
-    ratio = _PyObject_CallMethodId(f, &PyId_as_integer_ratio, NULL);
-    if (ratio == NULL)
+    ratio = get_float_as_integer_ratio(f);
+    if (ratio == NULL) {
         goto error;
+    }
     temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 1));
     Py_DECREF(pyus_in);
     pyus_in = NULL;