diff --git a/Misc/NEWS.d/next/Library/2026-07-30-12-05-00.gh-issue-148286.Bv3Nq8.rst b/Misc/NEWS.d/next/Library/2026-07-30-12-05-00.gh-issue-148286.Bv3Nq8.rst new file mode 100644 index 000000000000000..749c3de18f5ae9e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-30-12-05-00.gh-issue-148286.Bv3Nq8.rst @@ -0,0 +1,7 @@ +Fix undefined behaviour in :meth:`!io.StringIO.read` after an overseek. +Reading from a position past the end of the buffer formed the pointer +``self->buf + self->pos`` before returning the empty string; for a large +enough position the arithmetic wrapped and produced a pointer below the +buffer. :class:`io.StringIO` now returns the empty string early, as +``_stringio_readline()`` already did, which removes the +``Modules/_io/stringio.c`` entry from ``Tools/ubsan/suppressions.txt``. diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 27605b4c44239e9..ff74acee0bbc8f9 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -349,6 +349,17 @@ _io_StringIO_read_impl(stringio *self, Py_ssize_t size) } ENSURE_REALIZED(self); + + /* In case of overseek, return the empty string, as _stringio_readline() + does. `size` is already clamped to 0 here, so nothing would be read + through the pointer -- but forming `self->buf + self->pos` when + `self->pos` is past the end of the buffer is undefined behaviour on + its own, and for a large enough `self->pos` the multiplication by + sizeof(Py_UCS4) wraps and yields a pointer below `self->buf`. */ + if (self->pos >= self->string_size) { + return Py_GetConstant(Py_CONSTANT_EMPTY_STR); + } + output = self->buf + self->pos; self->pos += size; return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output, size); diff --git a/Tools/ubsan/suppressions.txt b/Tools/ubsan/suppressions.txt index 2b9ad49ebf1eefb..b22663e47759e9b 100644 --- a/Tools/ubsan/suppressions.txt +++ b/Tools/ubsan/suppressions.txt @@ -14,6 +14,3 @@ shift-base:Modules/_ctypes/cfield.c # Modules/_ctypes/cfield.c:640:1: runtime error: signed integer overflow: -2147483648 - 1 cannot be represented in type 'int' signed-integer-overflow:Modules/_ctypes/cfield.c - -# Modules/_io/stringio.c:350:24: runtime error: addition of unsigned offset to 0x7fd01ec25850 overflowed to 0x7fd01ec2584c -pointer-overflow:Modules/_io/stringio.c