diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 29b70e1a8a0..a042efd878b 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -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 index 00000000000..28b7bfb2ef3 --- /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 619ac84bf74..3dd7f827509 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -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;