libp2p / libp2p/rust-yamux

Read-side deadlock in `Connection::poll` under symmetric back-pressure

Open
#227 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
250
Forks
62
PR merge metrics
No merged PRs in 30d

Description

We hit this running a downstream fork (the TLSN MPC stack) under heavy concurrent multi-stream load. Patched it on our side in [tlsnotary/tlsn-utils@b452352][1], and the author flagged that the underlying coupling is inherited from upstream rust-yamux. Looking at current master, the same shape is still there, so filing rather than just sitting on it.

## What happens

`Connection::poll` gates socket reads on having no pending control reply queued — [yamux/src/connection.rs#L456][2]:

```rust
if self.pending_read_frame.is_none() {
match self.socket.poll_next_unpin(cx) { ... }
}
```

`pending_read_frame` is a one-slot buffer for control frames produced inside the read loop — a Pong in reply to an inbound Ping, or a GoAway
from a protocol error. The gate is clobber-prevention: if you read another inbound frame while the slot is full, its processing might also
produce a reply that has nowhere to go.

That's fine as long as the slot drains quickly. It only drains via `start_send_unpin`, which only runs when
`socket.poll_ready_unpin().is_ready()` — i.e., when the TCP send buffer has room. So the implicit assumption is "writes never block for long."

Under symmetric back-pressure that assumption fails. With both peers pushing stream data, each side's send buffer fills. A Ping arriving at
either side gets a Pong queued into `pending_read_frame` — and stays there, because the socket isn't writable. Both sides now have the read gate
closed; neither drains the other's send buffer; nothing recovers. Stream commands sit stranded in `stream_receivers` indefinitely. It's a
deadlock, not a transient stall.

This isn't a pathological workload — it's what symmetric application-level multiplexing tends to produce. We see it under heavy concurrent
multi-stream traffic where both peers are simultaneously busy enough for their send buffers to fill.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in yamux/src/connection.rs at Connection::poll, especially the pending_read_frame gate around line 456, and compare the behavior with tlsnotary/tlsn-utils@b452352. Exercise symmetric multi-stream back-pressure with both peers sending data; done means stream commands continue progressing instead of remaining stranded in stream_receivers.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.