Skip to content

Commit f37dd11

Browse files
miss-islingtonserhiy-storchaka
authored andcommitted
[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 865e4b4)
1 parent 99a51d4 commit f37dd11

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed

Lib/test/datetimetester.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,26 @@ def test_divmod(self):
846846

847847
self.assertRaises(TypeError, divmod, t, 10)
848848

849+
def test_issue31293(self):
850+
# The interpreter shouldn't crash in case a timedelta is divided or
851+
# multiplied by a float with a bad as_integer_ratio() method.
852+
def get_bad_float(bad_ratio):
853+
class BadFloat(float):
854+
def as_integer_ratio(self):
855+
return bad_ratio
856+
return BadFloat()
857+
858+
with self.assertRaises(TypeError):
859+
timedelta() / get_bad_float(1 << 1000)
860+
with self.assertRaises(TypeError):
861+
timedelta() * get_bad_float(1 << 1000)
862+
863+
for bad_ratio in [(), (42, ), (1, 2, 3)]:
864+
with self.assertRaises(ValueError):
865+
timedelta() / get_bad_float(bad_ratio)
866+
with self.assertRaises(ValueError):
867+
timedelta() * get_bad_float(bad_ratio)
868+
849869

850870
#############################################################################
851871
# date tests
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crashes in true division and multiplication of a timedelta object by a
2+
float with a bad as_integer_ratio() method. Patch by Oren Milman.

Modules/_datetimemodule.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,33 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
16461646
return result;
16471647
}
16481648

1649+
static PyObject *
1650+
get_float_as_integer_ratio(PyObject *floatobj)
1651+
{
1652+
PyObject *ratio;
1653+
1654+
assert(floatobj && PyFloat_Check(floatobj));
1655+
ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL);
1656+
if (ratio == NULL) {
1657+
return NULL;
1658+
}
1659+
if (!PyTuple_Check(ratio)) {
1660+
PyErr_Format(PyExc_TypeError,
1661+
"unexpected return type from as_integer_ratio(): "
1662+
"expected tuple, got '%.200s'",
1663+
Py_TYPE(ratio)->tp_name);
1664+
Py_DECREF(ratio);
1665+
return NULL;
1666+
}
1667+
if (PyTuple_Size(ratio) != 2) {
1668+
PyErr_SetString(PyExc_ValueError,
1669+
"as_integer_ratio() must return a 2-tuple");
1670+
Py_DECREF(ratio);
1671+
return NULL;
1672+
}
1673+
return ratio;
1674+
}
1675+
16491676
static PyObject *
16501677
multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta)
16511678
{
@@ -1656,9 +1683,10 @@ multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta)
16561683
pyus_in = delta_to_microseconds(delta);
16571684
if (pyus_in == NULL)
16581685
return NULL;
1659-
ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL);
1660-
if (ratio == NULL)
1686+
ratio = get_float_as_integer_ratio(floatobj);
1687+
if (ratio == NULL) {
16611688
goto error;
1689+
}
16621690
temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 0));
16631691
Py_DECREF(pyus_in);
16641692
pyus_in = NULL;
@@ -1754,9 +1782,10 @@ truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *f)
17541782
pyus_in = delta_to_microseconds(delta);
17551783
if (pyus_in == NULL)
17561784
return NULL;
1757-
ratio = _PyObject_CallMethodId(f, &PyId_as_integer_ratio, NULL);
1758-
if (ratio == NULL)
1785+
ratio = get_float_as_integer_ratio(f);
1786+
if (ratio == NULL) {
17591787
goto error;
1788+
}
17601789
temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 1));
17611790
Py_DECREF(pyus_in);
17621791
pyus_in = NULL;

0 commit comments

Comments
 (0)