Skip to content

Commit 4ffd465

Browse files
bpo-31752: Fix possible crash in timedelta constructor called with custom integers. (#3947)
Bad remainder in divmod() in intermediate calculations caused an assertion failure.
1 parent 7fed7bd commit 4ffd465

File tree

3 files changed

+28
-2
lines changed

3 files changed

+28
-2
lines changed

Lib/test/datetimetester.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,26 @@ def as_integer_ratio(self):
886886
with self.assertRaises(ValueError):
887887
timedelta() * get_bad_float(bad_ratio)
888888

889+
def test_issue31752(self):
890+
# The interpreter shouldn't crash because divmod() returns negative
891+
# remainder.
892+
class BadInt(int):
893+
def __mul__(self, other):
894+
return Prod()
895+
896+
class Prod:
897+
def __radd__(self, other):
898+
return Sum()
899+
900+
class Sum(int):
901+
def __divmod__(self, other):
902+
# negative remainder
903+
return (0, -1)
904+
905+
timedelta(microseconds=BadInt(1))
906+
timedelta(hours=BadInt(1))
907+
timedelta(weeks=BadInt(1))
908+
889909

890910
#############################################################################
891911
# date tests
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix possible crash in timedelta constructor called with custom integers.

Modules/_datetimemodule.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,7 @@ delta_to_microseconds(PyDateTime_Delta *self)
15371537
if (x2 == NULL)
15381538
goto Done;
15391539
result = PyNumber_Add(x1, x2);
1540+
assert(result == NULL || PyLong_CheckExact(result));
15401541

15411542
Done:
15421543
Py_XDECREF(x1);
@@ -1559,6 +1560,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
15591560
PyObject *num = NULL;
15601561
PyObject *result = NULL;
15611562

1563+
assert(PyLong_CheckExact(pyus));
15621564
tuple = PyNumber_Divmod(pyus, us_per_second);
15631565
if (tuple == NULL)
15641566
goto Done;
@@ -2081,11 +2083,13 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
20812083
assert(num != NULL);
20822084

20832085
if (PyLong_Check(num)) {
2084-
prod = PyNumber_Multiply(num, factor);
2086+
prod = PyNumber_Multiply(factor, num);
20852087
if (prod == NULL)
20862088
return NULL;
2089+
assert(PyLong_CheckExact(prod));
20872090
sum = PyNumber_Add(sofar, prod);
20882091
Py_DECREF(prod);
2092+
assert(sum == NULL || PyLong_CheckExact(sum));
20892093
return sum;
20902094
}
20912095

@@ -2128,7 +2132,7 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
21282132
* fractional part requires float arithmetic, and may
21292133
* lose a little info.
21302134
*/
2131-
assert(PyLong_Check(factor));
2135+
assert(PyLong_CheckExact(factor));
21322136
dnum = PyLong_AsDouble(factor);
21332137

21342138
dnum *= fracpart;
@@ -2143,6 +2147,7 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
21432147
Py_DECREF(sum);
21442148
Py_DECREF(x);
21452149
*leftover += fracpart;
2150+
assert(y == NULL || PyLong_CheckExact(y));
21462151
return y;
21472152
}
21482153

0 commit comments

Comments
 (0)