Skip to content

Commit 99710d4

Browse files
committed
bpo-15108: Prevent accessing the result tuple from Python in PySequence_Tuple
1 parent 2068b26 commit 99710d4

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

Lib/test/test_iter.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import sys
44
import unittest
5+
import gc
56
from test.support import run_unittest, cpython_only
67
from test.support.os_helper import TESTFN, unlink
78
from test.support import check_free_after_iterating, ALWAYS_EQ, NEVER_EQ
@@ -1036,6 +1037,25 @@ def test_error_iter(self):
10361037
self.assertRaises(TypeError, iter, typ())
10371038
self.assertRaises(ZeroDivisionError, iter, BadIterableClass())
10381039

1040+
def test_access_result_tuple_while_iterating(self):
1041+
TAG = object()
1042+
1043+
def monitor():
1044+
lst = [x for x in gc.get_referrers(TAG) if isinstance(x, tuple)]
1045+
# This would be the result tuple if is accessible mid-iteration
1046+
t = lst[0]
1047+
print(t)
1048+
return t
1049+
1050+
def my_iter():
1051+
yield TAG
1052+
t = monitor()
1053+
breakpoint()
1054+
for x in range(10):
1055+
yield x
1056+
1057+
self.assertRaises(IndexError, tuple, my_iter())
1058+
10391059

10401060
def test_main():
10411061
run_unittest(TestCase)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed an issue that could cause a crash when accessing an incomplete tuple
2+
when collecting an iterable into a :class:`tuple` using :c:func:`PySequence_Tuple`.
3+
Patch by Pablo Galindo.

Objects/abstract.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,6 +1993,9 @@ PySequence_Tuple(PyObject *v)
19931993
if (result == NULL)
19941994
goto Fail;
19951995

1996+
// bpo-15108: Code can access the result tuple while being
1997+
// incomplete when calling PyIter_Next().
1998+
PyObject_GC_UnTrack(result);
19961999
/* Fill the tuple. */
19972000
for (j = 0; ; ++j) {
19982001
PyObject *item = PyIter_Next(it);
@@ -2022,10 +2025,17 @@ PySequence_Tuple(PyObject *v)
20222025
Py_DECREF(item);
20232026
goto Fail;
20242027
}
2028+
// Resizing could track the tuple again
2029+
PyObject_GC_UnTrack(result);
20252030
}
20262031
PyTuple_SET_ITEM(result, j, item);
20272032
}
20282033

2034+
// No more calls can go back into Python, so is safe
2035+
// to re-track the tuple.
2036+
2037+
PyObject_GC_Track(result);
2038+
20292039
/* Cut tuple back if guess was too large. */
20302040
if (j < n &&
20312041
_PyTuple_Resize(&result, j) != 0)

0 commit comments

Comments
 (0)