Skip to content

frame/io: Introduce Poisoned state for WriteState#211

Merged
jxs merged 1 commit intolibp2p:masterfrom
lexnv:lexnv/yamux-panic
Oct 23, 2025
Merged

frame/io: Introduce Poisoned state for WriteState#211
jxs merged 1 commit intolibp2p:masterfrom
lexnv:lexnv/yamux-panic

Conversation

@lexnv
Copy link
Copy Markdown
Contributor

@lexnv lexnv commented Oct 21, 2025

The Sink implementation of the yamux-frame could panic when the number of bytes from a write operation exceeds the header size. This edge case was fixed in:

However, poll_flush and poll_close would still call into poll_ready. In these cases, the frame/io component would still panic since the offset is bigger than the actual header. To mitigate this, the PR introduces a Poisoned state, and further calls to poll_ready would result in an immediate error.

// cc @jxs @elenaf9

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Copy link
Copy Markdown
Member

@jxs jxs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Alexandru and thanks for this follow up. LGTM.
Do you need a release with this?

@jxs jxs merged commit 874455d into libp2p:master Oct 23, 2025
3 checks passed
@lexnv
Copy link
Copy Markdown
Contributor Author

lexnv commented Oct 23, 2025

Hey @jxs thanks a lot for the quick review!

Do you need a release with this?

Yep, would love to have this in a release 🙏

lexnv added a commit to paritytech/litep2p that referenced this pull request Jan 20, 2026
…plementation (#518)

The PR fixes a `AsyncWrite::poll_write` implementation of the
crypto/noise sockets that causes panics in rust-yamux and leads to
unnecessary connection closure and instability:
- T0: poll_write is called with buffer of len 512 bytes
  - the implementation encrypt data and buffers it
  - because the io socket is not ready, poll pending is returned.
- T1: tokio_tungstenite (or other component) decides that a new payload
must be sent (usually a PONG frame) of len 12 bytes
- because the inner buffers contain the previous message, upon flushing
the size of the older message is returned (ie 512)
- rust-yamux uses the 512 bytes to index a buffer of 12 bytes (as
expected by the second poll_write with buffer 12)
- This caused frequent panics on the websocket implementation, which is
currently addressed as abrupt connection termination
  
This PR fixes the `AsyncWrite` contract violation by effectively
decoupling the encryption from the writing steps.
- the inner buffers are drained as much as possible (until the socket
returns poll pending)
- The provided message is encrypted into the inner buffer if it has
capacity and the number of bytes is returned immediately
- a subsequent poll_write or poll_flush or poll_close will propagate the
encrypted buffered data to the underlying socket

Previous fixes:
- libp2p/rust-yamux#202
- libp2p/rust-yamux#211

The fixes are still needed since the tokio-tungstenite (websocket crate)
was not properly scoped and may exhibit similar behavior.

cc @paritytech/networking

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants