Skip to content

Commit 7a0d964

Browse files
bpo-35021: Fix assertion failures in _datetimemodule.c. (GH-10039)
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 <[email protected]>
1 parent dac5124 commit 7a0d964

File tree

2 files changed

+88
-54
lines changed

2 files changed

+88
-54
lines changed

Lib/test/datetimetester.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -872,19 +872,50 @@ def test_issue31752(self):
872872
class BadInt(int):
873873
def __mul__(self, other):
874874
return Prod()
875+
def __rmul__(self, other):
876+
return Prod()
877+
def __floordiv__(self, other):
878+
return Prod()
879+
def __rfloordiv__(self, other):
880+
return Prod()
875881

876882
class Prod:
883+
def __add__(self, other):
884+
return Sum()
877885
def __radd__(self, other):
878886
return Sum()
879887

880888
class Sum(int):
881889
def __divmod__(self, other):
882-
# negative remainder
883-
return (0, -1)
884-
885-
timedelta(microseconds=BadInt(1))
886-
timedelta(hours=BadInt(1))
887-
timedelta(weeks=BadInt(1))
890+
return divmodresult
891+
892+
for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
893+
with self.subTest(divmodresult=divmodresult):
894+
# The following examples should not crash.
895+
try:
896+
timedelta(microseconds=BadInt(1))
897+
except TypeError:
898+
pass
899+
try:
900+
timedelta(hours=BadInt(1))
901+
except TypeError:
902+
pass
903+
try:
904+
timedelta(weeks=BadInt(1))
905+
except (TypeError, ValueError):
906+
pass
907+
try:
908+
timedelta(1) * BadInt(1)
909+
except (TypeError, ValueError):
910+
pass
911+
try:
912+
BadInt(1) * timedelta(1)
913+
except TypeError:
914+
pass
915+
try:
916+
timedelta(1) // BadInt(1)
917+
except TypeError:
918+
pass
888919

889920

890921
#############################################################################

Modules/_datetimemodule.c

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,29 @@ delta_to_microseconds(PyDateTime_Delta *self)
15421542
return result;
15431543
}
15441544

1545+
static PyObject *
1546+
checked_divmod(PyObject *a, PyObject *b)
1547+
{
1548+
PyObject *result = PyNumber_Divmod(a, b);
1549+
if (result != NULL) {
1550+
if (!PyTuple_Check(result)) {
1551+
PyErr_Format(PyExc_TypeError,
1552+
"divmod() returned non-tuple (type %.200s)",
1553+
result->ob_type->tp_name);
1554+
Py_DECREF(result);
1555+
return NULL;
1556+
}
1557+
if (PyTuple_GET_SIZE(result) != 2) {
1558+
PyErr_Format(PyExc_TypeError,
1559+
"divmod() returned a tuple of size %zd",
1560+
PyTuple_GET_SIZE(result));
1561+
Py_DECREF(result);
1562+
return NULL;
1563+
}
1564+
}
1565+
return result;
1566+
}
1567+
15451568
/* Convert a number of us (as a Python int) to a timedelta.
15461569
*/
15471570
static PyObject *
@@ -1550,70 +1573,49 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
15501573
int us;
15511574
int s;
15521575
int d;
1553-
long temp;
15541576

15551577
PyObject *tuple = NULL;
15561578
PyObject *num = NULL;
15571579
PyObject *result = NULL;
15581580

1559-
assert(PyLong_CheckExact(pyus));
1560-
tuple = PyNumber_Divmod(pyus, us_per_second);
1561-
if (tuple == NULL)
1581+
tuple = checked_divmod(pyus, us_per_second);
1582+
if (tuple == NULL) {
15621583
goto Done;
1584+
}
15631585

1564-
num = PyTuple_GetItem(tuple, 1); /* us */
1565-
if (num == NULL)
1566-
goto Done;
1567-
temp = PyLong_AsLong(num);
1586+
num = PyTuple_GET_ITEM(tuple, 1); /* us */
1587+
us = _PyLong_AsInt(num);
15681588
num = NULL;
1569-
if (temp == -1 && PyErr_Occurred())
1570-
goto Done;
1571-
assert(0 <= temp && temp < 1000000);
1572-
us = (int)temp;
1573-
if (us < 0) {
1574-
/* The divisor was positive, so this must be an error. */
1575-
assert(PyErr_Occurred());
1589+
if (us == -1 && PyErr_Occurred()) {
15761590
goto Done;
15771591
}
1592+
if (!(0 <= us && us < 1000000)) {
1593+
goto BadDivmod;
1594+
}
15781595

1579-
num = PyTuple_GetItem(tuple, 0); /* leftover seconds */
1580-
if (num == NULL)
1581-
goto Done;
1596+
num = PyTuple_GET_ITEM(tuple, 0); /* leftover seconds */
15821597
Py_INCREF(num);
15831598
Py_DECREF(tuple);
15841599

1585-
tuple = PyNumber_Divmod(num, seconds_per_day);
1600+
tuple = checked_divmod(num, seconds_per_day);
15861601
if (tuple == NULL)
15871602
goto Done;
15881603
Py_DECREF(num);
15891604

1590-
num = PyTuple_GetItem(tuple, 1); /* seconds */
1591-
if (num == NULL)
1592-
goto Done;
1593-
temp = PyLong_AsLong(num);
1605+
num = PyTuple_GET_ITEM(tuple, 1); /* seconds */
1606+
s = _PyLong_AsInt(num);
15941607
num = NULL;
1595-
if (temp == -1 && PyErr_Occurred())
1596-
goto Done;
1597-
assert(0 <= temp && temp < 24*3600);
1598-
s = (int)temp;
1599-
1600-
if (s < 0) {
1601-
/* The divisor was positive, so this must be an error. */
1602-
assert(PyErr_Occurred());
1608+
if (s == -1 && PyErr_Occurred()) {
16031609
goto Done;
16041610
}
1611+
if (!(0 <= s && s < 24*3600)) {
1612+
goto BadDivmod;
1613+
}
16051614

1606-
num = PyTuple_GetItem(tuple, 0); /* leftover days */
1607-
if (num == NULL)
1608-
goto Done;
1615+
num = PyTuple_GET_ITEM(tuple, 0); /* leftover days */
16091616
Py_INCREF(num);
1610-
temp = PyLong_AsLong(num);
1611-
if (temp == -1 && PyErr_Occurred())
1612-
goto Done;
1613-
d = (int)temp;
1614-
if ((long)d != temp) {
1615-
PyErr_SetString(PyExc_OverflowError, "normalized days too "
1616-
"large to fit in a C int");
1617+
d = _PyLong_AsInt(num);
1618+
if (d == -1 && PyErr_Occurred()) {
16171619
goto Done;
16181620
}
16191621
result = new_delta_ex(d, s, us, 0, type);
@@ -1622,6 +1624,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
16221624
Py_XDECREF(tuple);
16231625
Py_XDECREF(num);
16241626
return result;
1627+
1628+
BadDivmod:
1629+
PyErr_SetString(PyExc_TypeError,
1630+
"divmod() returned a value out of range");
1631+
goto Done;
16251632
}
16261633

16271634
#define microseconds_to_delta(pymicros) \
@@ -1638,7 +1645,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
16381645
if (pyus_in == NULL)
16391646
return NULL;
16401647

1641-
pyus_out = PyNumber_Multiply(pyus_in, intobj);
1648+
pyus_out = PyNumber_Multiply(intobj, pyus_in);
16421649
Py_DECREF(pyus_in);
16431650
if (pyus_out == NULL)
16441651
return NULL;
@@ -2073,13 +2080,12 @@ delta_divmod(PyObject *left, PyObject *right)
20732080
return NULL;
20742081
}
20752082

2076-
divmod = PyNumber_Divmod(pyus_left, pyus_right);
2083+
divmod = checked_divmod(pyus_left, pyus_right);
20772084
Py_DECREF(pyus_left);
20782085
Py_DECREF(pyus_right);
20792086
if (divmod == NULL)
20802087
return NULL;
20812088

2082-
assert(PyTuple_Size(divmod) == 2);
20832089
delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1));
20842090
if (delta == NULL) {
20852091
Py_DECREF(divmod);
@@ -2110,13 +2116,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
21102116
assert(num != NULL);
21112117

21122118
if (PyLong_Check(num)) {
2113-
prod = PyNumber_Multiply(factor, num);
2119+
prod = PyNumber_Multiply(num, factor);
21142120
if (prod == NULL)
21152121
return NULL;
2116-
assert(PyLong_CheckExact(prod));
21172122
sum = PyNumber_Add(sofar, prod);
21182123
Py_DECREF(prod);
2119-
assert(sum == NULL || PyLong_CheckExact(sum));
21202124
return sum;
21212125
}
21222126

@@ -2174,7 +2178,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
21742178
Py_DECREF(sum);
21752179
Py_DECREF(x);
21762180
*leftover += fracpart;
2177-
assert(y == NULL || PyLong_CheckExact(y));
21782181
return y;
21792182
}
21802183

0 commit comments

Comments
 (0)