Skip to content

Commit a6a20ba

Browse files
committed
gh-000000: Fix use-after-free in itertools.count via re-entrant step
1 parent 59e67c2 commit a6a20ba

3 files changed

Lines changed: 14 additions & 4 deletions

File tree

‎Lib/test/test_itertools.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,15 @@ 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+
627636
def test_cycle(self):
628637
self.assertEqual(take(10, cycle('abc')), list('abcabcabca'))
629638
self.assertEqual(list(cycle('')), [])
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.count` when the step object's
2+
``__radd__`` re-enters the iterator. Patch by tonghuaroot.

‎Modules/itertoolsmodule.c‎

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

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;
3631+
PyObject *result = Py_NewRef(lz->long_cnt);
36343632

36353633
PyObject *stepped_up = PyNumber_Add(result, lz->long_step);
36363634
if (stepped_up == NULL) {
3635+
Py_DECREF(result);
36373636
return NULL;
36383637
}
3639-
lz->long_cnt = stepped_up;
3638+
Py_SETREF(lz->long_cnt, stepped_up);
36403639

36413640
return result;
36423641
}

0 commit comments

Comments
 (0)