[Python-checkins] [2.7] bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039) (GH-10617)
Serhiy Storchaka
webhook-mailer at python.org
Tue Nov 20 14:56:41 EST 2018
https://github.com/python/cpython/commit/40fdf471931f029ea07e6190f0fe116e0735661b
commit: 40fdf471931f029ea07e6190f0fe116e0735661b
branch: 2.7
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-11-20T21:56:34+02:00
summary:
[2.7] bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039) (GH-10617)
Fixes assertion failures in _datetimemodule.c
introduced in the previous fix (see bpo-31752).
Rather of trying to handle an int subclass as exact int,
let it to use overridden special methods, but check the
result of divmod().
(cherry picked from commit 3ec0f495163da3b7a15deb2805cec48aed432f58)
Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>
files:
M Lib/test/test_datetime.py
M Modules/datetimemodule.c
diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
index f56c3f5fe3de..2620fbb9fbf7 100644
--- a/Lib/test/test_datetime.py
+++ b/Lib/test/test_datetime.py
@@ -495,41 +495,64 @@ def as_hours(self):
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))
+ for inttype in (int, long):
+ class BadInt(inttype):
+ def __mul__(self, other):
+ return Prod()
+ def __rmul__(self, other):
+ return Prod()
+ def __floordiv__(self, other):
+ return Prod()
+ def __rfloordiv__(self, other):
+ return Prod()
+
+ class BadLong(long):
+ def __mul__(self, other):
+ return Prod()
+ def __rmul__(self, other):
+ return Prod()
+ def __floordiv__(self, other):
+ return Prod()
+ def __rfloordiv__(self, other):
+ return Prod()
+
+ class Prod:
+ def __add__(self, other):
+ return Sum()
+ def __radd__(self, other):
+ return Sum()
+
+ for inttype2 in (int, long):
+ class Sum(inttype2):
+ def __divmod__(self, other):
+ return divmodresult
+
+ for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
+ # The following examples should not crash.
+ try:
+ timedelta(microseconds=BadInt(1))
+ except TypeError:
+ pass
+ try:
+ timedelta(hours=BadInt(1))
+ except TypeError:
+ pass
+ try:
+ timedelta(weeks=BadInt(1))
+ except (TypeError, ValueError):
+ pass
+ try:
+ timedelta(1) * BadInt(1)
+ except (TypeError, ValueError):
+ pass
+ try:
+ BadInt(1) * timedelta(1)
+ except TypeError:
+ pass
+ try:
+ timedelta(1) // BadInt(1)
+ except TypeError:
+ pass
#############################################################################
diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c
index c0b7102de234..29492de5167c 100644
--- a/Modules/datetimemodule.c
+++ b/Modules/datetimemodule.c
@@ -1546,6 +1546,29 @@ delta_to_microseconds(PyDateTime_Delta *self)
return result;
}
+static PyObject *
+checked_divmod(PyObject *a, PyObject *b)
+{
+ PyObject *result = PyNumber_Divmod(a, b);
+ if (result != NULL) {
+ if (!PyTuple_Check(result)) {
+ PyErr_Format(PyExc_TypeError,
+ "divmod() returned non-tuple (type %.200s)",
+ result->ob_type->tp_name);
+ Py_DECREF(result);
+ return NULL;
+ }
+ if (PyTuple_GET_SIZE(result) != 2) {
+ PyErr_Format(PyExc_TypeError,
+ "divmod() returned a tuple of size %zd",
+ PyTuple_GET_SIZE(result));
+ Py_DECREF(result);
+ return NULL;
+ }
+ }
+ return result;
+}
+
/* Convert a number of us (as a Python int or long) to a timedelta.
*/
static PyObject *
@@ -1554,70 +1577,49 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
int us;
int s;
int d;
- long temp;
PyObject *tuple = NULL;
PyObject *num = NULL;
PyObject *result = NULL;
- assert(_PyAnyInt_CheckExact(pyus));
- tuple = PyNumber_Divmod(pyus, us_per_second);
- if (tuple == NULL)
+ tuple = checked_divmod(pyus, us_per_second);
+ if (tuple == NULL) {
goto Done;
+ }
- num = PyTuple_GetItem(tuple, 1); /* us */
- if (num == NULL)
- goto Done;
- temp = PyLong_AsLong(num);
+ num = PyTuple_GET_ITEM(tuple, 1); /* us */
+ us = _PyLong_AsInt(num);
num = NULL;
- if (temp == -1 && PyErr_Occurred())
- goto Done;
- assert(0 <= temp && temp < 1000000);
- us = (int)temp;
- if (us < 0) {
- /* The divisor was positive, so this must be an error. */
- assert(PyErr_Occurred());
+ if (us == -1 && PyErr_Occurred()) {
goto Done;
}
+ if (!(0 <= us && us < 1000000)) {
+ goto BadDivmod;
+ }
- num = PyTuple_GetItem(tuple, 0); /* leftover seconds */
- if (num == NULL)
- goto Done;
+ num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
Py_INCREF(num);
Py_DECREF(tuple);
- tuple = PyNumber_Divmod(num, seconds_per_day);
+ tuple = checked_divmod(num, seconds_per_day);
if (tuple == NULL)
goto Done;
Py_DECREF(num);
- num = PyTuple_GetItem(tuple, 1); /* seconds */
- if (num == NULL)
- goto Done;
- temp = PyLong_AsLong(num);
+ num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
+ s = _PyLong_AsInt(num);
num = NULL;
- if (temp == -1 && PyErr_Occurred())
- goto Done;
- assert(0 <= temp && temp < 24*3600);
- s = (int)temp;
-
- if (s < 0) {
- /* The divisor was positive, so this must be an error. */
- assert(PyErr_Occurred());
+ if (s == -1 && PyErr_Occurred()) {
goto Done;
}
+ if (!(0 <= s && s < 24*3600)) {
+ goto BadDivmod;
+ }
- num = PyTuple_GetItem(tuple, 0); /* leftover days */
- if (num == NULL)
- goto Done;
+ num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
Py_INCREF(num);
- temp = PyLong_AsLong(num);
- if (temp == -1 && PyErr_Occurred())
- goto Done;
- d = (int)temp;
- if ((long)d != temp) {
- PyErr_SetString(PyExc_OverflowError, "normalized days too "
- "large to fit in a C int");
+ d = _PyLong_AsInt(num);
+ if (d == -1 && PyErr_Occurred()) {
goto Done;
}
result = new_delta_ex(d, s, us, 0, type);
@@ -1626,6 +1628,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
Py_XDECREF(tuple);
Py_XDECREF(num);
return result;
+
+BadDivmod:
+ PyErr_SetString(PyExc_TypeError,
+ "divmod() returned a value out of range");
+ goto Done;
}
#define microseconds_to_delta(pymicros) \
@@ -1642,7 +1649,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
if (pyus_in == NULL)
return NULL;
- pyus_out = PyNumber_Multiply(pyus_in, intobj);
+ pyus_out = PyNumber_Multiply(intobj, pyus_in);
Py_DECREF(pyus_in);
if (pyus_out == NULL)
return NULL;
@@ -1853,13 +1860,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
assert(num != NULL);
if (_PyAnyInt_Check(num)) {
- prod = PyNumber_Multiply(factor, num);
+ prod = PyNumber_Multiply(num, factor);
if (prod == NULL)
return NULL;
- assert(_PyAnyInt_CheckExact(prod));
sum = PyNumber_Add(sofar, prod);
Py_DECREF(prod);
- assert(sum == NULL || _PyAnyInt_CheckExact(sum));
return sum;
}
@@ -1920,7 +1925,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
Py_DECREF(sum);
Py_DECREF(x);
*leftover += fracpart;
- assert(y == NULL || _PyAnyInt_CheckExact(y));
return y;
}
More information about the Python-checkins
mailing list