mirror of https://github.com/python/cpython
bpo-45030: Fix integer overflow in __reduce__ of the range iterator (GH-28000)
It happened with fast range iterator when the calculated stop = start + step * len was out of the C long range.
This commit is contained in:
parent
c2970fdec5
commit
936f6a16b9
|
@ -375,8 +375,14 @@ class RangeTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_iterator_pickling(self):
|
def test_iterator_pickling(self):
|
||||||
testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), (13, 21, 3),
|
testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), (13, 21, 3),
|
||||||
(-2, 2, 2), (2**31-3, 2**31-1), (2**33, 2**33+2),
|
(-2, 2, 2)]
|
||||||
(2**63-3, 2**63-1), (2**65, 2**65+2)]
|
for M in 2**31, 2**63:
|
||||||
|
testcases += [
|
||||||
|
(M-3, M-1), (4*M, 4*M+2),
|
||||||
|
(M-2, M-1, 2), (-M+1, -M, -2),
|
||||||
|
(1, 2, M-1), (-1, -2, -M),
|
||||||
|
(1, M-1, M-1), (-1, -M, -M),
|
||||||
|
]
|
||||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||||
for t in testcases:
|
for t in testcases:
|
||||||
with self.subTest(proto=proto, t=t):
|
with self.subTest(proto=proto, t=t):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix integer overflow in pickling and copying the range iterator.
|
|
@ -915,22 +915,14 @@ get_len_of_range(long lo, long hi, long step)
|
||||||
is not representable as a C long, OverflowError is raised. */
|
is not representable as a C long, OverflowError is raised. */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
fast_range_iter(long start, long stop, long step)
|
fast_range_iter(long start, long stop, long step, long len)
|
||||||
{
|
{
|
||||||
rangeiterobject *it = PyObject_New(rangeiterobject, &PyRangeIter_Type);
|
rangeiterobject *it = PyObject_New(rangeiterobject, &PyRangeIter_Type);
|
||||||
unsigned long ulen;
|
|
||||||
if (it == NULL)
|
if (it == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
it->start = start;
|
it->start = start;
|
||||||
it->step = step;
|
it->step = step;
|
||||||
ulen = get_len_of_range(start, stop, step);
|
it->len = len;
|
||||||
if (ulen > (unsigned long)LONG_MAX) {
|
|
||||||
Py_DECREF(it);
|
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
|
||||||
"range too large to represent as a range_iterator");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
it->len = (long)ulen;
|
|
||||||
it->index = 0;
|
it->index = 0;
|
||||||
return (PyObject *)it;
|
return (PyObject *)it;
|
||||||
}
|
}
|
||||||
|
@ -1092,7 +1084,7 @@ range_iter(PyObject *seq)
|
||||||
rangeobject *r = (rangeobject *)seq;
|
rangeobject *r = (rangeobject *)seq;
|
||||||
longrangeiterobject *it;
|
longrangeiterobject *it;
|
||||||
long lstart, lstop, lstep;
|
long lstart, lstop, lstep;
|
||||||
PyObject *int_it;
|
unsigned long ulen;
|
||||||
|
|
||||||
assert(PyRange_Check(seq));
|
assert(PyRange_Check(seq));
|
||||||
|
|
||||||
|
@ -1113,12 +1105,22 @@ range_iter(PyObject *seq)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
goto long_range;
|
goto long_range;
|
||||||
}
|
}
|
||||||
int_it = fast_range_iter(lstart, lstop, lstep);
|
ulen = get_len_of_range(lstart, lstop, lstep);
|
||||||
if (int_it == NULL && PyErr_ExceptionMatches(PyExc_OverflowError)) {
|
if (ulen > (unsigned long)LONG_MAX) {
|
||||||
PyErr_Clear();
|
|
||||||
goto long_range;
|
goto long_range;
|
||||||
}
|
}
|
||||||
return (PyObject *)int_it;
|
/* check for potential overflow of lstart + ulen * lstep */
|
||||||
|
if (ulen) {
|
||||||
|
if (lstep > 0) {
|
||||||
|
if (lstop > LONG_MAX - (lstep - 1))
|
||||||
|
goto long_range;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (lstop < LONG_MIN + (-1 - lstep))
|
||||||
|
goto long_range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fast_range_iter(lstart, lstop, lstep, (long)ulen);
|
||||||
|
|
||||||
long_range:
|
long_range:
|
||||||
it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type);
|
it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type);
|
||||||
|
@ -1204,7 +1206,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored))
|
||||||
|
|
||||||
new_stop = lstart - lstep;
|
new_stop = lstart - lstep;
|
||||||
new_start = (long)(new_stop + ulen * lstep);
|
new_start = (long)(new_stop + ulen * lstep);
|
||||||
return fast_range_iter(new_start, new_stop, -lstep);
|
return fast_range_iter(new_start, new_stop, -lstep, (long)ulen);
|
||||||
|
|
||||||
long_range:
|
long_range:
|
||||||
it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type);
|
it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type);
|
||||||
|
|
Loading…
Reference in New Issue