-
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
Enforce no-move rule of ReentrantMutex using Pin and fix UB in stdio #77801
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
As I wrote in zulip, I'd rather use I'd change the fn get_or_init_in_place(&'static self, f: impl FnOnce() -> T, g: impl FnOnce(&'static T)) -> &'static T And the only change I would make to Do you think there's an argument for having the extra flexibility of |
I think we should backport this to beta as well (1.48), patch here is not too large and seems like it's definitely in the hot path so should get plenty of testing. beta-nominated. |
Another option for this API, which seems more easy to make public, might be to use fn get_or_init_in_place<'a>(&'a self, f: impl for<'b> FnOnce(&'b mut MaybeUninit<T>)) -> &'a T |
That's exactly what I had in my first version (before pushing) ^^. I can probably find it somewhere in my git reflog if you really want that one. Though, I found it a bit tricky to properly explain the safety requirements that version, so I changed it to the one with Pin instead. This
So indeed, this However, it does restrict any future usage of this ReentrantMutex to static ones. I like the Pin version better, because then you can also And regardless of ReentrantMutex, I'd also really like to apply the Note that a |
Oh and also: In |
But the only way you use this function still requires unsafe because pin does not capture all of the requirements to initialize this mutex. If I were designing a OnceCell to initialize in place, this is exactly how I'd design it, not with pin. I can't imagine every making
My own experience trying to use pin this way (for example in the intrusive doubly linked list I used in shifgrethor) has been that it doesn't really buy a lot because there's not a lot of abstraction going on & I end up needing unsafe access into the pin every time I look at it, so it feels like more bookkeeping that doesn't really help maintain correctness. Maybe this will be a different experience, but its why I'm generally suspicious of applying pin to anything that involves immovable values without really clear benefits. Pin is mainly about limiting the power of |
Sure, but if it captures most of them that's already a big win. Tracking multiple unsafe requirements manually is hard, as evidenced by the bug this PR is fixing.
Every single Rust program that uses
Here's another example that shows Since this is beta-nominated, we shouldn't wait too long with this PR. If you're still opposed to using |
@m-ou-se This has not yet merged into nightly, and 1.48 releases in less than a week. Do you think there's something more targeted perhaps that we could backport? Otherwise I can try to review it in detail and approve it for beta -- I think @withoutboats' concerns are more towards maintenance than correctness, right? It does seem a bit worrying to let this slip into 1.48 (and now likely 1.49 as well, since that has branched). |
A Avoiding A slightly smaller change would be to add a method to
Yes. |
Ok, having reviewed this PR I think it's reasonable enough to backport it to beta directly. |
…ulacrum [beta] next This backports: * Avoid installing external LLVM dylibs rust-lang#78986 * Install CI llvm into the library directory rust-lang#79074 * Revert "Revert "resolve: Avoid "self-confirming" import resolutions in one more case"" rust-lang#78784 * Bump Rustfmt and RLS rust-lang#78775 * Enforce no-move rule of ReentrantMutex using Pin and fix UB in stdio rust-lang#77801 For RLS/rustfmt compilation to succeed: * change the order of type arguments on ControlFlow rust-lang#76614 * Add ControlFlow::is_{break,continue} methods rust-lang#78200 * Replace run_compiler with RunCompiler builder pattern rust-lang#77649 As a dependency of rust-lang#77801: * Add Pin::static_ref, static_mut. rust-lang#77726
Hmm, was this backported but not merged to nightly? |
Going to go ahead and renominate for 1.49 backport (will do that this weekend). Would be great to make some progress here though. |
Okay, I've reviewed the PR implementation in some detail, and I'm going to go ahead and @bors r+ this. I am not sure how I feel about @withoutboats concerns around I am also a little worried by "... which run the risk of becoming unsound by the addition of other APIs" -- it seems like if true, we shouldn't be adding those APIs? But I admit I'm not quite following which APIs, and on what, you refer to :) |
📌 Commit 912c1f78c70284c3f559ec29ea622057745a5fe0 has been approved by |
@bors r- Could you actually squash commits down here a bit too? |
…tex. It was used for marker::Send, but Send is already in scope.
The code in io::stdio before this change misused the ReentrantMutexes, by calling init() on them and moving them afterwards. Now that ReentrantMutex requires Pin for init(), this mistake is no longer easy to make.
…ulacrum [beta] backports * [beta] always disable copy_file_range to avoid EOVERFLOW errors rust-lang#79008 * Enforce no-move rule of ReentrantMutex using Pin and fix UB in stdio rust-lang#77801 * bootstrap: use the same version number for rustc and cargo rust-lang#79133 * [beta] Revert "Enable ASLR for windows-gnu" rust-lang#79141 * [beta] revert rust-lang#78790, vendor libtest for rustc-src rust-lang#79571 * Mirror centos vault to S3 rust-lang#79435 * [beta] Update cargo rust-lang#79739 This also bumps to non-dev stable compiler. r? `@ghost`
@bors r+ |
📌 Commit 67c18fd has been approved by |
☀️ Test successful - checks-actions |
A
sys_common::ReentrantMutex
may not be moved after initializing it with.init()
. This was not enforced, but only stated as a requirement in the comments on the unsafe functions. This change enforces this no-moving rule usingPin
, by changing&self
to aPin
in theinit()
andlock()
functions.This uncovered a bug I introduced in #77154: stdio.rs (the only user of ReentrantMutex) called
init()
on its ReentrantMutexes while constructing them in the intializer ofSyncOnceCell::get_or_init
, which would move them afterwards. Interestingly, the ReentrantMutex unit tests already had the same bug, so this invalid usage has been tested on all (CI-tested) platforms for a long time. Apparently this doesn't break badly on any of the major platforms, but it does break the rules.*To be able to keep using SyncOnceCell, this adds a
SyncOnceCell::get_or_init_pin
function, which makes it possible to work with pinned values inside a (pinned) SyncOnceCell. Whether this function should be public or not and what its exact behaviour and interface should be if it would be public is something I'd like to leave for a separate issue or PR. In this PR, this function is internal-only and marked withpub(crate)
.* Note: That bug is now included in 1.48, while this patch can only make it to
1.491.50. We should consider the implications of 1.48 shipping with a wrong usage ofpthread_mutex_t
/CRITICAL_SECTION
/ .. which technically invokes UB according to their specification. The risk is very low, considering the objects are not 'used' (locked) before the move, and the ReentrantMutex unit tests have verified this works fine in practice.Edit: This has been backported and included in 1.48. And soon 1.49 too.
In future changes, I want to push this usage of Pin further inside
sys
instead of onlysys_common
, and apply it to all 'unmovable' objects there (Mutex
,Condvar
,RwLock
). Also, whilesys_common
's mutexes and condvars are already taken care of by #77147 and #77648, itsRwLock
should still be made movable or get pinned.