Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/13415.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed an integer overflow on too large messages -- by :user:`Dreamsorcerer`.
1 change: 1 addition & 0 deletions aiohttp/_websocket/reader_c.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ cdef class WebSocketReader:
has_mask=bint,
fin=bint,
had_fragments=Py_ssize_t,
partial_len=Py_ssize_t,
payload_bytearray=bytearray,
)
cpdef void _feed_data(self, bytes data) except *
9 changes: 6 additions & 3 deletions aiohttp/_websocket/reader_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,11 +457,14 @@ def _feed_data(self, data: bytes) -> None:
OP_CODE_BINARY,
OP_CODE_CONTINUATION,
}:
projected_size = self._payload_bytes_to_read + len(self._partial)
if projected_size >= self._max_msg_size:
# partial_len declared in reader_c.pxd to keep it in C.
partial_len = len(self._partial)
# payload_bytes_to_read is a signed 64-bit C value,
# use subtraction here to avoid an integer overflow.
if self._payload_bytes_to_read >= self._max_msg_size - partial_len:
raise WebSocketError(
WSCloseCode.MESSAGE_TOO_BIG,
f"Message size {projected_size} "
f"Message size {int(self._payload_bytes_to_read) + partial_len} "
f"exceeds limit {self._max_msg_size}",
)

Expand Down
27 changes: 27 additions & 0 deletions tests/test_websocket_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,33 @@ def test_msg_too_large_text_after_non_fin_text(out: WebSocketDataQueue) -> None:
assert ctx.value.code == WSCloseCode.MESSAGE_TOO_BIG


def test_msg_too_large_declared_length_near_ssize_t_max(
out: WebSocketDataQueue,
) -> None:
# Regression test: the header-time size check compares
# `payload_bytes_to_read + len(partial)` against max_msg_size. In the
# compiled Cython reader payload_bytes_to_read is a signed 64-bit C
# value, and RFC 6455 allows a declared length up to 2**63-1, so a
# naive addition can wrap around to a negative number and bypass the
# limit entirely once anything is already buffered in `partial`.
max_msg_size = 4 * 1024 * 1024
parser = WebSocketReader(out, max_msg_size, compress=False, decode_text=True)

# Buffer one byte in `partial` via a non-fin fragment.
first = build_frame(b"a", WSMsgType.TEXT, is_fin=False)
parser._feed_data(first)

# Continuation header alone declares the maximum length a signed
# 64-bit payload_bytes_to_read can hold: 2**63-1.
header = PACK_LEN3(WSMsgType.CONTINUATION, 127, 2**63 - 1)
with pytest.raises(
WebSocketError,
match=rf"^Message size {2**63} exceeds limit {max_msg_size}$",
) as ctx:
parser._feed_data(header)
assert ctx.value.code == WSCloseCode.MESSAGE_TOO_BIG


@pytest.mark.parametrize(
"opcode",
(0x3, 0x4, 0x5, 0x6, 0x7, 0xB, 0xC, 0xD, 0xE, 0xF),
Expand Down
Loading