Skip to content

Commit 5b7499b

Browse files
bors[bot]petrosagg
andauthored
Merge #972
972: channel: unbounded: synchronize receiver disconnect with initialization r=taiki-e a=petrosagg Receiver disconnection relies on the incorrect assumption that `head.index != tail.index` implies that the channel is initialized (i.e `head.block` and `tail.block` point to allocated blocks). However, it can happen that `head.index != tail.index` and `head.block == null` at the same time which leads to a segfault when a channel is dropped in that state. This can happen because initialization is performed in two steps. First, the tail block is allocated and the `tail.block` is set. If that is successful `head.block` is set to the same pointer. Importantly, initialization is skipped if `tail.block` is not null. Therefore we can have the following situation: 1. Thread A starts to send the first value of the channel, observes that `tail.block` is null and begins initialization. It sets `tail.block` to point to a newly allocated block and then gets preempted. `head.block` is still null at this point. 2. Thread B starts to send the second value of the channel, observes that `tail.block` *is not* null and proceeds with writing its value in the allocated tail block and sets `tail.index` to 1. 3. Thread B drops the receiver of the channel which observes that `head.index != tail.index` (0 and 1 respectively), therefore there must be messages to drop. It starts traversing the linked list from `head.block` which is still a null pointer, leading to a segfault. The first commit of this PR includes a reproduction of this scenario. This PR fixes this problem by waiting for initialization to complete when `head.index != tail.index` and the `head.block` is still null. A similar check exists in `start_recv` for similar reasons. Fixes #971 Co-authored-by: Petros Angelatos <[email protected]>
2 parents 4cc0ab5 + 2d22628 commit 5b7499b

File tree

1 file changed

+11
-0
lines changed
  • crossbeam-channel/src/flavors

1 file changed

+11
-0
lines changed

crossbeam-channel/src/flavors/list.rs

+11
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,17 @@ impl<T> Channel<T> {
584584
let mut head = self.head.index.load(Ordering::Acquire);
585585
let mut block = self.head.block.load(Ordering::Acquire);
586586

587+
// If we're going to be dropping messages we need to synchronize with initialization
588+
if head >> SHIFT != tail >> SHIFT {
589+
// The block can be null here only if a sender is in the process of initializing the
590+
// channel while another sender managed to send a message by inserting it into the
591+
// semi-initialized channel and advanced the tail.
592+
// In that case, just wait until it gets initialized.
593+
while block.is_null() {
594+
backoff.snooze();
595+
block = self.head.block.load(Ordering::Acquire);
596+
}
597+
}
587598
unsafe {
588599
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
589600
while head >> SHIFT != tail >> SHIFT {

0 commit comments

Comments
 (0)