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
14 changes: 14 additions & 0 deletions Lib/test/test_io/test_memoryio.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ def testSeek(self):
self.assertEqual(buf[3:], bytesIo.read())
self.assertRaises(TypeError, bytesIo.seek, 0.0)

self.assertEqual(sys.maxsize, bytesIo.seek(sys.maxsize))
self.assertEqual(self.EOF, bytesIo.read(4))

self.assertEqual(sys.maxsize - 2, bytesIo.seek(sys.maxsize - 2))
self.assertEqual(self.EOF, bytesIo.read(4))

def testTell(self):
buf = self.buftype("1234567890")
bytesIo = self.ioclass(buf)
Expand Down Expand Up @@ -552,6 +558,14 @@ def test_relative_seek(self):
memio.seek(1, 1)
self.assertEqual(memio.read(), buf[1:])

def test_issue141311(self):
memio = self.ioclass()
# Seek allows PY_SSIZE_T_MAX, read should handle that.
# Past end of buffer read should always return 0 (EOF).
self.assertEqual(sys.maxsize, memio.seek(sys.maxsize))
buf = bytearray(2)
self.assertEqual(0, memio.readinto(buf))

def test_unicode(self):
memio = self.ioclass()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix assertion failure in :func:`!io.BytesIO.readinto` and undefined behavior
arising when read position is above capcity in :class:`io.BytesIO`.
16 changes: 13 additions & 3 deletions Modules/_io/bytesio.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,13 @@ read_bytes_lock_held(bytesio *self, Py_ssize_t size)
return Py_NewRef(self->buf);
}

/* gh-141311: Avoid undefined behavior when self->pos (limit PY_SSIZE_T_MAX)
is beyond the size of self->buf. Assert above validates size is always in
bounds. When self->pos is out of bounds calling code sets size to 0. */
if (size == 0) {
return PyBytes_FromStringAndSize(NULL, 0);
}

output = PyBytes_AS_STRING(self->buf) + self->pos;
self->pos += size;
return PyBytes_FromStringAndSize(output, size);
Expand Down Expand Up @@ -609,11 +616,14 @@ _io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer)
n = self->string_size - self->pos;
if (len > n) {
len = n;
if (len < 0)
len = 0;
if (len < 0) {
/* gh-141311: Avoid undefined behavior when self->pos (limit
PY_SSIZE_T_MAX) points beyond the size of self->buf. */
return PyLong_FromSsize_t(0);
}
}

assert(self->pos + len < PY_SSIZE_T_MAX);
assert(self->pos + len <= PY_SSIZE_T_MAX);
assert(len >= 0);
memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len);
self->pos += len;
Expand Down
Loading