8000 [2.7] bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-100… · python/cpython@40fdf47 · GitHub
[go: up one dir, main page]

Skip to content

Commit 40fdf47

Browse files
[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 3ec0f49) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 4845aa6 commit 40fdf47

File tree

2 files changed

+108
-81
lines changed

2 files changed

+108
-81
lines changed

Lib/test/test_datetime.py

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -495,41 +495,64 @@ def as_hours(self):
495495
def test_issue31752(self):
496496
# The interpreter shouldn't crash because divmod() returns negative
497497
# remainder.
498-
class BadInt(int):
499-
def __mul__(self, other):
500-
return Prod()
501-
502-
class BadLong(long):
503-
def __mul__(self, other):
504-
return Prod()
505-
506-
class Prod:
507-
def __radd__(self, other):
508-
return Sum()
509-
510-
class Sum(int):
511-
def __divmod__(self, other):
512-
# negative remainder
513-
return (0, -1)
514-
515-
timedelta(microseconds=BadInt(1))
516-
timedelta(hours=BadInt(1))
517-
timedelta(weeks=BadInt(1))
518-
timedelta(microseconds=BadLong(1))
519-
timedelta(hours=BadLong(1))
520-
timedelta(weeks=BadLong(1))
521-
522-
class Sum(long):
523-
def __divmod__(self, other):
524-
# negative remainder
525-
return (0, -1)
526-
527-
timedelta(microseconds=BadInt(1))
528-
timedelta(hours=BadInt(1))
529-
timedelta(weeks=BadInt(1))
530-
timedelta(microseconds=BadLong(1))
531-
timedelta(hours=BadLong(1))
532-
timedelta(weeks=BadLong(1))
498+
for inttype in (int, long):
499+
class BadInt(inttype):
500+
def __mul__(self, other):
501+
return Prod()
502+
def __rmul__(self, other):
503+
return Prod()
504+
def __floordiv__(self, other):
505+
return Prod()
506+
def __rfloordiv__(self, other):
507+
return Prod()
508+
509+
class BadLong(long):
510+
def __mul__(self, other):
511+
return Prod()
512+
def __rmul__(self, other):
513+
return Prod()
514+
def __floordiv__(self, other):
515+
return Prod()
516+
def __rfloordiv__(self, other):
517+
return Prod()
518+
519+
class Prod:
520+
def __add__(self, other):
521+
return Sum()
522+
def __radd__(self, other):
523+
return Sum()
524+
525+
for inttype2 in (int, long):
526+
class Sum(inttype2):
527+
def __divmod__(self, other):
528+
return divmodresult
529+
530+
for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
531+
# The following examples should not crash.
532+
try:
533+
timedelta(microseconds=BadInt(1))
534+
except TypeError:
535+
pass
536+
try:
537+
timedelta(hours=BadInt(1))
538+
except TypeError:
539+
pass
540+
try:
541+
timedelta(weeks=BadInt(1))
542+
except (TypeError, ValueError):
543+
pass
544+
try:
545+
timedelta(1) * BadInt(1)
546+
except (TypeError, ValueError):
547+
pass
548+
try:
549+
BadInt(1) * timedelta(1)
550+
except TypeError:
551+
pass
552+
try:
553+
timedelta(1) // BadInt(1)
554+
except TypeError:
555+
pass
533556

534557

535558
#############################################################################

Modules/datetimemodule.c

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,29 @@ delta_to_microseconds(PyDateTime_Delta *self)
15461546
return result;
15471547
}
15481548

1549+
static PyObject *
1550+
checked_divmod(PyObject *a, PyObject *b)
1551+
{
1552+
PyObject *result = PyNumber_Divmod(a, b);
1553+
if (result != NULL) {
1554+
if (!PyTuple_Check(result)) {
1555+
PyErr_Format(PyExc_TypeError,
1556+
"divmod() returned non-tuple (type %.200s)",
1557+
result->ob_type->tp_name);
1558+
Py_DECREF(result);
1559+
return NULL;
1560+
}
1561+
if (PyTuple_GET_SIZE(result) != 2) {
1562+
PyErr_Format(PyExc_TypeError,
1563+
"divmod() returned a tuple of size %zd",
1564+
PyTuple_GET_SIZE(result));
1565+
Py_DECREF(result);
1566+
return NULL;
1567+
}
1568+
}
1569+
return result;
1570+
}
1571+
15491572
/* Convert a number of us (as a Python int or long) to a timedelta.
15501573
*/
15511574
static PyObject *
@@ -1554,70 +1577,49 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
15541577
int us;
15551578
int s;
15561579
int d;
1557-
long temp;
15581580

15591581
PyObject *tuple = NULL;
15601582
PyObject *num = NULL;
15611583
PyObject *result = NULL;
15621584

1563-
assert(_PyAnyInt_CheckExact(pyus));
1564-
tuple = PyNumber_Divmod(pyus, us_per_second);
1565-
if (tuple == NULL)
1585+
tuple = checked_divmod(pyus, us_per_second);
1586+
if (tuple == NULL) {
15661587
goto Done;
1588+
}
15671589

1568-
num = PyTuple_GetItem(tuple, 1); /* us */
1569-
if (num == NULL)
1570-
goto Done;
1571-
temp = PyLong_AsLong(num);
1590+
num = PyTuple_GET_ITEM(tuple, 1); /* us */
1591+
us = _PyLong_AsInt(num);
15721592
num = NULL;
1573-
if (temp == -1 && PyErr_Occurred())
1574-
goto Done;
1575-
assert(0 <= temp && temp < 1000000);
1576-
us = (int)temp;
1577-
if (us < 0) {
1578-
/* The divisor was positive, so this must be an error. */
1579-
assert(PyErr_Occurred());
1593+
if (us == -1 && PyErr_Occurred()) {
15801594
goto Done;
15811595
}
1596+
if (!(0 <= us && us < 1000000)) {
1597+
goto BadDivmod;
1598+
}
15821599

1583-
num = PyTuple_GetItem(tuple, 0); /* leftover seconds */
1584-
if (num == NULL)
1585-
goto Done;
1600+
num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
15861601
Py_INCREF(num);
15871602
Py_DECREF(tuple);
15881603

1589-
tuple = PyNumber_Divmod(num, seconds_per_day);
1604+
tuple = checked_divmod(num, seconds_per_day);
15901605
if (tuple == NULL)
15911606
goto Done;
15921607
Py_DECREF(num);
15931608

1594-
num = PyTuple_GetItem(tuple, 1); /* seconds */
1595-
if (num == NULL)
1596-
goto Done;
1597-
temp = PyLong_AsLong(num);
1609+
num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
1610+
s = _PyLong_AsInt(num);
15981611
num = NULL;
1599-
if (temp == -1 && PyErr_Occurred())
1600-
goto Done;
1601-
assert(0 <= temp && temp < 24*3600);
1602-
s = (int)temp;
1603-
1604-
if (s < 0) {
1605-
/* The divisor was positive, so this must be an error. */
1606-
assert(PyErr_Occurred());
1612+
if (s == -1 && PyErr_Occurred()) {
16071613
goto Done;
16081614
}
1615+
if (!(0 <= s && s < 24*3600)) {
1616+
goto BadDivmod;
1617+
}
16091618

1610-
num = PyTuple_GetItem(tuple, 0); /* leftover days */
1611-
if (num == NULL)
1612-
goto Done;
1619+
num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
16131620
Py_INCREF(num);
1614-
temp = PyLong_AsLong(num);
1615-
if (temp == -1 && PyErr_Occurred())
1616-
goto Done;
1617-
d = (int)temp;
1618-
if ((long)d != temp) {
1619-
PyErr_SetString(PyExc_OverflowError, "normalized days too "
1620-
"large to fit in a C int");
1621+
d = _PyLong_AsInt(num);
1622+
if (d == -1 && PyErr_Occurred()) {
16211623
goto Done;
16221624
}
16231625
result = new_delta_ex(d, s, us, 0, type);
@@ -1626,6 +1628,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
16261628
Py_XDECREF(tuple);
16271629
Py_XDECREF(num);
16281630
return result;
1631+
1632+
BadDivmod:
1633+
PyErr_SetString(PyExc_TypeError,
1634+
"divmod() returned a value out of range");
1635+
goto Done;
16291636
}
16301637

16311638
#define microseconds_to_delta(pymicros) \
@@ -1642,7 +1649,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
16421649
if (pyus_in == NULL)
16431650
return NULL;
16441651

1645-
pyus_out = PyNumber_Multiply(pyus_in, intobj);
1652+
pyus_out = PyNumber_Multiply(intobj, pyus_in);
16461653
Py_DECREF(pyus_in);
16471654
if (pyus_out == NULL)
16481655
return NULL;
@@ -1853,13 +1860,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
18531860
assert(num != NULL);
18541861

18551862
if (_PyAnyInt_Check(num)) {
1856-
prod = PyNumber_Multiply(factor, num);
1863+
prod = PyNumber_Multiply(num, factor);
18571864
if (prod == NULL)
18581865
return NULL;
1859-
assert(_PyAnyInt_CheckExact(prod));
18601866
sum = PyNumbe 81CD r_Add(sofar, prod);
18611867
Py_DECREF(prod);
1862-
assert(sum == NULL || _PyAnyInt_CheckExact(sum));
18631868
return sum;
18641869
}
18651870

@@ -1920,7 +1925,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
19201925
Py_DECREF(sum);
19211926
Py_DECREF(x);
19221927
*leftover += fracpart;
1923-
assert(y == NULL || _PyAnyInt_CheckExact(y));
19241928
return y;
19251929
}
19261930

0 commit comments

Comments
 (0)
0