From cf6672ba61da0d4e52483ade0b06dea11cf4be55 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 3 Jan 2026 03:57:17 +0000 Subject: [PATCH] Use collections.deque for chunk splits (#11892) (#11912) CVE: CVE-2025-69229 Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/dc3170b56904bdf814228fae70a5501a42a6c712] (cherry picked from commit 271532ea355c65480c8ecc14137dfbb72aec8f6f) --------- Co-authored-by: Finder (cherry picked from commit dc3170b56904bdf814228fae70a5501a42a6c712) Signed-off-by: Darsh Kelaiya --- aiohttp/streams.py | 8 ++++---- tests/test_http_parser.py | 14 +++++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/aiohttp/streams.py b/aiohttp/streams.py index b9b9c3fd9..eb652ff45 100644 --- a/aiohttp/streams.py +++ b/aiohttp/streams.py @@ -122,7 +122,7 @@ class StreamReader(AsyncStreamReaderMixin): self._loop = loop self._size = 0 self._cursor = 0 - self._http_chunk_splits: Optional[List[int]] = None + self._http_chunk_splits: Optional[Deque[int]] = None self._buffer: Deque[bytes] = collections.deque() self._buffer_offset = 0 self._eof = False @@ -263,7 +263,7 @@ class StreamReader(AsyncStreamReaderMixin): raise RuntimeError( "Called begin_http_chunk_receiving when" "some data was already fed" ) - self._http_chunk_splits = [] + self._http_chunk_splits = collections.deque() def end_http_chunk_receiving(self) -> None: if self._http_chunk_splits is None: @@ -419,7 +419,7 @@ class StreamReader(AsyncStreamReaderMixin): raise self._exception while self._http_chunk_splits: - pos = self._http_chunk_splits.pop(0) + pos = self._http_chunk_splits.popleft() if pos == self._cursor: return (b"", True) if pos > self._cursor: @@ -491,7 +491,7 @@ class StreamReader(AsyncStreamReaderMixin): chunk_splits = self._http_chunk_splits # Prevent memory leak: drop useless chunk splits while chunk_splits and chunk_splits[0] < self._cursor: - chunk_splits.pop(0) + chunk_splits.popleft() if self._size < self._low_water and self._protocol._reading_paused: self._protocol.resume_reading() diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index 0a868f286..62830c2bd 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -1188,7 +1188,8 @@ def test_http_request_chunked_payload(parser) -> None: parser.feed_data(b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n") assert b"dataline" == b"".join(d for d in payload._buffer) - assert [4, 8] == payload._http_chunk_splits + assert payload._http_chunk_splits is not None + assert [4, 8] == list(payload._http_chunk_splits) assert payload.is_eof() @@ -1203,7 +1204,8 @@ def test_http_request_chunked_payload_and_next_message(parser) -> None: ) assert b"dataline" == b"".join(d for d in payload._buffer) - assert [4, 8] == payload._http_chunk_splits + assert payload._http_chunk_splits is not None + assert [4, 8] == list(payload._http_chunk_splits) assert payload.is_eof() assert len(messages) == 1 @@ -1227,12 +1229,13 @@ def test_http_request_chunked_payload_chunks(parser) -> None: parser.feed_data(b"test: test\r\n") assert b"dataline" == b"".join(d for d in payload._buffer) - assert [4, 8] == payload._http_chunk_splits + assert payload._http_chunk_splits is not None + assert [4, 8] == list(payload._http_chunk_splits) assert not payload.is_eof() parser.feed_data(b"\r\n") assert b"dataline" == b"".join(d for d in payload._buffer) - assert [4, 8] == payload._http_chunk_splits + assert [4, 8] == list(payload._http_chunk_splits) assert payload.is_eof() @@ -1243,7 +1246,8 @@ def test_parse_chunked_payload_chunk_extension(parser) -> None: parser.feed_data(b"4;test\r\ndata\r\n4\r\nline\r\n0\r\ntest: test\r\n\r\n") assert b"dataline" == b"".join(d for d in payload._buffer) - assert [4, 8] == payload._http_chunk_splits + assert payload._http_chunk_splits is not None + assert [4, 8] == list(payload._http_chunk_splits) assert payload.is_eof() -- 2.35.6