jedisct1 opened PR #13061 from dip-proto:async-readstream-eof-loop to bytecodealliance:main:
SUMMARY
AsyncReadStream::newkeeps its background read task alive after the wrapped reader reaches EOF, so closed streams continue looping instead of terminating the worker.PROVENANCE
This exploration and report were automatically generated by the Swival Security Scanner (https://swival.dev).PRECONDITIONS
- An
AsyncReadStreamis created around anAsyncReadthat can reach EOF; this is the intended use ofAsyncReadStreamand is exercised bytokio::io::empty()and finite readers incrates/wasi/src/p2/pipe.rs.- The stream remains alive after EOF, so the worker task is not cancelled by dropping the handle.
PROOF
AsyncReadStream::newspawns a background task that loops forever aroundreader.read_buf(&mut buf).awaitincrates/wasi/src/p2/pipe.rs:162.- When the wrapped reader reaches EOF, the
Ok(nbytes) if nbytes == 0arm sendsErr(StreamError::Closed)through the channel incrates/wasi/src/p2/pipe.rs:168.- After that send succeeds, execution falls through to the loop tail. The only terminating condition is
if sent.is_err() { break; }incrates/wasi/src/p2/pipe.rs:176, which does not fire when the receiver is still alive.- The next iteration reads EOF again and repeats the same send, so the task never terminates on its own.
- This is reachable today because the test helpers instantiate
AsyncReadStreamwith EOF-producing readers such astokio::io::empty()and finite pipe readers incrates/wasi/src/p2/pipe.rs:380andcrates/wasi/src/p2/pipe.rs:435.WHY THIS IS A REAL BUG
The worker is documented and structured as the asynchronous bridge for a single stream. Once EOF is observed, the stream is permanently closed, so continuing to poll the underlying reader and resignal closure is incorrect behavior, not a stylistic choice.PATCH RATIONALE
The patch makes EOF a terminal condition by breaking the loop after a successful closed notification, while preserving existing behavior for normal data delivery, receiver shutdown, and I/O errors. It is local to the loop that mishandles EOF and does not change unrelated stream semantics.RESIDUAL RISK
None
jedisct1 requested wasmtime-wasi-reviewers for a review on PR #13061.
github-actions[bot] added the label wasi on PR #13061.
jedisct1 closed without merge PR #13061.
Last updated: Apr 13 2026 at 00:25 UTC