-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Fix soundness issue in scoped threads. #94644
Conversation
r? @kennytm (rust-highfive has picked a reviewer for you, use r? to override) |
library/std/src/thread/mod.rs
Outdated
// This is only relevant for threads that aren't join()ed, as | ||
// join() will take the `result` and set it to None, such that | ||
// there is nothing left to drop here. | ||
// If this drop panics, that just results in an abort, because |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading just this comment in isolation, I feel like it is dangerous to assume that all unwinding implementations would abort in absence of landing pads on the stack. I suspect there won't be any documented mandate for this sort of behavior anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, but then that's a larger problem that Rust's regular threads (through std::thread::spawn
) also have (and had, since like forever).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's easy to fix it for both cases by adding a catch_unwind and abort here though. I'll do that in a second commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a second commit that fixes that issue for both scoped and regular threads. It now explicitly calls rtabort!()
when dropping the thread result panics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(cc rust-lang/lang-team#97, which would make this a non-issue.)
I wonder if there's any way to write a regression test here. I think comparing drop order between data captured by the scope and the data that refers to this captured data shouldn't he too difficult. Something like this perhaps? struct MustDropFirst;
struct MustDropSecond<'scope>(&'scope MustDropFirst);
impl Drop for MustDropFirst { /* checks drop order between the two types */ }
impl Drop for MustDropSecond { /* checks drop order between the two types */ }
fn main() {
let data = MustDropFirst;
let r = &data;
scope(|s| {
s.spawn(move || {
MustDropSecond(r);
});
});
} The implementation LGTM either way, though. |
It's a bit tricky, because the bug doesn't mean it gets ordered in the opposite direction, but that there's no guarantee how it gets ordered. In practice, without sleeping or a significant amount of work, the thread most likely finishes dropping the result before the main thread wakes up, making the bug invisible. It's somewhat easy to test this with sleeping, but I'm not sure it's a great idea to have a unit test that sleeps. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
18f5719
to
b97d875
Compare
@nagisa I've added a test. (And verified it fails without the changes in this PR.) |
@bors r+ |
📌 Commit b97d875 has been approved by |
⌛ Testing commit b97d875 with merge a301059c353dee85b3a3e52923c8094a9d7a02d1... |
💔 Test failed - checks-actions |
…ss, r=joshtriplett Fix soundness issue in scoped threads. This was discovered in rust-lang#94559 (comment) The `scope()` function returns when all threads are finished, but I accidentally considered a thread 'finished' before dropping their panic payload or ignored return value. So if a thread returned (or panics with) something that in its `Drop` implementation still uses borrowed stuff, it goes wrong. https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=2a1f19ac4676cdabe43e24e536ff9358
The job Click to see the possible cause of the failure (guessed by this bot)
|
@bors retry |
…ss, r=joshtriplett Fix soundness issue in scoped threads. This was discovered in rust-lang#94559 (comment) The `scope()` function returns when all threads are finished, but I accidentally considered a thread 'finished' before dropping their panic payload or ignored return value. So if a thread returned (or panics with) something that in its `Drop` implementation still uses borrowed stuff, it goes wrong. https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=2a1f19ac4676cdabe43e24e536ff9358
…ss, r=joshtriplett Fix soundness issue in scoped threads. This was discovered in rust-lang#94559 (comment) The `scope()` function returns when all threads are finished, but I accidentally considered a thread 'finished' before dropping their panic payload or ignored return value. So if a thread returned (or panics with) something that in its `Drop` implementation still uses borrowed stuff, it goes wrong. https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=2a1f19ac4676cdabe43e24e536ff9358
…askrgr Rollup of 8 pull requests Successful merges: - rust-lang#94440 (Better error for normalization errors from parent crates that use `#![feature(generic_const_exprs)]`) - rust-lang#94587 (Document new recommended use of `FromIterator::from_iter`) - rust-lang#94644 (Fix soundness issue in scoped threads.) - rust-lang#94740 (Unify impl blocks by wrapping them into a div) - rust-lang#94753 (Improve rustdoc book) - rust-lang#94796 (Allow `cargo run` instead of `cargo run -p bootstrap`) - rust-lang#94805 (Revert accidental stabilization) - rust-lang#94809 (RustWrapper: add missing include) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This was discovered in #94559 (comment)
The
scope()
function returns when all threads are finished, but I accidentally considered a thread 'finished' before dropping their panic payload or ignored return value.So if a thread returned (or panics with) something that in its
Drop
implementation still uses borrowed stuff, it goes wrong.https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=2a1f19ac4676cdabe43e24e536ff9358