-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
scoped threads: pass closure through MaybeUninit to avoid invalid dangling references #102589
Conversation
Hey! It looks like you've submitted a new PR for the library teams! If this PR contains changes to any Examples of
|
library/std/src/thread/mod.rs
Outdated
@@ -499,13 +499,18 @@ impl Builder { | |||
let output_capture = crate::io::set_output_capture(None); | |||
crate::io::set_output_capture(output_capture.clone()); | |||
|
|||
// Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. | |||
// See <https://github.com/rust-lang/rust/issues/101983> for more details. | |||
let f = mem::MaybeUninit::new(f); |
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.
Hm, this has the side effect that if the closure is dropped before being run, we leak f
. That is probably not what we want... bit right now that is hard to avoid. We'd need a small wrapper type around MaybeUninit
that drops its contents.
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.
Does the stdlib use scopeguard
? Writing such ad-hoc drop glues becomes the breeze it should have always been:
let mb_dangling_f = ::scopeguard::guard(MaybeUninit::new(f), /* drop: */ |f| unsafe {
drop(MaybeUninit::assume_init(f));
});
let closure = move/*(mb_dangling_f, ..)*/ |…| {
let f = ScopeGuard::into_inner(mb_dangling_f);
let f = unsafe {
MaybeUninit::assume_init(f)
};
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.
No I don't think it does.
d3db1d4
to
a0204bb
Compare
a0204bb
to
78b577c
Compare
@bors r+ |
Rollup of 7 pull requests Successful merges: - rust-lang#102258 (Remove unused variable in float formatting.) - rust-lang#102277 (Consistently write `RwLock`) - rust-lang#102412 (Never panic in `thread::park` and `thread::park_timeout`) - rust-lang#102589 (scoped threads: pass closure through MaybeUninit to avoid invalid dangling references) - rust-lang#102625 (fix backtrace small typo) - rust-lang#102859 (Move lifetime resolution module to rustc_hir_analysis.) - rust-lang#102898 (rustdoc: remove unneeded `<div>` wrapper from sidebar DOM) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
Just looking at this cause it was in twir and I'm confused - doesn't |
The bug is described in a bit more detail at #101983, does that help? |
The
main
function defined here looks roughly like this, if it were written as a more explicit stand-alone function:Note that
thread
continues to run even aftersignal_done
! Now consider what happens if theclosure
captures a reference of lifetime'lifetime
:closure
is a struct (the implicit unnameable closure type) with a&'lifetime mut T
field. References passed to a function are marked withdereferenceable
, which is LLVM speak for this reference will remain live for the entire duration of this function.signal_done
runs. Then -- potentially -- this thread gets scheduled away and the main thread runs, seeing the signal and returning to the user. Now'lifetime
ends and the memory the reference points to might be deallocated.thread
with the promise of remaining live for the entire duration of the function, actually got deallocated while the function still runs. Oops.Long-term I think we should be able to use
ManuallyDrop
to fix this withoutunsafe
, or maybe a newMaybeDangling
type. I am working on an RFC for that. But in the mean time it'd be nice to fix this so that Miri with-Zmiri-retag-fields
(which is needed for "full enforcement" of all the LLVM flags we generate) stops erroring on scoped threads.Fixes #101983
r? @m-ou-se