diff --git a/Misc/NEWS.d/next/Library/2026-07-08-15-26-00.gh-issue-153296.Twix12.rst b/Misc/NEWS.d/next/Library/2026-07-08-15-26-00.gh-issue-153296.Twix12.rst new file mode 100644 index 00000000000000..22604dd31dd605 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-08-15-26-00.gh-issue-153296.Twix12.rst @@ -0,0 +1 @@ +Fix a data race and use-after-free when iterating over an :class:`io.StringIO` object while it is being concurrently mutated. The ``__next__`` method now properly acquires the object's lock. diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index b8601383ad0a26..27605b4c44239e 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -407,8 +407,10 @@ _io_StringIO_readline_impl(stringio *self, Py_ssize_t size) } static PyObject * -stringio_iternext(PyObject *op) +stringio_iternext_lock_held(PyObject *op) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op); + PyObject *line; stringio *self = stringio_CAST(op); @@ -444,6 +446,16 @@ stringio_iternext(PyObject *op) return line; } +static PyObject * +stringio_iternext(PyObject *op) +{ + PyObject *ret; + Py_BEGIN_CRITICAL_SECTION(op); + ret = stringio_iternext_lock_held(op); + Py_END_CRITICAL_SECTION(); + return ret; +} + /*[clinic input] @critical_section _io.StringIO.truncate