Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of #102589 - RalfJung:scoped-threads-dangling, r=m-ou-se
scoped threads: pass closure through MaybeUninit to avoid invalid dangling references The `main` function defined here looks roughly like this, if it were written as a more explicit stand-alone function: ```rust // Not showing all the `'lifetime` tracking, the point is that // this closure might live shorter than `thread`. fn thread(control: ..., closure: impl FnOnce() + 'lifetime) { closure(); control.signal_done(); // A lot of time can pass here. } ``` Note that `thread` continues to run even after `signal_done`! Now consider what happens if the `closure` captures a reference of lifetime `'lifetime`: - The type of `closure` is a struct (the implicit unnameable closure type) with a `&'lifetime mut T` field. References passed to a function are marked with `dereferenceable`, which is LLVM speak for *this reference will remain live for the entire duration of this function*. - The closure runs, `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. - Now we have UB! The reference that as passed to `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 without `unsafe`, or maybe a new `MaybeDangling` 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`
- Loading branch information