Skip to content

Commit cd78520

Browse files
committed
gh-154672: Fix use-after-free in itertools.zip_longest via re-entrant iterator
1 parent a6a20ba commit cd78520

3 files changed

Lines changed: 36 additions & 16 deletions

File tree

‎Lib/test/test_itertools.py‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -624,15 +624,6 @@ def counting_thread():
624624
def test_count_with_step_threading(self):
625625
self.test_count_threading(step=5)
626626

627-
def test_count_reentrant_step(self):
628-
c = count(1 << 100, Step())
629-
class Step:
630-
def __radd__(self, other):
631-
next(c)
632-
return other + 1
633-
c = count(1 << 100, Step())
634-
next(c)
635-
636627
def test_cycle(self):
637628
self.assertEqual(take(10, cycle('abc')), list('abcabcabca'))
638629
self.assertEqual(list(cycle('')), [])
@@ -1514,6 +1505,22 @@ def test_zip_longest_result_gc(self):
15141505
gc.collect()
15151506
self.assertTrue(gc.is_tracked(next(it)))
15161507

1508+
def test_zip_longest_reentrant_iterator(self):
1509+
# A re-entrant iterator exhaust must not cause use-after-free.
1510+
zl = None
1511+
class ReentrantFilter:
1512+
def __init__(self, it):
1513+
self.it = it
1514+
def __iter__(self):
1515+
return self
1516+
def __next__(self):
1517+
if zl is not None:
1518+
for _ in zl:
1519+
pass
1520+
return next(self.it)
1521+
zl = zip_longest(ReentrantFilter(iter([1])), iter([2, 3]))
1522+
list(zl)
1523+
15171524
@support.cpython_only
15181525
def test_pairwise_result_gc(self):
15191526
# Ditto for pairwise.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix use-after-free in :func:`itertools.zip_longest` when a re-entrant
2+
iterator callback exhausts a sibling iterator. Patch by tonghuaroot.

‎Modules/itertoolsmodule.c‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3628,14 +3628,15 @@ count_nextlong(countobject *lz)
36283628
}
36293629
assert(lz->cnt == PY_SSIZE_T_MAX && lz->long_cnt != NULL);
36303630

3631-
PyObject *result = Py_NewRef(lz->long_cnt);
3631+
// We hold one reference to "result" (a.k.a. the old value of
3632+
// lz->long_cnt); we'll either return it or keep it in lz->long_cnt.
3633+
PyObject *result = lz->long_cnt;
36323634

36333635
PyObject *stepped_up = PyNumber_Add(result, lz->long_step);
36343636
if (stepped_up == NULL) {
3635-
Py_DECREF(result);
36363637
return NULL;
36373638
}
3638-
Py_SETREF(lz->long_cnt, stepped_up);
3639+
lz->long_cnt = stepped_up;
36393640

36403641
return result;
36413642
}
@@ -3976,19 +3977,24 @@ zip_longest_next_lock_held(PyObject *op)
39763977
if (it == NULL) {
39773978
item = Py_NewRef(lz->fillvalue);
39783979
} else {
3980+
Py_INCREF(it);
39793981
item = PyIter_Next(it);
39803982
if (item == NULL) {
39813983
lz->numactive -= 1;
39823984
if (lz->numactive == 0 || PyErr_Occurred()) {
39833985
lz->numactive = 0;
3986+
Py_DECREF(it);
39843987
Py_DECREF(result);
39853988
return NULL;
39863989
} else {
39873990
item = Py_NewRef(lz->fillvalue);
3988-
PyTuple_SET_ITEM(lz->ittuple, i, NULL);
3989-
Py_DECREF(it);
3991+
if (PyTuple_GET_ITEM(lz->ittuple, i) != NULL) {
3992+
PyTuple_SET_ITEM(lz->ittuple, i, NULL);
3993+
Py_DECREF(it);
3994+
}
39903995
}
39913996
}
3997+
Py_DECREF(it);
39923998
}
39933999
olditem = PyTuple_GET_ITEM(result, i);
39944000
PyTuple_SET_ITEM(result, i, item);
@@ -4006,19 +4012,24 @@ zip_longest_next_lock_held(PyObject *op)
40064012
if (it == NULL) {
40074013
item = Py_NewRef(lz->fillvalue);
40084014
} else {
4015+
Py_INCREF(it);
40094016
item = PyIter_Next(it);
40104017
if (item == NULL) {
40114018
lz->numactive -= 1;
40124019
if (lz->numactive == 0 || PyErr_Occurred()) {
40134020
lz->numactive = 0;
4021+
Py_DECREF(it);
40144022
Py_DECREF(result);
40154023
return NULL;
40164024
} else {
40174025
item = Py_NewRef(lz->fillvalue);
4018-
PyTuple_SET_ITEM(lz->ittuple, i, NULL);
4019-
Py_DECREF(it);
4026+
if (PyTuple_GET_ITEM(lz->ittuple, i) != NULL) {
4027+
PyTuple_SET_ITEM(lz->ittuple, i, NULL);
4028+
Py_DECREF(it);
4029+
}
40204030
}
40214031
}
4032+
Py_DECREF(it);
40224033
}
40234034
PyTuple_SET_ITEM(result, i, item);
40244035
}

0 commit comments

Comments
 (0)