From 936f6a16b9ef85bd56b18a247b962801e954c30e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 4 Sep 2021 21:02:21 +0300 Subject: 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. --- Lib/test/test_range.py | 10 +++++-- .../2021-08-27-19-01-23.bpo-45030.tAmBbY.rst | 1 + Objects/rangeobject.c | 34 ++++++++++++---------- 3 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-08-27-19-01-23.bpo-45030.tAmBbY.rst diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 897162b..851ad5b 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -375,8 +375,14 @@ class RangeTest(unittest.TestCase): def test_iterator_pickling(self): 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**63-3, 2**63-1), (2**65, 2**65+2)] + (-2, 2, 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 t in testcases: with self.subTest(proto=proto, t=t): diff --git a/Misc/NEWS.d/next/Library/2021-08-27-19-01-23.bpo-45030.tAmBbY.rst b/Misc/NEWS.d/next/Library/2021-08-27-19-01-23.bpo-45030.tAmBbY.rst new file mode 100644 index 0000000..dec8c88 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-08-27-19-01-23.bpo-45030.tAmBbY.rst @@ -0,0 +1 @@ +Fix integer overflow in pickling and copying the range iterator. diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 5c3230d..a848d67 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -915,22 +915,14 @@ get_len_of_range(long lo, long hi, long step) is not representable as a C long, OverflowError is raised. */ 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); - unsigned long ulen; if (it == NULL) return NULL; it->start = start; it->step = step; - ulen = get_len_of_range(start, stop, step); - 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->len = len; it->index = 0; return (PyObject *)it; } @@ -1092,7 +1084,7 @@ range_iter(PyObject *seq) rangeobject *r = (rangeobject *)seq; longrangeiterobject *it; long lstart, lstop, lstep; - PyObject *int_it; + unsigned long ulen; assert(PyRange_Check(seq)); @@ -1113,12 +1105,22 @@ range_iter(PyObject *seq) PyErr_Clear(); goto long_range; } - int_it = fast_range_iter(lstart, lstop, lstep); - if (int_it == NULL && PyErr_ExceptionMatches(PyExc_OverflowError)) { - PyErr_Clear(); + ulen = get_len_of_range(lstart, lstop, lstep); + if (ulen > (unsigned long)LONG_MAX) { 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: it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type); @@ -1204,7 +1206,7 @@ range_reverse(PyObject *seq, PyObject *Py_UNUSED(ignored)) new_stop = lstart - 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: it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type); -- cgit v0.12