[Python-checkins] bpo-45030: Fix integer overflow in __reduce__ of the range iterator (GH-28000)

serhiy-storchaka webhook-mailer at python.org
Sat Sep 4 14:02:34 EDT 2021


https://github.com/python/cpython/commit/936f6a16b9ef85bd56b18a247b962801e954c30e
commit: 936f6a16b9ef85bd56b18a247b962801e954c30e
branch: main
author: Serhiy Storchaka <storchaka at gmail.com>
committer: serhiy-storchaka <storchaka at gmail.com>
date: 2021-09-04T21:02:21+03:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2021-08-27-19-01-23.bpo-45030.tAmBbY.rst
M Lib/test/test_range.py
M Objects/rangeobject.c

diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py
index 897162b2b17457..851ad5b7c2f485 100644
--- a/Lib/test/test_range.py
+++ b/Lib/test/test_range.py
@@ -375,8 +375,14 @@ def test_pickling(self):
 
     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 00000000000000..dec8c88b155888
--- /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 5c3230d860f8f1..a848d67a65152e 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);



More information about the Python-checkins mailing list