Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Lib/test/test_io/test_textio.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a crash when calling :meth:`io.TextIOWrapper.seek` with a malformed cookie.
11 changes: 9 additions & 2 deletions Modules/_io/textio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Comment thread
ByteFlowing1337 marked this conversation as resolved.
PyErr_SetString(PyExc_OSError, "can't restore logical file position");
goto fail;
}
Expand Down Expand Up @@ -2932,7 +2933,13 @@ _io_TextIOWrapper_tell_impl(textio *self)

/* Fast search for an acceptable start point, close to our
current pos */
skip_bytes = (Py_ssize_t) (self->b2cratio * chars_to_skip);
double skip_bytes_d = self->b2cratio * (double) chars_to_skip;
if (!(skip_bytes_d >= 0) || skip_bytes_d >= (double) PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError,
"chars_to_skip are too large");
goto fail;
}
skip_bytes = (Py_ssize_t) skip_bytes_d;
skip_back = 1;
assert(skip_bytes <= PyBytes_GET_SIZE(next_input));
input = PyBytes_AS_STRING(next_input);
Expand Down
Loading