mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-11-07 13:44:50 +00:00
python3-aiohttp: fix CVE-2026-54274
This patch applies the reviewed upstream fix shown in [1]. The advisory identifying the fix is referenced in [2]. [1] https://github.com/aio-libs/aiohttp/commit/14b6ee851fb16ec199acb950de0c82d476799e7d [2] https://nvd.nist.gov/vuln/detail/CVE-2026-54274 Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
1 parent
8e776e0477
commit
bb01c56027
2 files changed
+196
No files matched your search
@@ -0,0 +1,195 @@
|
||||
From 23b4506906b28c91439bce6d5cbce2b9989ffe61 Mon Sep 17 00:00:00 2001
|
||||
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
|
||||
Date: Sat, 6 Jun 2026 00:05:19 +0100
|
||||
Subject: [PATCH] [PR #12817/69344c6e backport][3.14] Improve websocket checks
|
||||
(#12818)
|
||||
|
||||
**This is a backport of PR #12817 as merged into master
|
||||
(69344c6efa3e5dd80b1c88079fa06d4e902a3b83).**
|
||||
|
||||
CVE: CVE-2026-54274
|
||||
Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/14b6ee851fb16ec199acb950de0c82d476799e7d]
|
||||
|
||||
Backport Changes:
|
||||
- Adapt the fix from aiohttp/_websocket/reader_py.py in aiohttp 3.14
|
||||
to aiohttp/http_websocket.py in 3.9.5, mapping OP_CODE_* to
|
||||
WSMsgType.* and _payload_bytes_to_read to _payload_length.
|
||||
- Preserve the older parser's two partial-message size-check removals,
|
||||
accounting for the additional deletion relative to upstream.
|
||||
- Omit WebSocketDataQueue annotations because that type is not imported
|
||||
by the 3.9.5 websocket parser test module.
|
||||
|
||||
Co-authored-by: Sam Bull <git@sambull.org>
|
||||
(cherry picked from commit 14b6ee851fb16ec199acb950de0c82d476799e7d)
|
||||
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
|
||||
---
|
||||
CHANGES/12817.bugfix.rst | 1 +
|
||||
aiohttp/http_websocket.py | 43 ++++++++++++++-------
|
||||
tests/test_websocket_parser.py | 68 ++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 98 insertions(+), 14 deletions(-)
|
||||
create mode 100644 CHANGES/12817.bugfix.rst
|
||||
|
||||
diff --git a/CHANGES/12817.bugfix.rst b/CHANGES/12817.bugfix.rst
|
||||
new file mode 100644
|
||||
index 000000000..c8a35e309
|
||||
--- /dev/null
|
||||
+++ b/CHANGES/12817.bugfix.rst
|
||||
@@ -0,0 +1 @@
|
||||
+Tightened up some websocket parser checks -- by :user:`Dreamsorcerer`.
|
||||
diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py
|
||||
index 39f2e4a5c..6d95f386d 100644
|
||||
--- a/aiohttp/http_websocket.py
|
||||
+++ b/aiohttp/http_websocket.py
|
||||
@@ -370,13 +370,6 @@ class WebSocketReader:
|
||||
if opcode != WSMsgType.CONTINUATION:
|
||||
self._opcode = opcode
|
||||
self._partial.extend(payload)
|
||||
- if self._max_msg_size and len(self._partial) >= self._max_msg_size:
|
||||
- raise WebSocketError(
|
||||
- WSCloseCode.MESSAGE_TOO_BIG,
|
||||
- "Message size {} exceeds limit {}".format(
|
||||
- len(self._partial), self._max_msg_size
|
||||
- ),
|
||||
- )
|
||||
else:
|
||||
# previous frame was non finished
|
||||
# we should get continuation opcode
|
||||
@@ -394,13 +387,6 @@ class WebSocketReader:
|
||||
self._opcode = None
|
||||
|
||||
self._partial.extend(payload)
|
||||
- if self._max_msg_size and len(self._partial) >= self._max_msg_size:
|
||||
- raise WebSocketError(
|
||||
- WSCloseCode.MESSAGE_TOO_BIG,
|
||||
- "Message size {} exceeds limit {}".format(
|
||||
- len(self._partial), self._max_msg_size
|
||||
- ),
|
||||
- )
|
||||
|
||||
# Decompress process must to be done after all packets
|
||||
# received.
|
||||
@@ -482,6 +468,19 @@ class WebSocketReader:
|
||||
"Received frame with non-zero reserved bits",
|
||||
)
|
||||
|
||||
+ if opcode not in {
|
||||
+ WSMsgType.CONTINUATION,
|
||||
+ WSMsgType.TEXT,
|
||||
+ WSMsgType.BINARY,
|
||||
+ WSMsgType.CLOSE,
|
||||
+ WSMsgType.PING,
|
||||
+ WSMsgType.PONG,
|
||||
+ }:
|
||||
+ raise WebSocketError(
|
||||
+ WSCloseCode.PROTOCOL_ERROR,
|
||||
+ f"Unexpected opcode={opcode!r}",
|
||||
+ )
|
||||
+
|
||||
if opcode > 0x7 and fin == 0:
|
||||
raise WebSocketError(
|
||||
WSCloseCode.PROTOCOL_ERROR,
|
||||
@@ -555,6 +554,22 @@ class WebSocketReader:
|
||||
else WSParserState.READ_PAYLOAD
|
||||
)
|
||||
|
||||
+ # Reject oversized data frames before buffering any payload
|
||||
+ # bytes. Control frames are capped at 125 bytes (checked in
|
||||
+ # READ_HEADER) so only text/binary/continuation need this.
|
||||
+ if self._max_msg_size and self._frame_opcode in {
|
||||
+ WSMsgType.TEXT,
|
||||
+ WSMsgType.BINARY,
|
||||
+ WSMsgType.CONTINUATION,
|
||||
+ }:
|
||||
+ projected_size = self._payload_length + len(self._partial)
|
||||
+ if projected_size >= self._max_msg_size:
|
||||
+ raise WebSocketError(
|
||||
+ WSCloseCode.MESSAGE_TOO_BIG,
|
||||
+ f"Message size {projected_size} "
|
||||
+ f"exceeds limit {self._max_msg_size}",
|
||||
+ )
|
||||
+
|
||||
# read payload mask
|
||||
if self._state == WSParserState.READ_PAYLOAD_MASK:
|
||||
if buf_length - start_pos >= 4:
|
||||
diff --git a/tests/test_websocket_parser.py b/tests/test_websocket_parser.py
|
||||
index 3bdd8108e..b9efe9dcf 100644
|
||||
--- a/tests/test_websocket_parser.py
|
||||
+++ b/tests/test_websocket_parser.py
|
||||
@@ -498,6 +498,74 @@ def test_compressed_msg_too_large(out) -> None:
|
||||
assert ctx.value.code == WSCloseCode.MESSAGE_TOO_BIG
|
||||
|
||||
|
||||
+@pytest.mark.parametrize("fin", (0x80, 0x00), ids=("fin", "non-fin"))
|
||||
+def test_msg_too_large_at_header(out, fin: int) -> None:
|
||||
+ max_msg_size = 256
|
||||
+ parser = WebSocketReader(out, max_msg_size, compress=False)
|
||||
+
|
||||
+ # Header alone: TEXT, 64-bit length, declares 1 MiB of payload.
|
||||
+ header = PACK_LEN3(fin | WSMsgType.TEXT, 127, 1024 * 1024)
|
||||
+ with pytest.raises(
|
||||
+ WebSocketError, match=r"^Message size 1048576 exceeds limit 256$"
|
||||
+ ) as ctx:
|
||||
+ parser._feed_data(header)
|
||||
+ assert ctx.value.code == WSCloseCode.MESSAGE_TOO_BIG
|
||||
+
|
||||
+
|
||||
+def test_msg_too_large_across_fragments(out) -> None:
|
||||
+ # Individual fragments fit under max_msg_size but accumulate past it.
|
||||
+ max_msg_size = 256
|
||||
+ parser = WebSocketReader(out, max_msg_size, compress=False)
|
||||
+
|
||||
+ first = build_frame(b"a" * 100, WSMsgType.TEXT, is_fin=False)
|
||||
+ parser._feed_data(first)
|
||||
+ middle = build_frame(b"b" * 100, WSMsgType.CONTINUATION, is_fin=False)
|
||||
+ parser._feed_data(middle)
|
||||
+
|
||||
+ # Third 100-byte fragment would push the accumulated total to 300.
|
||||
+ last = build_frame(b"c" * 100, WSMsgType.CONTINUATION, is_fin=False)
|
||||
+ with pytest.raises(
|
||||
+ WebSocketError, match=r"^Message size 300 exceeds limit 256$"
|
||||
+ ) as ctx:
|
||||
+ parser._feed_data(last)
|
||||
+ assert ctx.value.code == WSCloseCode.MESSAGE_TOO_BIG
|
||||
+
|
||||
+
|
||||
+def test_msg_too_large_text_after_non_fin_text(out) -> None:
|
||||
+ # Protocol-violating sequence: a fresh TEXT arrives while a fragmented
|
||||
+ # message is still open.
|
||||
+ max_msg_size = 256
|
||||
+ parser = WebSocketReader(out, max_msg_size, compress=False)
|
||||
+
|
||||
+ first = build_frame(b"a" * 200, WSMsgType.TEXT, is_fin=False)
|
||||
+ parser._feed_data(first)
|
||||
+
|
||||
+ # Second TEXT header alone announces 100 bytes; 100 + 200 partial = 300.
|
||||
+ second_header = PACK_LEN1(WSMsgType.TEXT, 100)
|
||||
+ with pytest.raises(
|
||||
+ WebSocketError, match=r"^Message size 300 exceeds limit 256$"
|
||||
+ ) as ctx:
|
||||
+ parser._feed_data(second_header)
|
||||
+ assert ctx.value.code == WSCloseCode.MESSAGE_TOO_BIG
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize(
|
||||
+ "opcode",
|
||||
+ (0x3, 0x4, 0x5, 0x6, 0x7, 0xB, 0xC, 0xD, 0xE, 0xF),
|
||||
+ ids=lambda v: f"0x{v:x}",
|
||||
+)
|
||||
+def test_reserved_opcode_rejected_at_header(
|
||||
+ out, opcode: int
|
||||
+) -> None:
|
||||
+ # RFC 6455 reserves opcodes 0x3-0x7 (non-control) and 0xB-0xF (control).
|
||||
+ parser = WebSocketReader(out, max_msg_size=256, compress=False)
|
||||
+
|
||||
+ header = PACK_LEN3(0x80 | opcode, 127, 1024 * 1024)
|
||||
+ with pytest.raises(WebSocketError, match=rf"^Unexpected opcode={opcode}$") as ctx:
|
||||
+ parser._feed_data(header)
|
||||
+ assert ctx.value.code == WSCloseCode.PROTOCOL_ERROR
|
||||
+
|
||||
+
|
||||
class TestWebSocketError:
|
||||
def test_ctor(self) -> None:
|
||||
err = WebSocketError(WSCloseCode.PROTOCOL_ERROR, "Something invalid")
|
||||
--
|
||||
2.35.6
|
||||
|
||||
Reference in new issue
Block a user