Skip to content

mplex: frame body read is unbounded (no message-size limit) #1436

Description

@yashksaini-coder

Mplex.read_message reads the frame body via read_varint_prefixed_bytes (mplex.py:273 → varint.py), which takes the length straight from the wire and calls read_exactly with no cap. Every muxer besides mplex bounds this: yamux rejects DATA frames > MAX_WINDOW_SIZE (16 MB) and read_length_prefixed_protobuf caps at 1 MB. go-mplex enforces MaxMessageSize = 1 MiB. So a peer can make the victim buffer an arbitrarily large frame (bandwidth-bound; amplified across many streams/connections).

Why it's not a one-line cap. read_exactly doesn't pre-allocate (no amplification per frame), and mplex write() sends each app write as a single unchunked frame — so a read cap alone breaks legitimate py↔py writes larger than the cap. Pairing it with write-chunking (like yamux) then runs into mplex's flow control: _handle_message pushes each frame into a per-stream channel of size MPLEX_MESSAGE_CHANNEL_SIZE = 8 and resets the stream on overflow — so chunking a large write into many frames can spuriously reset it.

Proposed direction:

  1. Add an optional max_length to read_varint_prefixed_bytes and enforce a message-size limit in mplex (spec-aligned, e.g. 1 MiB → MplexUnavailable).
  2. Chunk writes at that limit, and move per-stream flow control from message-count to a byte-based buffer so chunking doesn't trip the 8-frame reset.

Flagging for maintainer input on the flow-control policy before a PR, since (2) changes behavior. Related: #572 (resource exhaustion), #534 (mplex→yamux).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions