Skip to content

Commit 865e4b4

Browse files
orenmnserhiy-storchaka
authored andcommitted
bpo-31293: Fix crashes in truediv and mul of a timedelta by a float with a bad as_integer_ratio() method. (#3227)
1 parent 9974e1b commit 865e4b4

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
@@ -866,6 +866,26 @@ def test_divmod(self):
866866

867867
self.assertRaises(TypeError, divmod, t, 10)
868868

869+
def test_issue31293(self):
870+
# The interpreter shouldn't crash in case a timedelta is divided or
871+
# multiplied by a float with a bad as_integer_ratio() method.
872+
def get_bad_float(bad_ratio):
873+
class BadFloat(float):
874+
def as_integer_ratio(self):
875+
return bad_ratio
876+
return BadFloat()
877+
878+
with self.assertRaises(TypeError):
879+
timedelta() / get_bad_float(1 << 1000)
880+
with self.assertRaises(TypeError):
881+
timedelta() * get_bad_float(1 << 1000)
882+
883+
for bad_ratio in [(), (42, ), (1, 2, 3)]:
884+
with self.assertRaises(ValueError):
885+
timedelta() / get_bad_float(bad_ratio)
886+
with self.assertRaises(ValueError):
887+
timedelta() * get_bad_float(bad_ratio)
888+
869889

870890
#############################################################################
871891
# 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
@@ -1650,6 +1650,33 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
16501650
return result;
16511651
}
16521652

1653+
static PyObject *
1654+
get_float_as_integer_ratio(PyObject *floatobj)
1655+
{
1656+
PyObject *ratio;
1657+
1658+
assert(floatobj && PyFloat_Check(floatobj));
1659+
ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL);
1660+
if (ratio == NULL) {
1661+
return NULL;
1662+
}
1663+
if (!PyTuple_Check(ratio)) {
1664+
PyErr_Format(PyExc_TypeError,
1665+
"unexpected return type from as_integer_ratio(): "
1666+
"expected tuple, got '%.200s'",
1667+
Py_TYPE(ratio)->tp_name);
1668+
Py_DECREF(ratio);
1669+
return NULL;
1670+
}
1671+
if (PyTuple_Size(ratio) != 2) {
1672+
PyErr_SetString(PyExc_ValueError,
1673+
"as_integer_ratio() must return a 2-tuple");
1674+
Py_DECREF(ratio);
1675+
return NULL;
1676+
}
1677+
return ratio;
1678+
}
1679+
16531680
static PyObject *
16541681
multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta)
16551682
{
@@ -1660,9 +1687,10 @@ multiply_float_timedelta(PyObject *floatobj, PyDateTime_Delta *delta)
16601687
pyus_in = delta_to_microseconds(delta);
16611688
if (pyus_in == NULL)
16621689
return NULL;
1663-
ratio = _PyObject_CallMethodId(floatobj, &PyId_as_integer_ratio, NULL);
1664-
if (ratio == NULL)
1690+
ratio = get_float_as_integer_ratio(floatobj);
1691+
if (ratio == NULL) {
16651692
goto error;
1693+
}
16661694
temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 0));
16671695
Py_DECREF(pyus_in);
16681696
pyus_in = NULL;
@@ -1758,9 +1786,10 @@ truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *f)
17581786
pyus_in = delta_to_microseconds(delta);
17591787
if (pyus_in == NULL)
17601788
return NULL;
1761-
ratio = _PyObject_CallMethodId(f, &PyId_as_integer_ratio, NULL);
1762-
if (ratio == NULL)
1789+
ratio = get_float_as_integer_ratio(f);
1790+
if (ratio == NULL) {
17631791
goto error;
1792+
}
17641793
temp = PyNumber_Multiply(pyus_in, PyTuple_GET_ITEM(ratio, 1));
17651794
Py_DECREF(pyus_in);
17661795
pyus_in = NULL;

0 commit comments

Comments
 (0)