From: Serhiy Storchaka Date: Mon, 23 Oct 2017 16:57:04 +0000 (+0300) Subject: [2.7] bpo-31752: Fix possible crash in timedelta constructor called with custom integ... X-Git-Tag: v2.7.15rc1~159 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5ef883b096895a84123760859f0f34ad37bf2277;p=python [2.7] bpo-31752: Fix possible crash in timedelta constructor called with custom integers. (GH-3947) (#4088) Bad remainder in divmod() in intermediate calculations caused an assertion failure.. (cherry picked from commit 4ffd4653a7ec9c97775472276cf5e159e2366bb2) --- diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 20abe74bbc..f56c3f5fe3 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -492,6 +492,46 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase): self.assertEqual(str(t3), str(t4)) self.assertEqual(t4.as_hours(), -1) + def test_issue31752(self): + # The interpreter shouldn't crash because divmod() returns negative + # remainder. + class BadInt(int): + def __mul__(self, other): + return Prod() + + class BadLong(long): + def __mul__(self, other): + return Prod() + + class Prod: + def __radd__(self, other): + return Sum() + + class Sum(int): + def __divmod__(self, other): + # negative remainder + return (0, -1) + + timedelta(microseconds=BadInt(1)) + timedelta(hours=BadInt(1)) + timedelta(weeks=BadInt(1)) + timedelta(microseconds=BadLong(1)) + timedelta(hours=BadLong(1)) + timedelta(weeks=BadLong(1)) + + class Sum(long): + def __divmod__(self, other): + # negative remainder + return (0, -1) + + timedelta(microseconds=BadInt(1)) + timedelta(hours=BadInt(1)) + timedelta(weeks=BadInt(1)) + timedelta(microseconds=BadLong(1)) + timedelta(hours=BadLong(1)) + timedelta(weeks=BadLong(1)) + + ############################################################################# # date tests diff --git a/Misc/NEWS.d/next/Library/2017-10-11-00-45-01.bpo-31752.DhWevN.rst b/Misc/NEWS.d/next/Library/2017-10-11-00-45-01.bpo-31752.DhWevN.rst new file mode 100644 index 0000000000..4ec140b536 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-10-11-00-45-01.bpo-31752.DhWevN.rst @@ -0,0 +1 @@ +Fix possible crash in timedelta constructor called with custom integers. diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index 34b8ed41b8..e818c5cfdc 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -1537,6 +1537,7 @@ delta_to_microseconds(PyDateTime_Delta *self) if (x2 == NULL) goto Done; result = PyNumber_Add(x1, x2); + assert(result == NULL || PyInt_CheckExact(result) || PyLong_CheckExact(result)); Done: Py_XDECREF(x1); @@ -1559,6 +1560,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) PyObject *num = NULL; PyObject *result = NULL; + assert(PyInt_CheckExact(pyus) || PyLong_CheckExact(pyus)); tuple = PyNumber_Divmod(pyus, us_per_second); if (tuple == NULL) goto Done; @@ -1851,11 +1853,13 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor, assert(num != NULL); if (PyInt_Check(num) || PyLong_Check(num)) { - prod = PyNumber_Multiply(num, factor); + prod = PyNumber_Multiply(factor, num); if (prod == NULL) return NULL; + assert(PyInt_CheckExact(prod) || PyLong_CheckExact(prod)); sum = PyNumber_Add(sofar, prod); Py_DECREF(prod); + assert(sum == NULL || PyInt_CheckExact(sum) || PyLong_CheckExact(sum)); return sum; } @@ -1898,7 +1902,7 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor, * fractional part requires float arithmetic, and may * lose a little info. */ - assert(PyInt_Check(factor) || PyLong_Check(factor)); + assert(PyInt_CheckExact(factor) || PyLong_CheckExact(factor)); if (PyInt_Check(factor)) dnum = (double)PyInt_AsLong(factor); else @@ -1916,6 +1920,7 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor, Py_DECREF(sum); Py_DECREF(x); *leftover += fracpart; + assert(y == NULL || PyInt_CheckExact(y) || PyLong_CheckExact(y)); return y; }