python3-aiohttp: fix CVE-2026-111750

Backport the upstream HEAD request parsing fix [1] to aiohttp 3.13.5,
as identified by [2]. Only responses to HEAD may skip their body;
incoming HEAD requests must consume the declared request body.

Retain the upstream regression that embeds a request in a HEAD body
and verifies that the following request remains correctly framed.
The pure-Python parser remains available with extensions installed.

[1] https://github.com/aio-libs/aiohttp/commit/9b27347e61454d9f8ca2055f4c67a37dee336e83
[2] https://github.com/aio-libs/aiohttp/security/advisories/GHSA-pqxq-w44r-73xw

Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Darsh Kelaiya authored and Anuj Mittal committed 2026-11-09 09:10:48 +05:30
1 parent ba8d0af836
commit e04156eb37
2 files changed
+84

No files matched your search

@@ -0,0 +1,83 @@
From 9b27347e61454d9f8ca2055f4c67a37dee336e83 Mon Sep 17 00:00:00 2001
From: Sam Bull <git@sambull.org>
Date: Wed, 9 Sep 2026 01:25:13 +0100
Subject: [PATCH] Fix head request parsing (#13671) (#13673)
(cherry picked from commit 5789546e37fe35af27d0a2f840870b22bc15bfc4)
CVE: CVE-2026-111750
Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/9b27347e61454d9f8ca2055f4c67a37dee336e83]
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
CHANGES/13671.bugfix.rst | 1 +
aiohttp/http_parser.py | 6 +++++-
tests/test_http_parser.py | 27 ++++++++++++++++++++++++++-
3 files changed, 32 insertions(+), 2 deletions(-)
create mode 100644 CHANGES/13671.bugfix.rst
diff --git a/CHANGES/13671.bugfix.rst b/CHANGES/13671.bugfix.rst
new file mode 100644
index 0000000..e6fbc70
--- /dev/null
+++ b/CHANGES/13671.bugfix.rst
@@ -0,0 +1 @@
+Fixed pure-Python request parser not reading a body in a ``HEAD`` request -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py
index f4458dc..ce15764 100644
--- a/aiohttp/http_parser.py
+++ b/aiohttp/http_parser.py
@@ -426,8 +426,12 @@ class HttpParser(abc.ABC, Generic[_MsgT]):
assert self.protocol is not None
# calculate payload
+ # https://www.rfc-editor.org/info/rfc9112/#name-message-body-length
+ # https://www.rfc-editor.org/info/rfc9110/#section-9.3.1-6
+ # EMPTY_BODY_METHODS should only apply to responses.
+ # self.method is None on request parser.
empty_body = code in EMPTY_BODY_STATUS_CODES or bool(
- method and method in EMPTY_BODY_METHODS
+ self.method and self.method in EMPTY_BODY_METHODS
)
if not empty_body and (
(length is not None and length > 0) or msg.chunked
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index 4cdd94c..4255f4d 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -1781,7 +1781,32 @@ def test_http_request_chunked_payload_and_next_message(parser) -> None:
assert not payload2.is_eof()
-def test_http_request_chunked_payload_chunks(parser) -> None:
+def test_http_request_parser_head_with_content_length_payload(
+ parser: HttpRequestParser,
+) -> None:
+ smuggled = b"GET /smuggled HTTP/1.1\r\nHost: a\r\n\r\n"
+ text = (
+ b"HEAD /test HTTP/1.1\r\nHost: a\r\nContent-Length: %d\r\n\r\n" % len(smuggled)
+ + smuggled
+ + b"POST /next HTTP/1.1\r\nHost: a\r\nContent-Length: 0\r\n\r\n"
+ )
+ messages, upgraded, tail = parser.feed_data(text)
+
+ assert len(messages) == 2
+ msg, payload = messages[0]
+ assert msg.method == "HEAD"
+ assert b"".join(payload._buffer) == smuggled
+ assert payload.is_eof()
+
+ msg2, payload2 = messages[1]
+ assert msg2.method == "POST"
+ assert msg2.path == "/next"
+ assert payload2.is_eof()
+ assert not upgraded
+ assert not tail
+
+
+def test_http_request_chunked_payload_chunks(parser: HttpRequestParser) -> None:
text = b"GET /test HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n"
msg, payload = parser.feed_data(text)[0][0]
--
2.35.6
@@ -24,6 +24,7 @@ SRC_URI += " \
file://CVE-2026-69244.patch \
file://CVE-2026-111066_p1.patch \
file://CVE-2026-111066_p2.patch \
file://CVE-2026-111750.patch \
"
CVE_PRODUCT = "aiohttp:aiohttp"