aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-12-04 13:20:19 +0100
committerGitHub <noreply@github.com>2023-12-04 12:20:19 +0000
commite3d380ded6efd25d60a21b7eb913142e86153e33 (patch)
treeffb839eb0e0e46254497aa2ff7fdcc8fe675a736
parent[3.11] gh-101100: Fix sphinx warnings in `Doc/library/__future__.rst` (GH-109... (diff)
downloadcpython-e3d380ded6efd25d60a21b7eb913142e86153e33.tar.gz
cpython-e3d380ded6efd25d60a21b7eb913142e86153e33.tar.bz2
cpython-e3d380ded6efd25d60a21b7eb913142e86153e33.zip
[3.11] gh-109786: Fix leaks and crash when re-enter itertools.pairwise.__next__() (GH-109788) (GH-112700)
(cherry picked from commit 6ca9d3e0173c38e2eac50367b187d4c1d43f9892) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
-rw-r--r--Lib/test/test_itertools.py72
-rw-r--r--Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst2
-rw-r--r--Modules/itertoolsmodule.c13
3 files changed, 85 insertions, 2 deletions
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 0d0030e4160..c912ceba61a 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -1079,6 +1079,78 @@ class TestBasicOps(unittest.TestCase):
with self.assertRaises(TypeError):
pairwise(None) # non-iterable argument
+ def test_pairwise_reenter(self):
+ def check(reenter_at, expected):
+ class I:
+ count = 0
+ def __iter__(self):
+ return self
+ def __next__(self):
+ self.count +=1
+ if self.count in reenter_at:
+ return next(it)
+ return [self.count] # new object
+
+ it = pairwise(I())
+ for item in expected:
+ self.assertEqual(next(it), item)
+
+ check({1}, [
+ (([2], [3]), [4]),
+ ([4], [5]),
+ ])
+ check({2}, [
+ ([1], ([1], [3])),
+ (([1], [3]), [4]),
+ ([4], [5]),
+ ])
+ check({3}, [
+ ([1], [2]),
+ ([2], ([2], [4])),
+ (([2], [4]), [5]),
+ ([5], [6]),
+ ])
+ check({1, 2}, [
+ ((([3], [4]), [5]), [6]),
+ ([6], [7]),
+ ])
+ check({1, 3}, [
+ (([2], ([2], [4])), [5]),
+ ([5], [6]),
+ ])
+ check({1, 4}, [
+ (([2], [3]), (([2], [3]), [5])),
+ ((([2], [3]), [5]), [6]),
+ ([6], [7]),
+ ])
+ check({2, 3}, [
+ ([1], ([1], ([1], [4]))),
+ (([1], ([1], [4])), [5]),
+ ([5], [6]),
+ ])
+
+ def test_pairwise_reenter2(self):
+ def check(maxcount, expected):
+ class I:
+ count = 0
+ def __iter__(self):
+ return self
+ def __next__(self):
+ if self.count >= maxcount:
+ raise StopIteration
+ self.count +=1
+ if self.count == 1:
+ return next(it, None)
+ return [self.count] # new object
+
+ it = pairwise(I())
+ self.assertEqual(list(it), expected)
+
+ check(1, [])
+ check(2, [])
+ check(3, [])
+ check(4, [(([2], [3]), [4])])
+
def test_product(self):
for args, result in [
([], [()]), # zero iterables
diff --git a/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst b/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst
new file mode 100644
index 00000000000..07222fa339d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-09-23-14-40-51.gh-issue-109786.UX3pKv.rst
@@ -0,0 +1,2 @@
+Fix possible reference leaks and crash when re-enter the ``__next__()`` method of
+:class:`itertools.pairwise`.
diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c
index d5cdfc591e2..7a02a0d25b2 100644
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -119,21 +119,30 @@ pairwise_next(pairwiseobject *po)
return NULL;
}
if (old == NULL) {
- po->old = old = (*Py_TYPE(it)->tp_iternext)(it);
+ old = (*Py_TYPE(it)->tp_iternext)(it);
+ Py_XSETREF(po->old, old);
if (old == NULL) {
Py_CLEAR(po->it);
return NULL;
}
+ it = po->it;
+ if (it == NULL) {
+ Py_CLEAR(po->old);
+ return NULL;
+ }
}
+ Py_INCREF(old);
new = (*Py_TYPE(it)->tp_iternext)(it);
if (new == NULL) {
Py_CLEAR(po->it);
Py_CLEAR(po->old);
+ Py_DECREF(old);
return NULL;
}
/* Future optimization: Reuse the result tuple as we do in enumerate() */
result = PyTuple_Pack(2, old, new);
- Py_SETREF(po->old, new);
+ Py_XSETREF(po->old, new);
+ Py_DECREF(old);
return result;
}