-
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
Scoped threads violate 'dereferenceable for function call' requirement of references #101983
Comments
I believe #98517 included a fix for this. |
Doesn't look like it -- the user-provided closure seems to be handled exactly the same as currently? |
Ah, I read too quickly and misunderstood, apologies. It solves a different potential unsoundness (one more similar to the Arc drop issue). |
Yeah, it looks like an alternative fix for the |
While the RFC for a proper fix is cooking, I am proposing a preliminary fix in #102589. |
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 rust-lang/rust#101983 r? `@m-ou-se`
Scopes threads as currently implemented are potentially unsound due to passing a reference to a function where the memory the reference points to is potentially deallocated while the function still runs.
Specifically, this main function here takes as implicit argument its closure environment, which contains
f
. That environment might just be a reference to some memory that is allocated in the caller stack frame. However themain
function can keep running aftertheir_packet
got dropped (implicitly at the end ofmain
). Then the scope might end and the memory might be deallocated all whilemain
is still running. If the environment is just a reference, it ends up being a newtype and we will (AFAIK) add thedereferenceable
attribute, meaning deallocation while the function runs is actually LLVM UB.Miri found the error in this doc test, where the
a.push(4)
invalidates the&a
reference that was passed to the forked-off threads. (a
doesn't actually get deallocated, it just has a unique reference created to it, but deallocation is also a possibility).To fix this properly, I think we need a language extension: we want a way to store references in a type without having alias guarantees for that reference. In terms of Stacked Borrows this means Stacked Borrows will stop recursing at that type during retags. In terms of the LLVM IR we generate it means we must not add
noalias
ordereferenceable
attributes for references inside that type. We don't have such a type currently but I think it makes sense to makeManuallyDrop
that type (or even if we add a new type for this,ManuallyDrop
should use it). Also see this Zulip discussion.The text was updated successfully, but these errors were encountered: