From patchwork Mon Sep 28 17:43:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)" X-Patchwork-Id: 99493 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3B234CA5FA3 for ; Mon, 28 Sep 2026 17:43:41 +0000 (UTC) Received: from rcdn-iport-3.cisco.com (rcdn-iport-3.cisco.com [173.37.86.74]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.64574.1790617412700567676 for ; Mon, 28 Sep 2026 10:43:33 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: message contains an insecure body length tag" header.i=@cisco.com header.s=iport01 header.b=lmGkHVJW; spf=pass (domain: cisco.com, ip: 173.37.86.74, mailfrom: dkelaiya@cisco.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cisco.com; i=@cisco.com; l=13857; q=dns/txt; s=iport01; t=1790617412; x=1791827012; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=HYhHWTAiX5nDEPjWj75rUN2L1JalIYzw5gkle5o9wXk=; b=lmGkHVJWwAVKTZH4Yw+L7qEEbwFNVPp+DhmIH41tIw8Ft3JhGjJJDNsL nt6232QLcB7xp9qxIU54heIouuh+eDG076TBsoxS+9qhZCCFatWM5w/QG usUjbK9DaECSnofJwbp+EKtHGZgOaS8y3MbiiQOFY+GXHCHUFRdfplUsa XqNlPNUEq13DCdrd8XtobbzqIWQ4mhoFfCUtvI7z7OJ5hFbM2O634QIXV reVA4rOJGBSeVq8JoSJkg7RheC05/XiW4+HvZMPk9OKBduoXixwkTNC1b /sYejYEVIzmBog0BxwPRz+yl2Tt0MQDzR5C6OzmLcmTijC/N1rutPvEGe A==; X-CSE-ConnectionGUID: l/B61G9uRc+EaIppuiauRA== X-CSE-MsgGUID: KOKq/MW8RN2zstKQY5dmCA== X-IPAS-Result: A0BIAgBOprpq/5X/Ja1aHgEBCxIMggULgld1YENJlkoDnhqBfg8BAQEPRA0EAQGEP0YCjgkCJjQJDgECBAMCAwEBAQEBAQEBAQEBAQoBAQUBAQECAQcFgQ4Thk8NkBIBAgEDJwsBGAEtEBwDAQIvKyMIGYMCAYJ0AxHCV4F5M4EBgykBPwJDUNsyAQsUAYE4hUCII10YAYR8JxsbgXKBFYNpgQWBXAEBiCUEgiKBDIFagQOSQEiBHgNZLAFVEw0KCwcFgWYDNRIqFW4yHYEjPheBBxsGBYEdgSCCGSMZNnqBCV6BKylgARAXgQeCBwKCVIIBAgFJQw4HRVMJJUEKEkcmIggSCQETGjALgSE4QQkoERgNSBEsNxUZBD5uB5AuHoJkAYEOAQogAYEYCBQZpDSCIaEPCiiDdowilToaM4QEgVeSQJJUC5h9glmLMZVzXYRpgWg8gUcLB3AVO4JnCUoZD44rDguDYIF/g2XGVScyAgkyAQEHAgcOAwuBaJF+AQE IronPort-Data: A9a23:stAeGqqtbCmW1E/ERaJLtZRWI5leBmJIZBIvgKrLsJaIsI4StFCzt garIBmAMvqOMWP3KI8gPYmx8B4A6MWGztM2QVA+rXtgQilB9+PIVI+TRqvS04x+DSFioGZPt Zh2hgzodZhsJpPkjk7zdOWn9T8jhfngqoPUUIbsIjp2SRJvVBAvgBdin/9RqoNziLBVOSvV0 T/Ji5OZYgDNNwJcaDpOtfrS8kM35ZwehRtB1rAATaET1LPhvyF94KI3fcmZM3b+S49IKe+2L 86r5K255G7Q4yA2AdqjlLvhGmVSKlIFFVHT4pb+c/HKbilq/kTe4I5iXBYvQRs/ZwGyojxE4 I4lWapc5useFvakdOw1C3G0GszlVEFM0OevzXOX6aR/w6BaGpfh660GMa04AWEX0uIwKz8X2 q0FEjtOQxmin/qrwbumcMA506zPLOGzVG8ekmtrwTecCbMtRorOBvyQo9RZxzw3wMtJGJ4yZ eJANmEpN0uGOUASfA5LWfrSn8/w7pX7WzFbpVacpLs+y2PS1wd2lrPqNbI5f/TXH5QNxxrJ+ j6uE2LRITY5LZu69mq/1Ci+nfPspBKmVIc5PejtnhJtqBjJroAJMzURTVa9rPyzh0KyVt4aJ 0EK9y4Gqakp6FftScHwWRC9qnOIshMQHd1KHIUHBBql0KHY5UOdQ2MDVDMEMYZgv84tTjts3 ViM9z/0OQFSXHSuYSr13t+pQfmaYED58Udqifc4cDY4 IronPort-HdrOrdr: A9a23:XZ36cqHLAxxtiL/ZpLqEyseALOsnbusQ8zAXPo5KJiC9Ffbo8P xG88576faZslsssTQb6LK90cq7MBfhHOBOgbX5VI3KNGKNhILrFvAG0WKI+VPd8kPFmtK1rZ 0QEJRWOZnXEUVwi9r87U2TFtYtx8TCzYWT7N2uqUuEiWpRGtldB8ATMHfjLnFL X-Talos-CUID: 9a23:g/u34GEFt38Ag1SOqmJjs2RKQ+90b0fWj1rZAlegFGdgVYe8HAo= X-Talos-MUID: 9a23:NAgaiA1fNINs0ZgBXIwSIaI86jUjwIONEQcorsU8nZOWLjBTAgbAgDGca9py X-IronPort-Anti-Spam-Filtered: true X-IronPort-AV: E=Sophos;i="6.27,128,1787011200"; d="scan'208";a="529014944" Received: from rcdn-l-core-12.cisco.com ([173.37.255.149]) by rcdn-iport-3.cisco.com with ESMTP/TLS/TLS_AES_256_GCM_SHA384; 28 Sep 2026 17:43:31 +0000 Received: from sjc-ads-5675.cisco.com (sjc-ads-5675.cisco.com [10.28.88.189]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by rcdn-l-core-12.cisco.com (Postfix) with ESMTPS id 805E618000248; Mon, 28 Sep 2026 17:43:31 +0000 (GMT) Received: by sjc-ads-5675.cisco.com (Postfix, from userid 1887444) id 28EEBCBF202; Mon, 28 Sep 2026 10:43:31 -0700 (PDT) From: "Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)" To: openembedded-devel@lists.openembedded.org Cc: Darsh Kelaiya Subject: [oe][meta-python][scarthgap][PATCH 02/13] python3-aiohttp: fix CVE-2026-34516 Date: Mon, 28 Sep 2026 10:43:12 -0700 Message-Id: <20260928174323.1810308-3-dkelaiya@cisco.com> X-Mailer: git-send-email 2.35.6 In-Reply-To: <20260928174323.1810308-1-dkelaiya@cisco.com> References: <20260928174323.1810308-1-dkelaiya@cisco.com> MIME-Version: 1.0 X-Outbound-Client-TLS: ANONYMOUS;sjc-ads-5675.cisco.com [10.28.88.189];TLSv1.3;TLS_AES_256_GCM_SHA384;256 X-Outbound-SMTP-Client: 10.28.88.189, sjc-ads-5675.cisco.com X-Outbound-Node: rcdn-l-core-12.cisco.com List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 28 Sep 2026 17:43:41 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-devel/message/130446 From: Darsh Kelaiya This patch applies the upstream stable-branch fix in [1], which backports the original upstream commit in [2]. The advisory identifying the fix is referenced in [3]. [1] https://github.com/aio-libs/aiohttp/commit/8a74257b3804c9aac0bf644af93070f68f6c5a6f [2] https://github.com/aio-libs/aiohttp/commit/5fe9dfb64400a574f5ba3f2d3b49b7db47567c29 [3] https://nvd.nist.gov/vuln/detail/CVE-2026-34516 Signed-off-by: Darsh Kelaiya --- .../python3-aiohttp/CVE-2026-34516.patch | 356 ++++++++++++++++++ .../python/python3-aiohttp_3.9.5.bb | 1 + 2 files changed, 357 insertions(+) create mode 100644 meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-34516.patch diff --git a/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-34516.patch b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-34516.patch new file mode 100644 index 0000000000..47956fe975 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-34516.patch @@ -0,0 +1,356 @@ +From b119720e4e9c19b23f8589dae05b9a39b26995e1 Mon Sep 17 00:00:00 2001 +From: Sam Bull +Date: Tue, 10 Mar 2026 20:36:38 +0000 +Subject: [PATCH] Restrict multipart header sizes (#12208) (#12228) + +CVE: CVE-2026-34516 +Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/8a74257b3804c9aac0bf644af93070f68f6c5a6f] + +Backport Changes: +- Retained aiohttp 3.9.5's Optional/Union annotations in + MultipartReader because this version supports Python 3.8 and cannot + use the upstream PEP 604 union syntax. +- Omitted the test_read_boundary_across_chunks() readline() signature + update because that test is not present in aiohttp 3.9.5; retained + the applicable Stream test-helper update. +- Applied the multipart header trimming change using rstrip(b"\r\n") + for compatibility with the aiohttp 3.9.5 codebase. + +(cherry picked from commit 5fe9dfb64400a574f5ba3f2d3b49b7db47567c29) +(cherry picked from commit 8a74257b3804c9aac0bf644af93070f68f6c5a6f) +Signed-off-by: Darsh Kelaiya +--- + aiohttp/multipart.py | 27 +++++++++++++++++++--- + aiohttp/streams.py | 16 +++++++----- + aiohttp/test_utils.py | 3 +++ + aiohttp/web_protocol.py | 7 ++++++ + aiohttp/web_request.py | 7 +++++- + tests/test_multipart.py | 3 ++- + tests/test_streams.py | 9 ++++--- + tests/test_web_request.py | 53 ++++++++++++++++++++++++++++++++++++++- + 8 files changed, 106 insertions(+), 17 deletions(-) + +diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py +index 9e5ff9b41..d7b993218 100644 +--- a/aiohttp/multipart.py ++++ b/aiohttp/multipart.py +@@ -37,6 +37,7 @@ from .hdrs import ( + ) + from .helpers import CHAR, TOKEN, parse_mimetype, reify + from .http import HeadersParser ++from .http_exceptions import BadHttpMessage + from .payload import ( + JsonPayload, + LookupError, +@@ -547,7 +548,14 @@ class MultipartReader: + #: Body part reader class for non multipart/* content types. + part_reader_cls = BodyPartReader + +- def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None: ++ def __init__( ++ self, ++ headers: Mapping[str, str], ++ content: StreamReader, ++ *, ++ max_field_size: int = 8190, ++ max_headers: int = 128, ++ ) -> None: + self._mimetype = parse_mimetype(headers[CONTENT_TYPE]) + assert self._mimetype.type == "multipart", "multipart/* content type expected" + if "boundary" not in self._mimetype.parameters: +@@ -560,6 +568,8 @@ class MultipartReader: + self._content = content + self._default_charset: Optional[str] = None + self._last_part: Optional[Union["MultipartReader", BodyPartReader]] = None ++ self._max_field_size = max_field_size ++ self._max_headers = max_headers + self._at_eof = False + self._at_bof = True + self._unread: List[bytes] = [] +@@ -661,7 +671,12 @@ class MultipartReader: + if mimetype.type == "multipart": + if self.multipart_reader_cls is None: + return type(self)(headers, self._content) +- return self.multipart_reader_cls(headers, self._content) ++ return self.multipart_reader_cls( ++ headers, ++ self._content, ++ max_field_size=self._max_field_size, ++ max_headers=self._max_headers, ++ ) + else: + return self.part_reader_cls( + self._boundary, +@@ -723,12 +738,14 @@ class MultipartReader: + async def _read_headers(self) -> "CIMultiDictProxy[str]": + lines = [] + while True: +- chunk = await self._content.readline() ++ chunk = await self._content.readline(max_line_length=self._max_field_size) +- chunk = chunk.strip() ++ chunk = chunk.rstrip(b"\r\n") + lines.append(chunk) + if not chunk: + break +- parser = HeadersParser() ++ if len(lines) > self._max_headers: ++ raise BadHttpMessage("Too many headers received") ++ parser = HeadersParser(max_field_size=self._max_field_size) + headers, raw_headers = parser.parse_headers(lines) + return headers + +diff --git a/aiohttp/streams.py b/aiohttp/streams.py +index 121528842..dffaf374b 100644 +--- a/aiohttp/streams.py ++++ b/aiohttp/streams.py +@@ -21,6 +21,7 @@ from .helpers import ( + set_exception, + set_result, + ) ++from .http_exceptions import LineTooLong + from .log import internal_logger + + __all__ = ( +@@ -327,10 +328,12 @@ class StreamReader(AsyncStreamReaderMixin): + finally: + self._waiter = None + +- async def readline(self) -> bytes: +- return await self.readuntil() ++ async def readline(self, *, max_line_length: Optional[int] = None) -> bytes: ++ return await self.readuntil(max_size=max_line_length) + +- async def readuntil(self, separator: bytes = b"\n") -> bytes: ++ async def readuntil( ++ self, separator: bytes = b"\n", *, max_size: Optional[int] = None ++ ) -> bytes: + seplen = len(separator) + if seplen == 0: + raise ValueError("Separator should be at least one-byte string") +@@ -341,6 +344,7 @@ class StreamReader(AsyncStreamReaderMixin): + chunk = b"" + chunk_size = 0 + not_enough = True ++ max_size = max_size or self._high_water + + while not_enough: + while self._buffer and not_enough: +@@ -355,8 +359,8 @@ class StreamReader(AsyncStreamReaderMixin): + if ichar: + not_enough = False + +- if chunk_size > self._high_water: +- raise ValueError("Chunk too big") ++ if chunk_size > max_size: ++ raise LineTooLong(chunk[:100] + b"...", max_size) + + if self._eof: + break +@@ -572,7 +576,7 @@ class EmptyStreamReader(StreamReader): # lgtm [py/missing-call-to-init] + def feed_data(self, data: bytes, n: int = 0) -> None: + pass + +- async def readline(self) -> bytes: ++ async def readline(self, *, max_line_length: Optional[int] = None) -> bytes: + return b"" + + async def read(self, n: int = -1) -> bytes: +diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py +index a36e85996..a12bb0505 100644 +--- a/aiohttp/test_utils.py ++++ b/aiohttp/test_utils.py +@@ -638,6 +638,9 @@ def make_mocked_request( + + if protocol is sentinel: + protocol = mock.Mock() ++ protocol.max_field_size = 8190 ++ protocol.max_line_length = 8190 ++ protocol.max_headers = 128 + protocol.transport = transport + + if writer is sentinel: +diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py +index a06503e0c..a73bb4364 100644 +--- a/aiohttp/web_protocol.py ++++ b/aiohttp/web_protocol.py +@@ -136,6 +136,9 @@ class RequestHandler(BaseProtocol): + KEEPALIVE_RESCHEDULE_DELAY = 1 + + __slots__ = ( ++ "max_field_size", ++ "max_headers", ++ "max_line_size", + "_request_count", + "_keepalive", + "_manager", +@@ -193,6 +196,10 @@ class RequestHandler(BaseProtocol): + self._request_handler: Optional[_RequestHandler] = manager.request_handler + self._request_factory: Optional[_RequestFactory] = manager.request_factory + ++ self.max_line_size = max_line_size ++ self.max_headers = max_headers ++ self.max_field_size = max_field_size ++ + self._tcp_keepalive = tcp_keepalive + # placeholder to be replaced on keepalive timeout setup + self._keepalive_time = 0.0 +diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py +index cd77b7bde..2ec09565c 100644 +--- a/aiohttp/web_request.py ++++ b/aiohttp/web_request.py +@@ -687,7 +687,12 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin): + + async def multipart(self) -> MultipartReader: + """Return async iterator to process BODY as multipart.""" +- return MultipartReader(self._headers, self._payload) ++ return MultipartReader( ++ self._headers, ++ self._payload, ++ max_field_size=self._protocol.max_field_size, ++ max_headers=self._protocol.max_headers, ++ ) + + async def post(self) -> "MultiDictProxy[Union[str, bytes, FileField]]": + """Return POST parameters.""" +diff --git a/tests/test_multipart.py b/tests/test_multipart.py +index e4a2be1f3..3bc9efd71 100644 +--- a/tests/test_multipart.py ++++ b/tests/test_multipart.py +@@ -3,6 +3,7 @@ import io + import json + import pathlib + import zlib ++from typing import Optional + from unittest import mock + + import pytest +@@ -63,7 +64,7 @@ class Stream: + def at_eof(self): + return self.content.tell() == len(self.content.getbuffer()) + +- async def readline(self): ++ async def readline(self, *, max_line_length: Optional[int] = None) -> bytes: + return self.content.readline() + + def unread_data(self, data): +diff --git a/tests/test_streams.py b/tests/test_streams.py +index ed65b567a..2076bc9ba 100644 +--- a/tests/test_streams.py ++++ b/tests/test_streams.py +@@ -12,6 +12,7 @@ import pytest + from re_assert import Matches + + from aiohttp import streams ++from aiohttp.http_exceptions import LineTooLong + + DATA = b"line1\nline2\nline3\n" + +@@ -325,7 +326,7 @@ class TestStreamReader: + stream.feed_data(b"li") + stream.feed_data(b"ne1\nline2\n") + +- with pytest.raises(ValueError): ++ with pytest.raises(LineTooLong): + await stream.readline() + # The buffer should contain the remaining data after exception + stream.feed_eof() +@@ -346,7 +347,7 @@ class TestStreamReader: + + loop.call_soon(cb) + +- with pytest.raises(ValueError): ++ with pytest.raises(LineTooLong): + await stream.readline() + data = await stream.read() + assert b"chunk3\n" == data +@@ -436,7 +437,7 @@ class TestStreamReader: + stream.feed_data(b"li") + stream.feed_data(b"ne1" + separator + b"line2" + separator) + +- with pytest.raises(ValueError): ++ with pytest.raises(LineTooLong): + await stream.readuntil(separator) + # The buffer should contain the remaining data after exception + stream.feed_eof() +@@ -458,7 +459,7 @@ class TestStreamReader: + + loop.call_soon(cb) + +- with pytest.raises(ValueError, match="Chunk too big"): ++ with pytest.raises(LineTooLong): + await stream.readuntil(separator) + data = await stream.read() + assert b"chunk3#" == data +diff --git a/tests/test_web_request.py b/tests/test_web_request.py +index 962092999..c1d61f895 100644 +--- a/tests/test_web_request.py ++++ b/tests/test_web_request.py +@@ -11,6 +11,7 @@ from yarl import URL + + from aiohttp import HttpVersion + from aiohttp.base_protocol import BaseProtocol ++from aiohttp.http_exceptions import BadHttpMessage, LineTooLong + from aiohttp.http_parser import RawRequestMessage + from aiohttp.streams import StreamReader + from aiohttp.test_utils import make_mocked_request +@@ -676,7 +677,57 @@ async def test_multipart_formdata_file(protocol: BaseProtocol) -> None: + result["a_file"].file.close() + + +-async def test_make_too_big_request_limit_None(protocol) -> None: ++async def test_multipart_formdata_headers_too_many(protocol: BaseProtocol) -> None: ++ many = b"".join(f"X-{i}: a\r\n".encode() for i in range(130)) ++ body = ( ++ b"--b\r\n" ++ b'Content-Disposition: form-data; name="a"\r\n' + many + b"\r\n1\r\n" ++ b"--b--\r\n" ++ ) ++ content_type = "multipart/form-data; boundary=b" ++ payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) ++ payload.feed_data(body) ++ payload.feed_eof() ++ req = make_mocked_request( ++ "POST", ++ "/", ++ headers={"CONTENT-TYPE": content_type}, ++ payload=payload, ++ ) ++ ++ with pytest.raises(BadHttpMessage, match="Too many headers received"): ++ await req.post() ++ ++ ++async def test_multipart_formdata_header_too_long(protocol: BaseProtocol) -> None: ++ k = b"t" * 4100 ++ body = ( ++ b"--b\r\n" ++ b'Content-Disposition: form-data; name="a"\r\n' ++ + k ++ + b":" ++ + k ++ + b"\r\n" ++ + b"\r\n1\r\n" ++ b"--b--\r\n" ++ ) ++ content_type = "multipart/form-data; boundary=b" ++ payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) ++ payload.feed_data(body) ++ payload.feed_eof() ++ req = make_mocked_request( ++ "POST", ++ "/", ++ headers={"CONTENT-TYPE": content_type}, ++ payload=payload, ++ ) ++ ++ match = "400, message:\n Got more than 8190 bytes when reading" ++ with pytest.raises(LineTooLong, match=match): ++ await req.post() ++ ++ ++async def test_make_too_big_request_limit_None(protocol: BaseProtocol) -> None: + payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + large_file = 1024**2 * b"x" + too_large_file = large_file + b"x" +-- +2.35.6 diff --git a/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb b/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb index 400a4a838b..d7c7a5014a 100644 --- a/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb +++ b/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb @@ -22,6 +22,7 @@ SRC_URI += "file://CVE-2024-52304.patch \ file://CVE-2026-34993.patch \ file://CVE-2026-34518.patch \ file://CVE-2026-34519.patch \ + file://CVE-2026-34516.patch \ " CVE_STATUS[CVE-2026-34515] = "not-applicable-platform: Vulnerability only affects applications running on Windows"