diff --git a/Lib/asyncio/sslproto.py b/Lib/asyncio/sslproto.py index 84e82fc69cc5fd..e27f7b62cab994 100644 --- a/Lib/asyncio/sslproto.py +++ b/Lib/asyncio/sslproto.py @@ -773,8 +773,10 @@ def _do_read__buffered(self): if count > 0: offset = count + # gh-156275: a bytearray slice is a copy, slice a view instead + view = memoryview(buf) while offset < wants: - count = self._sslobj.read(wants - offset, buf[offset:]) + count = self._sslobj.read(wants - offset, view[offset:]) if count > 0: offset += count else: diff --git a/Lib/test/test_asyncio/test_ssl.py b/Lib/test/test_asyncio/test_ssl.py index 902dc7e04e5809..b4935645652166 100644 --- a/Lib/test/test_asyncio/test_ssl.py +++ b/Lib/test/test_asyncio/test_ssl.py @@ -894,6 +894,46 @@ async def client(addr): asyncio.wait_for(client(srv.addr), timeout=self.TIMEOUT)) + def test_buffered_proto_bytearray(self): + # gh-156275: decrypt into the caller's buffer, not a copy of a slice + CHUNKS = [b'A' * 30, b'B' * 30, b'C' * 30] + + class ClientProto(asyncio.BufferedProtocol): + def __init__(self, done): + self.done = done + self.buf = bytearray(100) + self.data = b'' + + def get_buffer(self, sizehint): + return self.buf + + def buffer_updated(self, nbytes): + self.data += self.buf[:nbytes] + + def connection_lost(self, exc): + self.done.set_result(self.data) + + async def serve(reader, writer): + for chunk in CHUNKS: + writer.write(chunk) + writer.close() + + async def run(): + server = await asyncio.start_server( + serve, '127.0.0.1', 0, ssl=test_utils.simple_server_sslcontext()) + try: + done = self.loop.create_future() + await self.loop.create_connection( + lambda: ClientProto(done), + *server.sockets[0].getsockname()[:2], + ssl=test_utils.simple_client_sslcontext()) + self.assertEqual(await done, b''.join(CHUNKS)) + finally: + self.loop.call_soon(server.close) + await server.wait_closed() + + self.loop.run_until_complete(asyncio.wait_for(run(), timeout=self.TIMEOUT)) + def test_start_tls_slow_client_cancel(self): HELLO_MSG = b'1' * self.PAYLOAD_SIZE diff --git a/Misc/NEWS.d/next/Library/2026-08-23-13-10-54.gh-issue-156276.rQeeS5.rst b/Misc/NEWS.d/next/Library/2026-08-23-13-10-54.gh-issue-156276.rQeeS5.rst new file mode 100644 index 00000000000000..6bd492e6eb7cb0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-23-13-10-54.gh-issue-156276.rQeeS5.rst @@ -0,0 +1,2 @@ +Fix :mod:`asyncio` losing received TLS data when +:meth:`~asyncio.BufferedProtocol.get_buffer` returns a :class:`bytearray`.