From ec77aac9605cc95eff20c7fb179fc105f0f06955 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 19 Oct 2026 13:41:28 -0700 Subject: [PATCH] gh-159739: Handle overlapping buffers in BytesIO.readinto Backport to 3.15 and 3.14 (move the test to Lib/test/test_memoryio.py on 3.14). --- Lib/test/test_io/test_memoryio.py | 6 ++++++ .../Library/2026-10-19-20-38-17.gh-issue-159739.a81270.rst | 1 + Modules/_io/bytesio.c | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-19-20-38-17.gh-issue-159739.a81270.rst diff --git a/Lib/test/test_io/test_memoryio.py b/Lib/test/test_io/test_memoryio.py index b6f3aa93e7aa43..90bdc791787439 100644 --- a/Lib/test/test_io/test_memoryio.py +++ b/Lib/test/test_io/test_memoryio.py @@ -962,6 +962,12 @@ class CBytesIOTest(PyBytesIOTest): ioclass = io.BytesIO UnsupportedOperation = io.UnsupportedOperation + def test_readinto_overlapping_buffer(self): + with self.ioclass(b'abcd') as stream: + with stream.getbuffer()[1:] as target: + self.assertEqual(stream.readinto(target), 3) + self.assertEqual(stream.getvalue(), b'aabc') + def test_getstate(self): memio = self.ioclass() state = memio.__getstate__() diff --git a/Misc/NEWS.d/next/Library/2026-10-19-20-38-17.gh-issue-159739.a81270.rst b/Misc/NEWS.d/next/Library/2026-10-19-20-38-17.gh-issue-159739.a81270.rst new file mode 100644 index 00000000000000..413fc0d7f18f68 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-19-20-38-17.gh-issue-159739.a81270.rst @@ -0,0 +1 @@ +Fix :class:`io.BytesIO` reads into overlapping views of the same buffer. diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 1f97450f53beec..e725e863a0740a 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -714,7 +714,7 @@ _io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer) assert(self->pos + len <= PY_SSIZE_T_MAX); assert(len >= 0); - memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len); + memmove(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len); self->pos += len; return PyLong_FromSsize_t(len);