summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOren Milman <orenmn@gmail.com>2017-09-19 12:58:11 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2017-09-19 12:58:11 (GMT)
commit865e4b4f630e2ae91e61239258abb58b488f1d65 (patch)
tree3c3ae126a58e5a9e7dddef58d305fc6faafad54e
parent9974e1bcf3d0cec9b38b39b39b7ec8a1ebd9ef54 (diff)
downloadcpython-865e4b4f630e2ae91e61239258abb58b488f1d65.zip
cpython-865e4b4f630e2ae91e61239258abb58b488f1d65.tar.gz
cpython-865e4b4f630e2ae91e61239258abb58b488f1d65.tar.bz2
bpo-31293: Fix crashes in truediv and mul of a timedelta by a float with a bad as_integer_ratio() method. (#3227)
-rw-r--r--Lib/test/datetimetester.py20
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2017-08-28-17-51-42.bpo-31293.eMYZXj.rst2
-rw-r--r--Modules/_datetimemodule.c37
3 files changed, 55 insertions, 4 deletions
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 29b70e1..a042efd 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 0000000..28b7bfb
--- /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 619ac84..3dd7f82 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -1651,6 +1651,33 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
}
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)
{
PyObject *result = NULL;
@@ -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;