Skip to content

Fix race between block initialization and receiver disconnection #1084

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 1 commit into from
Feb 26, 2024

Conversation

ibraheemdev
Copy link
Contributor

@ibraheemdev ibraheemdev commented Feb 25, 2024

Reported in rust-lang/rust#121582. This fixes a race that can occur if a sender installs the first block right after all receivers have disconnected and checked for any existing messages, causing a memory leak.

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!

@taiki-e taiki-e merged commit c9af259 into crossbeam-rs:master Feb 26, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 26, 2024
Fix race between block initialization and receiver disconnection

Port of crossbeam-rs/crossbeam#1084. Closes rust-lang#121582.
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]>
This was referenced Apr 11, 2025
facebook-github-bot pushed a commit to facebookincubator/reindeer that referenced this pull request Apr 12, 2025
… Drop Package: crossbeam-channel 0.5.14

Summary:
VULNERABILITY RUSTSEC-2025-0024 - 2025-04-08: crossbeam-channel: double free on Drop
Package: crossbeam-channel 0.5.14

The internal `Channel` type's `Drop` method has a race
which could, in some circumstances, lead to a double-free.
This could result in memory corruption.

Quoting from the
[upstream description in merge request \#1187](crossbeam-rs/crossbeam#1187 (comment)):

> 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 was introduced while fixing a memory leak, in
upstream [MR \#1084](crossbeam-rs/crossbeam#1084),
first published in 0.5.12.

The fix is in
upstream [MR \#1187](crossbeam-rs/crossbeam#1187)
and has been published in 0.5.15

Reviewed By: dtolnay

Differential Revision: D72915462

fbshipit-source-id: c53113f91eedf478646588a84d0fa3aa85a6d2a0
facebook-github-bot pushed a commit to facebook/pyrefly that referenced this pull request Apr 12, 2025
… Drop Package: crossbeam-channel 0.5.14

Summary:
VULNERABILITY RUSTSEC-2025-0024 - 2025-04-08: crossbeam-channel: double free on Drop
Package: crossbeam-channel 0.5.14

The internal `Channel` type's `Drop` method has a race
which could, in some circumstances, lead to a double-free.
This could result in memory corruption.

Quoting from the
[upstream description in merge request \#1187](crossbeam-rs/crossbeam#1187 (comment)):

> 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 was introduced while fixing a memory leak, in
upstream [MR \#1084](crossbeam-rs/crossbeam#1084),
first published in 0.5.12.

The fix is in
upstream [MR \#1187](crossbeam-rs/crossbeam#1187)
and has been published in 0.5.15

Reviewed By: dtolnay

Differential Revision: D72915462

fbshipit-source-id: c53113f91eedf478646588a84d0fa3aa85a6d2a0
facebook-github-bot pushed a commit to facebook/hhvm that referenced this pull request Apr 12, 2025
… Drop Package: crossbeam-channel 0.5.14

Summary:
VULNERABILITY RUSTSEC-2025-0024 - 2025-04-08: crossbeam-channel: double free on Drop
Package: crossbeam-channel 0.5.14

The internal `Channel` type's `Drop` method has a race
which could, in some circumstances, lead to a double-free.
This could result in memory corruption.

Quoting from the
[upstream description in merge request \#1187](crossbeam-rs/crossbeam#1187 (comment)):

> 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 was introduced while fixing a memory leak, in
upstream [MR \#1084](crossbeam-rs/crossbeam#1084),
first published in 0.5.12.

The fix is in
upstream [MR \#1187](crossbeam-rs/crossbeam#1187)
and has been published in 0.5.15

Reviewed By: dtolnay

Differential Revision: D72915462

fbshipit-source-id: c53113f91eedf478646588a84d0fa3aa85a6d2a0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

2 participants