Skip to content

Fix race between block initialization and receiver disconnection #1084

New issue

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

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

Already on GitHub? # 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 <petrosagg@gmail.com>
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 <petrosagg@gmail.com>
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 <petrosagg@gmail.com>
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 <petrosagg@gmail.com>
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
# for free to join this conversation on GitHub. Already have an account? # to comment
Development

Successfully merging this pull request may close these issues.

2 participants