Skip to content

Make pooled virtual L4 streams reusable - #971

Open
nbarbier-265 wants to merge 2 commits into
cloudflare:mainfrom
nbarbier-265:virtual-stream-reuse
Open

Make pooled virtual L4 streams reusable#971
nbarbier-265 wants to merge 2 commits into
cloudflare:mainfrom
nbarbier-265:virtual-stream-reuse

Conversation

@nbarbier-265

Copy link
Copy Markdown
Contributor

Fixes #883.

Virtual streams report fd -1, so the fd-based reuse check in reused_stream() always fails at getpeername() and pooled virtual streams can never be reused. The shared -1 id also breaks pool bookkeeping: ConnectionMeta keys connections by (group, id) and PoolNode stores them in a HashMap by id, so pooling a second virtual stream under the same group key silently overwrites the first.

Two changes:

  • Every virtual stream gets a unique ID from a range no real fd can occupy: counting down from -2 on unix (-1 stays the "no fd" sentinel), from usize::MAX - 1 on windows (usize::MAX is INVALID_SOCKET). as_raw_fd() still returns -1 since there is no real fd.
  • In reused_stream(), a negative ID routes to an address comparison instead of getpeername(): the peer address recorded in the stream's socket digest must equal the peer's address. SocketDigest::peer_addr is a public OnceCell, so creators of virtual streams opt in by presetting it (e.g. SocketDigest::from_raw_fd(-1) + peer_addr.set(...)), which also keeps the lazy getpeername from running. Streams without a recorded peer address are never reused, so existing virtual streams keep today's behavior.

The address comparison is unix-only; on windows virtual streams remain non-reusable as today (the matches_sock check fails on the synthetic handle), but the unique IDs fix the pool overwrite there too.

Tests: unique/negative IDs; a pooled virtual stream with a matching recorded peer address is reused exactly once; a stream pooled under another peer's key (as a reuse_hash collision would) is rejected; a stream without a digest is rejected.

Virtual streams report fd -1, so the fd-based reuse check in
reused_stream() always fails at getpeername() and pooled virtual
streams can never be reused. The -1 fd also collides in the pool:
ConnectionMeta keys connections by (group, id), and PoolNode stores
them in a HashMap by id, so pooling a second virtual stream under the
same group silently overwrites the first.

Give every virtual stream a unique ID from a range no real fd can
occupy (counting down from -2 on unix; from usize::MAX - 1 on
windows). as_raw_fd() still returns -1 since there is no real fd.

In reused_stream(), a negative ID routes to an address comparison
instead of getpeername(): the peer address recorded in the stream's
socket digest must equal the peer's address. Streams without a
recorded peer address are never reused, so existing virtual streams
keep today's behavior unless their creator opts in by setting
SocketDigest::peer_addr.

Fixes cloudflare#883
h2 0.3.27 comes in through the same legacy aws chain as the existing
rustls-webpki ignores: dial9-tokio-telemetry -> aws-sdk-s3-transfer-manager
-> aws-config -> aws-smithy-http-client, which still uses hyper 0.14. The
advisory's only fix is h2 >= 0.4.16 and no 0.3.x patch exists, so this
cannot be resolved from this workspace's manifests. The vulnerable code
needs a malicious HTTP/2 peer; in this chain h2 is only a TLS client to
AWS endpoints.

Every CI run has failed the cargo audit step since the advisory was
published on 2026-08-17.
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.

Reusable virtual L4 streams

1 participant