From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Tue, 19 Sep 2017 14:00:44 +0000 (-0700) Subject: [3.6] bpo-31293: Fix crashes in truediv and mul of a timedelta by a float with a... X-Git-Tag: v3.6.4rc1~247 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f37dd11f0d4832c15d49c2ddc83d533ddaa36e74;p=python [3.6] bpo-31293: Fix crashes in truediv and mul of a timedelta by a float with a bad as_integer_ratio() method. (GH-3227) (#3654) (cherry picked from commit 865e4b4f630e2ae91e61239258abb58b488f1d65) --- diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index b25e6c1713..77df1b3744 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -846,6 +846,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 index 0000000000..28b7bfb2ef --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-08-28-17-51-42.bpo-31293.eMYZXj.rst @@ -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. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index c2ad9a203e..d44bb3e047 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1646,6 +1646,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) { @@ -1656,9 +1683,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; @@ -1754,9 +1782,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;