From d18e5dfbb0cdc28725bebb28cde80a6c11ee32bc Mon Sep 17 00:00:00 2001 From: ADD-SP Date: Mon, 23 Feb 2026 20:49:37 -0800 Subject: [PATCH] io: fix race in `Mock::poll_write` (#7882) --- tokio-test/src/io.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tokio-test/src/io.rs b/tokio-test/src/io.rs index c33170927..4873f46e5 100644 --- a/tokio-test/src/io.rs +++ b/tokio-test/src/io.rs @@ -438,7 +438,30 @@ impl AsyncWrite for Mock { let until = Instant::now() + rem; self.inner.sleep = Some(Box::pin(time::sleep_until(until))); } else { - panic!("unexpected WouldBlock {}", self.pmsg()); + // A race condition (TOCTOU) can occur if the + // timer expires between the `write()` call + // and `remaining_wait()` due to preemption or other + // delays. In this case, the `Wait` action is already popped by + // `action()`, so we continue to the next one. + // + // Consider the following sequence: + // + // poll_write Inner action() + // |--write()--->| | + // | |--action()---->| (returns Wait) + // |<-WouldBlk---| | + // | | | + // | <--- TIMEOUT! ---> | + // | (due to preemption, etc.) | + // | | | + // |-rem_wait()->| | + // | |--action()---->| (time's up, pop Wait) + // |<--None------| | + // | | | + // |---continue->| (process next action) + // + // See . + continue; } } Ok(0) => {