diff --git a/Lib/test/test_io/test_textio.py b/Lib/test/test_io/test_textio.py index 82096ab0987395..f6d83200069c27 100644 --- a/Lib/test/test_io/test_textio.py +++ b/Lib/test/test_io/test_textio.py @@ -1425,6 +1425,22 @@ def test_read_non_blocking(self): os.close(r) os.close(w) + @support.subTests('cookie_value', [ + # Set 'cookie.chars_to_skip' to INT_MIN + (100 << (12 * 8)) | (0x80 << (19 * 8)), + # Set 'cookie.chars_to_skip' to INT_MAX + (100 << (12 * 8)) | (0x7FFFFFFF << (16 * 8)) + ]) + def test_seek_reject_invalid_chars_to_skip(self, cookie_value): + # See https://github.com/python/cpython/issues/153662 + data = b"1" + raw = io.BytesIO(data) + buf = io.BufferedReader(raw) + tio = io.TextIOWrapper(buf, encoding='utf-8') + tio.read() + with self.assertRaisesRegex(OSError, "can't restore logical file position"): + tio.seek(cookie_value) + class MemviewBytesIO(io.BytesIO): '''A BytesIO object whose read method returns memoryviews diff --git a/Misc/NEWS.d/next/Library/2026-07-13-23-47-14.gh-issue-153662.ei9ya3.rst b/Misc/NEWS.d/next/Library/2026-07-13-23-47-14.gh-issue-153662.ei9ya3.rst new file mode 100644 index 00000000000000..b5d56795272003 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-13-23-47-14.gh-issue-153662.ei9ya3.rst @@ -0,0 +1 @@ +Fix a crash when calling :meth:`io.TextIOWrapper.seek` with a malformed cookie. diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 5b2a20a30c28cb..a6c433a247bb1a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -2781,7 +2781,8 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) textiowrapper_set_decoded_chars(self, decoded); /* Skip chars_to_skip of the decoded characters. */ - if (PyUnicode_GetLength(self->decoded_chars) < cookie.chars_to_skip) { + /* gh-153662: Reject negative chars_to_skip */ + if (cookie.chars_to_skip < 0 || PyUnicode_GetLength(self->decoded_chars) < cookie.chars_to_skip) { PyErr_SetString(PyExc_OSError, "can't restore logical file position"); goto fail; }