Skip to content

Commit 71a1232

Browse files
committed
gh-129139: Fix __setstate__ on exhausted list iterators in free-threaded build
In free-threaded builds, exhausted list iterators signal exhaustion by setting it_index to -1 without clearing it_seq to NULL. The existing it_seq != NULL guard in listiter_setstate and listreviter_setstate allowed __setstate__ to revive an exhausted iterator by resetting it_index to a valid value. Add an it_index >= 0 check so that __setstate__ is a no-op on exhausted iterators, consistent with GIL-enabled build behavior.
1 parent 83360b5 commit 71a1232

File tree

3 files changed

+17
-2
lines changed

3 files changed

+17
-2
lines changed

Lib/test/test_list.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,20 @@ def test_reversed_pickle(self):
213213
a[:] = data
214214
self.assertEqual(list(it), [])
215215

216+
def test_exhausted_iterator_setstate(self):
217+
# gh-129139: __setstate__ on an exhausted iterator must not revive it
218+
it = iter([1, 2, 3])
219+
list(it) # exhaust the iterator
220+
it.__setstate__(0)
221+
self.assertRaises(StopIteration, next, it)
222+
223+
def test_exhausted_reversed_iterator_setstate(self):
224+
# gh-129139: __setstate__ on an exhausted reversed iterator must not revive it
225+
it = reversed([1, 2, 3])
226+
list(it) # exhaust the iterator
227+
it.__setstate__(0)
228+
self.assertRaises(StopIteration, next, it)
229+
216230
def test_step_overflow(self):
217231
a = [0, 1, 2, 3, 4]
218232
a[1::sys.maxsize] = [0]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In free-threaded build, fix ``__setstate__`` on exhausted list iterators and reversed list iterators reviving the iterator instead of being a no-op.

Objects/listobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4108,7 +4108,7 @@ listiter_setstate(PyObject *self, PyObject *state)
41084108
Py_ssize_t index = PyLong_AsSsize_t(state);
41094109
if (index == -1 && PyErr_Occurred())
41104110
return NULL;
4111-
if (it->it_seq != NULL) {
4111+
if (it->it_seq != NULL && FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index) >= 0) {
41124112
if (index < -1)
41134113
index = -1;
41144114
else if (index > PyList_GET_SIZE(it->it_seq))
@@ -4260,7 +4260,7 @@ listreviter_setstate(PyObject *self, PyObject *state)
42604260
Py_ssize_t index = PyLong_AsSsize_t(state);
42614261
if (index == -1 && PyErr_Occurred())
42624262
return NULL;
4263-
if (it->it_seq != NULL) {
4263+
if (it->it_seq != NULL && FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index) >= 0) {
42644264
if (index < -1)
42654265
index = -1;
42664266
else if (index > PyList_GET_SIZE(it->it_seq) - 1)

0 commit comments

Comments
 (0)