[oe][meta-python][wrynose][PATCH 07/10] python3-aiohttp: fix CVE-2026-54277

"Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)" <[email protected]>
Newsgroups org.openembedded.lists.openembedded-devel
Message-ID <[email protected]>
From: Darsh Kelaiya <[email protected]>

This patch applies the upstream fix as referenced in [2], using
the commit shown in [1].

[1] https://github.com/aio-libs/aiohttp/commit/5ab61bb4cd88f19b712f12c7c9295fe262bf804d
[2] https://github.com/advisories/GHSA-63hw-fmq6-xxg2

Signed-off-by: Darsh Kelaiya <[email protected]>
---
Changes in v2
- Added the corresponding generated aiohttp/_http_parser.c changes.
- Updated cb_on_url() and cb_on_status() in the generated C parser to
  enforce max_line_size against the accumulated parser buffer.
- Ensured the security fix is included when Wrynose builds the
  generated C parser without regenerating it from the patched .pyx.
- Documented the Wrynose-specific backport changes in the patch
  metadata.
---
 .../python3-aiohttp/CVE-2026-54277.patch      | 249 ++++++++++++++++++
 .../python/python3-aiohttp_3.13.5.bb          |   1 +
 2 files changed, 250 insertions(+)
 create mode 100644 meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54277.patch

diff --git a/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54277.patch b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54277.patch
new file mode 100644
index 0000000000..db50155d33
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54277.patch
@@ -0,0 +1,249 @@
+From f61e8949f10a0e77ceba28a6639627e6e57bf167 Mon Sep 17 00:00:00 2001
+From: "J. Nick Koston" <[email protected]>
+Date: Sun, 7 Jun 2026 00:33:03 -0500
+Subject: [PATCH] [PR #12826/36df6c13 backport][3.14] Enforce max_line_size on
+ fragmented request target and reason in C parser (#12837)
+
+CVE: CVE-2026-54277
+Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/5ab61bb4cd88f19b712f12c7c9295fe262bf804d]
+
+Backport Changes:
+- Added aiohttp/_http_parser.c because Wrynose builds the
+  pre-generated C parser without running Cython.
+- Updated cb_on_url() and cb_on_status() to enforce max_line_size
+  against the accumulated parser buffer.
+
+(cherry picked from commit 5ab61bb4cd88f19b712f12c7c9295fe262bf804d)
+Signed-off-by: Darsh Kelaiya <[email protected]>
+---
+ CHANGES/12826.bugfix.rst  |  1 +
+ aiohttp/_http_parser.c    | 36 ++++++++++++++++++------------------
+ aiohttp/_http_parser.pyx  |  4 ++--
+ tests/test_http_parser.py | 22 ++++++++++++++++++++++
+ 4 files changed, 43 insertions(+), 20 deletions(-)
+ create mode 100644 CHANGES/12826.bugfix.rst
+
+diff --git a/CHANGES/12826.bugfix.rst b/CHANGES/12826.bugfix.rst
+new file mode 100644
+index 000000000..7e095615d
+--- /dev/null
++++ b/CHANGES/12826.bugfix.rst
+@@ -0,0 +1 @@
++Fixed the C HTTP parser not enforcing ``max_line_size`` on a request target or response reason phrase that is split across multiple reads; each fragment was checked on its own, so an accumulated line could exceed the limit without raising ``LineTooLong``. The accumulated length is now checked, matching the pure-Python parser -- by :user:`bdraco`.
+diff --git a/aiohttp/_http_parser.c b/aiohttp/_http_parser.c
+index 911c9aa65..14c5b3350 100644
+--- a/aiohttp/_http_parser.c
++++ b/aiohttp/_http_parser.c
+@@ -14760,7 +14760,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_url(llhttp_t *__pyx_v_parser, c
+  *                    const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data             # <<<<<<<<<<<<<<
+  *     try:
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+ */
+   __pyx_t_1 = ((PyObject *)__pyx_v_parser->data);
+   __Pyx_INCREF(__pyx_t_1);
+@@ -14771,7 +14771,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_url(llhttp_t *__pyx_v_parser, c
+  *                    const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:             # <<<<<<<<<<<<<<
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             status = pyparser._buf + at[:length]
+ */
+   {
+@@ -14786,16 +14786,16 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_url(llhttp_t *__pyx_v_parser, c
+       /* "aiohttp/_http_parser.pyx":721
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:
+- *         if length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
+  *             status = pyparser._buf + at[:length]
+  *             raise LineTooLong(status[:100] + b"...", pyparser._max_line_size)
+ */
+-      __pyx_t_5 = (__pyx_v_length > __pyx_v_pyparser->_max_line_size);
++      __pyx_t_5 = ((__Pyx_PyByteArray_GET_SIZE(__pyx_v_pyparser->_buf) + __pyx_v_length) > __pyx_v_pyparser->_max_line_size);
+       if (unlikely(__pyx_t_5)) {
+ 
+         /* "aiohttp/_http_parser.pyx":722
+  *     try:
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             status = pyparser._buf + at[:length]             # <<<<<<<<<<<<<<
+  *             raise LineTooLong(status[:100] + b"...", pyparser._max_line_size)
+  *         extend(pyparser._buf, at, length)
+@@ -14809,7 +14809,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_url(llhttp_t *__pyx_v_parser, c
+         __pyx_t_6 = 0;
+ 
+         /* "aiohttp/_http_parser.pyx":723
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             status = pyparser._buf + at[:length]
+  *             raise LineTooLong(status[:100] + b"...", pyparser._max_line_size)             # <<<<<<<<<<<<<<
+  *         extend(pyparser._buf, at, length)
+@@ -14854,7 +14854,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_url(llhttp_t *__pyx_v_parser, c
+         /* "aiohttp/_http_parser.pyx":721
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:
+- *         if length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
+  *             status = pyparser._buf + at[:length]
+  *             raise LineTooLong(status[:100] + b"...", pyparser._max_line_size)
+ */
+@@ -14878,7 +14878,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_url(llhttp_t *__pyx_v_parser, c
+  *                    const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:             # <<<<<<<<<<<<<<
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             status = pyparser._buf + at[:length]
+ */
+     }
+@@ -14968,7 +14968,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_url(llhttp_t *__pyx_v_parser, c
+  *                    const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:             # <<<<<<<<<<<<<<
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             status = pyparser._buf + at[:length]
+ */
+     __pyx_L5_except_error:;
+@@ -15045,7 +15045,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_status(llhttp_t *__pyx_v_parser
+  *                       const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data             # <<<<<<<<<<<<<<
+  *     try:
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+ */
+   __pyx_t_1 = ((PyObject *)__pyx_v_parser->data);
+   __Pyx_INCREF(__pyx_t_1);
+@@ -15056,7 +15056,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_status(llhttp_t *__pyx_v_parser
+  *                       const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:             # <<<<<<<<<<<<<<
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             reason = pyparser._buf + at[:length]
+ */
+   {
+@@ -15071,16 +15071,16 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_status(llhttp_t *__pyx_v_parser
+       /* "aiohttp/_http_parser.pyx":736
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:
+- *         if length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
+  *             reason = pyparser._buf + at[:length]
+  *             raise LineTooLong(reason[:100] + b"...", pyparser._max_line_size)
+ */
+-      __pyx_t_5 = (__pyx_v_length > __pyx_v_pyparser->_max_line_size);
++      __pyx_t_5 = ((__Pyx_PyByteArray_GET_SIZE(__pyx_v_pyparser->_buf) + __pyx_v_length) > __pyx_v_pyparser->_max_line_size);
+       if (unlikely(__pyx_t_5)) {
+ 
+         /* "aiohttp/_http_parser.pyx":737
+  *     try:
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             reason = pyparser._buf + at[:length]             # <<<<<<<<<<<<<<
+  *             raise LineTooLong(reason[:100] + b"...", pyparser._max_line_size)
+  *         extend(pyparser._buf, at, length)
+@@ -15094,7 +15094,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_status(llhttp_t *__pyx_v_parser
+         __pyx_t_6 = 0;
+ 
+         /* "aiohttp/_http_parser.pyx":738
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             reason = pyparser._buf + at[:length]
+  *             raise LineTooLong(reason[:100] + b"...", pyparser._max_line_size)             # <<<<<<<<<<<<<<
+  *         extend(pyparser._buf, at, length)
+@@ -15139,7 +15139,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_status(llhttp_t *__pyx_v_parser
+         /* "aiohttp/_http_parser.pyx":736
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:
+- *         if length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:             # <<<<<<<<<<<<<<
+  *             reason = pyparser._buf + at[:length]
+  *             raise LineTooLong(reason[:100] + b"...", pyparser._max_line_size)
+ */
+@@ -15163,7 +15163,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_status(llhttp_t *__pyx_v_parser
+  *                       const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:             # <<<<<<<<<<<<<<
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             reason = pyparser._buf + at[:length]
+ */
+     }
+@@ -15253,7 +15253,7 @@ static int __pyx_f_7aiohttp_12_http_parser_cb_on_status(llhttp_t *__pyx_v_parser
+  *                       const char *at, size_t length) except -1:
+  *     cdef HttpParser pyparser = <HttpParser>parser.data
+  *     try:             # <<<<<<<<<<<<<<
+- *         if length > pyparser._max_line_size:
++ *         if len(pyparser._buf) + length > pyparser._max_line_size:
+  *             reason = pyparser._buf + at[:length]
+ */
+     __pyx_L5_except_error:;
+diff --git a/aiohttp/_http_parser.pyx b/aiohttp/_http_parser.pyx
+index 5da835bc6..e1edee310 100644
+--- a/aiohttp/_http_parser.pyx
++++ b/aiohttp/_http_parser.pyx
+@@ -718,7 +718,7 @@ cdef int cb_on_url(cparser.llhttp_t* parser,
+                    const char *at, size_t length) except -1:
+     cdef HttpParser pyparser = <HttpParser>parser.data
+     try:
+-        if length > pyparser._max_line_size:
++        if len(pyparser._buf) + length > pyparser._max_line_size:
+             status = pyparser._buf + at[:length]
+             raise LineTooLong(status[:100] + b"...", pyparser._max_line_size)
+         extend(pyparser._buf, at, length)
+@@ -733,7 +733,7 @@ cdef int cb_on_status(cparser.llhttp_t* parser,
+                       const char *at, size_t length) except -1:
+     cdef HttpParser pyparser = <HttpParser>parser.data
+     try:
+-        if length > pyparser._max_line_size:
++        if len(pyparser._buf) + length > pyparser._max_line_size:
+             reason = pyparser._buf + at[:length]
+             raise LineTooLong(reason[:100] + b"...", pyparser._max_line_size)
+         extend(pyparser._buf, at, length)
+diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
+index 25604dbcc..8cb591f20 100644
+--- a/tests/test_http_parser.py
++++ b/tests/test_http_parser.py
+@@ -1272,6 +1272,17 @@ def test_http_request_max_status_line_under_limit(parser: HttpRequestParser) ->
+     assert msg.url == URL("/path" + path.decode())
+ 
+ 
++def test_http_request_max_status_line_fragmented(
++    parser: HttpRequestParser,
++) -> None:
++    # Split an overlong request target across reads so that each callback
++    # fragment is under the limit but the accumulated target is not.
++    match = "400, message:\n  Got more than 8190 bytes when reading"
++    with pytest.raises(http_exceptions.LineTooLong, match=match):
++        parser.feed_data(b"GET /" + b"a" * 8000)
++        parser.feed_data(b"a" * 8000 + b" HTTP/1.1\r\nHost: a\r\n\r\n")
++
++
+ def test_http_response_parser_utf8(response) -> None:
+     text = "HTTP/1.1 200 Ok\r\nx-test:тест\r\n\r\n".encode()
+ 
+@@ -1349,6 +1360,17 @@ def test_http_response_parser_status_line_under_limit(
+     assert msg.reason == reason.decode()
+ 
+ 
++def test_http_response_parser_status_line_too_long_fragmented(
++    response: HttpResponseParser,
++) -> None:
++    # Split an overlong reason phrase across reads so that each callback
++    # fragment is under the limit but the accumulated reason is not.
++    match = "400, message:\n  Got more than 8190 bytes when reading"
++    with pytest.raises(http_exceptions.LineTooLong, match=match):
++        response.feed_data(b"HTTP/1.1 200 " + b"a" * 8000)
++        response.feed_data(b"a" * 8000 + b"\r\n\r\n")
++
++
+ def test_http_response_parser_bad_version(response) -> None:
+     with pytest.raises(http_exceptions.BadHttpMessage):
+         response.feed_data(b"HT/11 200 Ok\r\n\r\n")
+-- 
+2.35.6
+
diff --git a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
index 3c07933200..1a6baebfe9 100644
--- a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
+++ b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
@@ -13,6 +13,7 @@ SRC_URI += " \
     file://CVE-2026-54274.patch \
     file://CVE-2026-54275.patch \
     file://CVE-2026-54276.patch \
+    file://CVE-2026-54277.patch \
 "
 
 CVE_PRODUCT = "aiohttp"
-- 
2.35.6
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.