Skip to content

channel: unbounded: synchronize receiver disconnect with initialization #972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 9, 2023

Conversation

petrosagg
Copy link
Contributor

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

We must take into account the case where the channel has messages in the
block pointed to by `tail` but the head is still pointing to a null
pointer. This can happen with two concurrent senders if one gets
preempted during initialization.

Signed-off-by: Petros Angelatos <[email protected]>
Copy link
Member

@taiki-e taiki-e left a comment

Choose a reason for hiding this comment

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

Thanks!

bors r+

@bors
Copy link
Contributor

bors bot commented Apr 9, 2023

@bors bors bot merged commit 5b7499b into crossbeam-rs:master Apr 9, 2023
bors bot added a commit that referenced this pull request Apr 9, 2023
973: v0.8: Prepare for the next release r=taiki-e a=taiki-e

- crossbeam-channel 0.5.7 -> 0.5.8
  - Fix race condition in unbounded channel. (#972)

Also, yanking `>= 0.5.1, <= 0.5.7` that affected by the bug fixed in this release.

Co-authored-by: Petros Angelatos <[email protected]>
Co-authored-by: Taiki Endo <[email protected]>
petrosagg added a commit to petrosagg/materialize that referenced this pull request Apr 10, 2023
Update to `v0.5.8` which includes
crossbeam-rs/crossbeam#972

Signed-off-by: Petros Angelatos <[email protected]>
petrosagg added a commit to petrosagg/materialize that referenced this pull request Apr 10, 2023
Update to `v0.5.8` which includes
crossbeam-rs/crossbeam#972

Signed-off-by: Petros Angelatos <[email protected]>
petrosagg added a commit to petrosagg/materialize that referenced this pull request Apr 10, 2023
Update to `v0.5.8` which includes
crossbeam-rs/crossbeam#972

Signed-off-by: Petros Angelatos <[email protected]>
sploiselle pushed a commit to sploiselle/materialize that referenced this pull request Apr 14, 2023
Update to `v0.5.8` which includes
crossbeam-rs/crossbeam#972

Signed-off-by: Petros Angelatos <[email protected]>
klensy added a commit to klensy/rust that referenced this pull request May 26, 2023
update iana-time-zone-haiku to drop bumch of cxx* deps
cargo update -p iana-time-zone-haiku
    Updating crates.io index
    Updating cc v1.0.77 -> v1.0.79
    Removing codespan-reporting v0.11.1
    Removing cxx v1.0.94
    Removing cxx-build v1.0.94
    Removing cxxbridge-flags v1.0.94
    Removing cxxbridge-macro v1.0.94
    Updating iana-time-zone-haiku v0.1.1 -> v0.1.2
    Removing link-cplusplus v1.0.8
    Removing scratch v1.0.5

fixes known issue crossbeam-rs/crossbeam#972
cargo update -p crossbeam-channel
    Updating crates.io index
    Updating crossbeam-channel v0.5.6 -> v0.5.8

dedupes memoffset versions
cargo update -p crossbeam-epoch
    Updating crates.io index
    Updating crossbeam-epoch v0.9.13 -> v0.9.14
    Removing memoffset v0.7.1

dedupes bstr versions
cargo update -p ignore -p opener
    Updating crates.io index
    Removing bstr v0.2.17
    Updating globset v0.4.9 -> v0.4.10
    Updating ignore v0.4.18 -> v0.4.20
    Updating opener v0.5.0 -> v0.5.2
bors added a commit to rust-lang-ci/rust that referenced this pull request May 30, 2023
…lacrum

deps: bump crates

Updates few deps:

drops a lot of cxx* crates:
```console
$ cargo update -p iana-time-zone-haiku
    Updating crates.io index
    Updating cc v1.0.77 -> v1.0.79
    Removing codespan-reporting v0.11.1
    Removing cxx v1.0.94
    Removing cxx-build v1.0.94
    Removing cxxbridge-flags v1.0.94
    Removing cxxbridge-macro v1.0.94
    Updating iana-time-zone-haiku v0.1.1 -> v0.1.2
    Removing link-cplusplus v1.0.8
    Removing scratch v1.0.5
```
cc: https://github.com/rust-lang/cc-rs/releases/tag/1.0.78, https://github.com/rust-lang/cc-rs/releases/tag/1.0.79
iana-time-zone-haiku: https://github.com/strawlab/iana-time-zone/releases/tag/haiku%2Fv0.1.2

fixed crossbeam-rs/crossbeam#972 (similar fixed in rust repo rust-lang#110089)
```console
$ cargo update -p crossbeam-channel
    Updating crates.io index
    Updating crossbeam-channel v0.5.6 -> v0.5.8
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-channel/CHANGELOG.md#version-058

dedupes memoffset versions:
```console
$ cargo update -p crossbeam-epoch
    Updating crates.io index
    Updating crossbeam-epoch v0.9.13 -> v0.9.14
    Removing memoffset v0.7.1
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-epoch/CHANGELOG.md#version-0914
Gilnaa/memoffset@v0.6.5...v0.8.0
rust-lang#108638 (comment)

dedupes bstr versions
```console
$ cargo update -p ignore -p opener
    Updating crates.io index
    Removing bstr v0.2.17
    Updating globset v0.4.9 -> v0.4.10
    Updating ignore v0.4.18 -> v0.4.20
    Updating opener v0.5.0 -> v0.5.2
```

globset BurntSushi/ripgrep@ac8fecb
ignore https://github.com/BurntSushi/ripgrep/commits/master/crates/ignore hard to track, but drop dep on crossbeam-utils (BurntSushi/ripgrep@e95254a), don't stat git if require_git is false (BurntSushi/ripgrep@009dda1) and added bunch of formats to ignore list
opener Seeker14491/opener@v0.5.0...v0.5.2 nothing interesting
RalfJung pushed a commit to RalfJung/miri that referenced this pull request May 31, 2023
deps: bump crates

Updates few deps:

drops a lot of cxx* crates:
```console
$ cargo update -p iana-time-zone-haiku
    Updating crates.io index
    Updating cc v1.0.77 -> v1.0.79
    Removing codespan-reporting v0.11.1
    Removing cxx v1.0.94
    Removing cxx-build v1.0.94
    Removing cxxbridge-flags v1.0.94
    Removing cxxbridge-macro v1.0.94
    Updating iana-time-zone-haiku v0.1.1 -> v0.1.2
    Removing link-cplusplus v1.0.8
    Removing scratch v1.0.5
```
cc: https://github.com/rust-lang/cc-rs/releases/tag/1.0.78, https://github.com/rust-lang/cc-rs/releases/tag/1.0.79
iana-time-zone-haiku: https://github.com/strawlab/iana-time-zone/releases/tag/haiku%2Fv0.1.2

fixed crossbeam-rs/crossbeam#972 (similar fixed in rust repo rust-lang/rust#110089)
```console
$ cargo update -p crossbeam-channel
    Updating crates.io index
    Updating crossbeam-channel v0.5.6 -> v0.5.8
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-channel/CHANGELOG.md#version-058

dedupes memoffset versions:
```console
$ cargo update -p crossbeam-epoch
    Updating crates.io index
    Updating crossbeam-epoch v0.9.13 -> v0.9.14
    Removing memoffset v0.7.1
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-epoch/CHANGELOG.md#version-0914
Gilnaa/memoffset@v0.6.5...v0.8.0
rust-lang/rust#108638 (comment)

dedupes bstr versions
```console
$ cargo update -p ignore -p opener
    Updating crates.io index
    Removing bstr v0.2.17
    Updating globset v0.4.9 -> v0.4.10
    Updating ignore v0.4.18 -> v0.4.20
    Updating opener v0.5.0 -> v0.5.2
```

globset BurntSushi/ripgrep@ac8fecb
ignore https://github.com/BurntSushi/ripgrep/commits/master/crates/ignore hard to track, but drop dep on crossbeam-utils (BurntSushi/ripgrep@e95254a), don't stat git if require_git is false (BurntSushi/ripgrep@009dda1) and added bunch of formats to ignore list
opener Seeker14491/opener@v0.5.0...v0.5.2 nothing interesting
jakmeier added a commit to jakmeier/nearcore that referenced this pull request Sep 26, 2023
github-merge-queue bot pushed a commit to near/nearcore that referenced this pull request Sep 26, 2023
github-merge-queue bot pushed a commit to near/nearcore that referenced this pull request Sep 26, 2023
github-merge-queue bot pushed a commit to near/nearcore that referenced this pull request Sep 26, 2023
github-merge-queue bot pushed a commit to near/nearcore that referenced this pull request Sep 26, 2023
nikurt pushed a commit to near/nearcore that referenced this pull request Oct 2, 2023
nikurt pushed a commit to near/nearcore that referenced this pull request Oct 2, 2023
nikurt pushed a commit to near/nearcore that referenced this pull request Oct 11, 2023
RalfJung pushed a commit to RalfJung/rust-analyzer that referenced this pull request Apr 20, 2024
deps: bump crates

Updates few deps:

drops a lot of cxx* crates:
```console
$ cargo update -p iana-time-zone-haiku
    Updating crates.io index
    Updating cc v1.0.77 -> v1.0.79
    Removing codespan-reporting v0.11.1
    Removing cxx v1.0.94
    Removing cxx-build v1.0.94
    Removing cxxbridge-flags v1.0.94
    Removing cxxbridge-macro v1.0.94
    Updating iana-time-zone-haiku v0.1.1 -> v0.1.2
    Removing link-cplusplus v1.0.8
    Removing scratch v1.0.5
```
cc: https://github.com/rust-lang/cc-rs/releases/tag/1.0.78, https://github.com/rust-lang/cc-rs/releases/tag/1.0.79
iana-time-zone-haiku: https://github.com/strawlab/iana-time-zone/releases/tag/haiku%2Fv0.1.2

fixed crossbeam-rs/crossbeam#972 (similar fixed in rust repo rust-lang/rust#110089)
```console
$ cargo update -p crossbeam-channel
    Updating crates.io index
    Updating crossbeam-channel v0.5.6 -> v0.5.8
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-channel/CHANGELOG.md#version-058

dedupes memoffset versions:
```console
$ cargo update -p crossbeam-epoch
    Updating crates.io index
    Updating crossbeam-epoch v0.9.13 -> v0.9.14
    Removing memoffset v0.7.1
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-epoch/CHANGELOG.md#version-0914
Gilnaa/memoffset@v0.6.5...v0.8.0
rust-lang/rust#108638 (comment)

dedupes bstr versions
```console
$ cargo update -p ignore -p opener
    Updating crates.io index
    Removing bstr v0.2.17
    Updating globset v0.4.9 -> v0.4.10
    Updating ignore v0.4.18 -> v0.4.20
    Updating opener v0.5.0 -> v0.5.2
```

globset BurntSushi/ripgrep@ac8fecb
ignore https://github.com/BurntSushi/ripgrep/commits/master/crates/ignore hard to track, but drop dep on crossbeam-utils (BurntSushi/ripgrep@e95254a), don't stat git if require_git is false (BurntSushi/ripgrep@009dda1) and added bunch of formats to ignore list
opener Seeker14491/opener@v0.5.0...v0.5.2 nothing interesting
RalfJung pushed a commit to RalfJung/rust-analyzer that referenced this pull request Apr 27, 2024
deps: bump crates

Updates few deps:

drops a lot of cxx* crates:
```console
$ cargo update -p iana-time-zone-haiku
    Updating crates.io index
    Updating cc v1.0.77 -> v1.0.79
    Removing codespan-reporting v0.11.1
    Removing cxx v1.0.94
    Removing cxx-build v1.0.94
    Removing cxxbridge-flags v1.0.94
    Removing cxxbridge-macro v1.0.94
    Updating iana-time-zone-haiku v0.1.1 -> v0.1.2
    Removing link-cplusplus v1.0.8
    Removing scratch v1.0.5
```
cc: https://github.com/rust-lang/cc-rs/releases/tag/1.0.78, https://github.com/rust-lang/cc-rs/releases/tag/1.0.79
iana-time-zone-haiku: https://github.com/strawlab/iana-time-zone/releases/tag/haiku%2Fv0.1.2

fixed crossbeam-rs/crossbeam#972 (similar fixed in rust repo rust-lang/rust#110089)
```console
$ cargo update -p crossbeam-channel
    Updating crates.io index
    Updating crossbeam-channel v0.5.6 -> v0.5.8
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-channel/CHANGELOG.md#version-058

dedupes memoffset versions:
```console
$ cargo update -p crossbeam-epoch
    Updating crates.io index
    Updating crossbeam-epoch v0.9.13 -> v0.9.14
    Removing memoffset v0.7.1
```
https://github.com/crossbeam-rs/crossbeam/blob/master/crossbeam-epoch/CHANGELOG.md#version-0914
Gilnaa/memoffset@v0.6.5...v0.8.0
rust-lang/rust#108638 (comment)

dedupes bstr versions
```console
$ cargo update -p ignore -p opener
    Updating crates.io index
    Removing bstr v0.2.17
    Updating globset v0.4.9 -> v0.4.10
    Updating ignore v0.4.18 -> v0.4.20
    Updating opener v0.5.0 -> v0.5.2
```

globset BurntSushi/ripgrep@ac8fecb
ignore https://github.com/BurntSushi/ripgrep/commits/master/crates/ignore hard to track, but drop dep on crossbeam-utils (BurntSushi/ripgrep@e95254a), don't stat git if require_git is false (BurntSushi/ripgrep@009dda1) and added bunch of formats to ignore list
opener Seeker14491/opener@v0.5.0...v0.5.2 nothing interesting
petrosagg added a commit to petrosagg/crossbeam that referenced this pull request Apr 8, 2025
This PR is fixing a regression introduced by crossbeam-rs#1084 that can lead to a
double free when dropping the channel.

The method `Channel::discard_all_messages` has the property that if it
observes `head.block` pointing to a non-null pointer it will attempt to
free it.

The same property holds for the `Channel::drop` method and so it is
critical that whenever `head.block` is freed it must also be set to a
null pointer so that it is freed exactly once.

Before crossbeam-rs#1084 the behavior of `discard_all_messages` ensured `head.block`
was `null` after its execution due to the atomic store right before
exiting [1].

After crossbeam-rs#1084 `discard_all_messages` atomically swaps the current value of
`head.block` with a null pointer at the moment the value is read instead
of waiting for the end of the function.

The problem lies in the fact that `dicard_all_messages` contained two
paths that could lead to `head.block` being read but only one of them
would swap the value. This meant that `dicard_all_messages` could end up
observing a non-null block pointer (and therefore attempting to free it)
without setting `head.block` to null. This would then lead to
`Channel::drop` making a second attempt at dropping the same pointer.

The bug is similar to the one previously fixed by crossbeam-rs#972 and the double
free can be reproduced by reverting the reproduction commit from that PR
[2].

As with crossbeam-rs#972 it is quite difficult to trigger this bug without
introducing artificial sleeps in critical points so this PR does not
include a test.

[1] https://github.com/crossbeam-rs/crossbeam/blob/crossbeam-channel-0.5.11/crossbeam-channel/src/flavors/list.rs#L625
[2] crossbeam-rs@2d22628

Signed-off-by: Petros Angelatos <[email protected]>
taiki-e pushed a commit that referenced this pull request Apr 8, 2025
This PR is fixing a regression introduced by #1084 that can lead to a
double free when dropping the channel.

The method `Channel::discard_all_messages` has the property that if it
observes `head.block` pointing to a non-null pointer it will attempt to
free it.

The same property holds for the `Channel::drop` method and so it is
critical that whenever `head.block` is freed it must also be set to a
null pointer so that it is freed exactly once.

Before #1084 the behavior of `discard_all_messages` ensured `head.block`
was `null` after its execution due to the atomic store right before
exiting [1].

After #1084 `discard_all_messages` atomically swaps the current value of
`head.block` with a null pointer at the moment the value is read instead
of waiting for the end of the function.

The problem lies in the fact that `dicard_all_messages` contained two
paths that could lead to `head.block` being read but only one of them
would swap the value. This meant that `dicard_all_messages` could end up
observing a non-null block pointer (and therefore attempting to free it)
without setting `head.block` to null. This would then lead to
`Channel::drop` making a second attempt at dropping the same pointer.

The bug is similar to the one previously fixed by #972 and the double
free can be reproduced by reverting the reproduction commit from that PR
[2].

As with #972 it is quite difficult to trigger this bug without
introducing artificial sleeps in critical points so this PR does not
include a test.

[1] https://github.com/crossbeam-rs/crossbeam/blob/crossbeam-channel-0.5.11/crossbeam-channel/src/flavors/list.rs#L625
[2] 2d22628

Signed-off-by: Petros Angelatos <[email protected]>
taiki-e pushed a commit that referenced this pull request Apr 8, 2025
This PR is fixing a regression introduced by #1084 that can lead to a
double free when dropping the channel.

The method `Channel::discard_all_messages` has the property that if it
observes `head.block` pointing to a non-null pointer it will attempt to
free it.

The same property holds for the `Channel::drop` method and so it is
critical that whenever `head.block` is freed it must also be set to a
null pointer so that it is freed exactly once.

Before #1084 the behavior of `discard_all_messages` ensured `head.block`
was `null` after its execution due to the atomic store right before
exiting [1].

After #1084 `discard_all_messages` atomically swaps the current value of
`head.block` with a null pointer at the moment the value is read instead
of waiting for the end of the function.

The problem lies in the fact that `dicard_all_messages` contained two
paths that could lead to `head.block` being read but only one of them
would swap the value. This meant that `dicard_all_messages` could end up
observing a non-null block pointer (and therefore attempting to free it)
without setting `head.block` to null. This would then lead to
`Channel::drop` making a second attempt at dropping the same pointer.

The bug is similar to the one previously fixed by #972 and the double
free can be reproduced by reverting the reproduction commit from that PR
[2].

As with #972 it is quite difficult to trigger this bug without
introducing artificial sleeps in critical points so this PR does not
include a test.

[1] https://github.com/crossbeam-rs/crossbeam/blob/crossbeam-channel-0.5.11/crossbeam-channel/src/flavors/list.rs#L625
[2] 2d22628

Signed-off-by: Petros Angelatos <[email protected]>
taiki-e pushed a commit that referenced this pull request Apr 8, 2025
This PR is fixing a regression introduced by #1084 that can lead to a
double free when dropping the channel.

The method `Channel::discard_all_messages` has the property that if it
observes `head.block` pointing to a non-null pointer it will attempt to
free it.

The same property holds for the `Channel::drop` method and so it is
critical that whenever `head.block` is freed it must also be set to a
null pointer so that it is freed exactly once.

Before #1084 the behavior of `discard_all_messages` ensured `head.block`
was `null` after its execution due to the atomic store right before
exiting [1].

After #1084 `discard_all_messages` atomically swaps the current value of
`head.block` with a null pointer at the moment the value is read instead
of waiting for the end of the function.

The problem lies in the fact that `dicard_all_messages` contained two
paths that could lead to `head.block` being read but only one of them
would swap the value. This meant that `dicard_all_messages` could end up
observing a non-null block pointer (and therefore attempting to free it)
without setting `head.block` to null. This would then lead to
`Channel::drop` making a second attempt at dropping the same pointer.

The bug is similar to the one previously fixed by #972 and the double
free can be reproduced by reverting the reproduction commit from that PR
[2].

As with #972 it is quite difficult to trigger this bug without
introducing artificial sleeps in critical points so this PR does not
include a test.

[1] https://github.com/crossbeam-rs/crossbeam/blob/crossbeam-channel-0.5.11/crossbeam-channel/src/flavors/list.rs#L625
[2] 2d22628

Signed-off-by: Petros Angelatos <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Segfault in crossbeam_channel::flavors::list
2 participants