Skip to content

sync: fix underflow in mpsc channel len()#8062

Open
Darksonn wants to merge 2 commits intotokio-rs:masterfrom
Darksonn:mpsc-len-underflow
Open

sync: fix underflow in mpsc channel len()#8062
Darksonn wants to merge 2 commits intotokio-rs:masterfrom
Darksonn:mpsc-len-underflow

Conversation

@Darksonn
Copy link
Copy Markdown
Member

Fixes: #8061

@Darksonn Darksonn added A-tokio Area: The main tokio crate M-sync Module: tokio/sync labels Apr 17, 2026
@Darksonn Darksonn changed the title sync: fix underflow in mpsc channel sync: fix underflow in mpsc channel len() Apr 17, 2026
@github-actions github-actions bot added the R-loom-sync Run loom sync tests on this PR label Apr 17, 2026
@Darksonn Darksonn force-pushed the mpsc-len-underflow branch from e0ee027 to a0a47ea Compare April 17, 2026 09:56
// but it will be closed
let tail_position = tx.tail_position.load(Acquire);
tail_position - self.index - (tx.is_closed() as usize)
let mut len = tail_position.wrapping_sub(self.index);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I realized that we should also be using wrapping arithmetic for the position indices. On 32-bit machines these indices can wrap around after a few billion messages.

}

fn has_value(&self, slot_index: usize) -> bool {
let block = unsafe { self.find_block(slot_index).as_ref() };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we add a safety comment here?

#[test]
fn len_accurate_during_close() {
loom::model(|| {
let (tx, rx) = mpsc::channel::<()>(2);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test would be more interesting if it sends BLOCK_CAP messages before closing. This will test the edge case where block_tail advances to a new block during close().

// Note that it is also possible for the ready bit to be unset on a normal message, but
// this happens only if that message is currently being sent *right now* in parallel on
// another thread. That is okay because it is optional to count messages that are currently
// being sent.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it possible another thread to advance the block_tail between line 271 (tail_position.load()) and line 285 (tx.has_value()) ?
If this happens tx.has_value() may enter an infinite loop calling block.grow() in find_block().

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The loop in find_block can only run finitely many times because each iteration advances to the next block, and it can only move to the next block offset/32 times.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-tokio Area: The main tokio crate M-sync Module: tokio/sync R-loom-sync Run loom sync tests on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic in subtraction with overflow in tokio/src/sync/mpsc/list.rs

3 participants