Skip to content
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

#[thread_local] static mut is allowed to have 'static lifetime #54366

Open
nikomatsakis opened this issue Sep 19, 2018 · 16 comments
Open

#[thread_local] static mut is allowed to have 'static lifetime #54366

nikomatsakis opened this issue Sep 19, 2018 · 16 comments
Labels
A-NLL Area: Non-lexical lifetimes (NLL) F-thread_local `#![feature(thread_local)]` NLL-sound Working towards the "invalid code does not compile" goal P-medium Medium priority requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Sep 19, 2018

Spinning off from #51269:

The MIR borrow checker (i.e., NLL) permits #[thread_local] static mut to have 'static lifetime. This is probably a bug. It arises because we ignore borrows of "unsafe places", which includes static mut.

We probably ought to stop doing that — at least not ignoring them entirely — but if we do so, we have to ensure that we continue to accept overlapping borrows of static mut (even though that is basically guaranteed UB), since it compiles today:

fn main() {
 static mut X: usize = 22;
 unsafe {
  let p = &mut X;
  let q = &mut X;
  *p += 1;
  *q += 1;
  *p += 1;
 }
}
@nikomatsakis nikomatsakis added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-NLL Area: Non-lexical lifetimes (NLL) NLL-deferred labels Sep 19, 2018
kennytm added a commit to kennytm/rust that referenced this issue Sep 20, 2018
…borrow-test, r=pnkfelix

Add regression test for thread local static mut borrows

FIXME(rust-lang#54366) - We probably shouldn't allow `#[thread_local] static mut` to get a `'static` lifetime, but for now, we should at least test the behavior that `rustc` currently has.
@pnkfelix
Copy link
Member

Re-triaging for #56754. Based on #29594 (comment) I am tagging this as P-medium.

@pnkfelix pnkfelix added P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. NLL-deferred labels Dec 21, 2018
@nikomatsakis nikomatsakis added the NLL-sound Working towards the "invalid code does not compile" goal label Jan 23, 2019
@matthewjasper matthewjasper added F-thread_local `#![feature(thread_local)]` requires-nightly This issue requires a nightly compiler in some way. labels Aug 11, 2019
@oli-obk
Copy link
Contributor

oli-obk commented Dec 26, 2019

Fixing this issue is easy now, but it will be super hard to do with a future incompat lint, because the real change is happening elsewhere. The fix is to change https://github.com/rust-lang/rust/blob/a916ac22b9f7f1f0f7aba0a41a789b3ecd765018/src/librustc/ty/util.rs#L666.L667 to

if self.is_mutable_static(def_id) {
    self.mk_mut_ref(self.lifetimes.re_erased, static_ty)

Because then borrowck will pick it up correctly and detect the thread local annotation and not give it the static lifetime

@bstrie
Copy link
Contributor

bstrie commented Feb 3, 2020

@oli-obk, is a future incompat lint necessary if this feature is still unstable? It doesn't sound like that change would affect stable users.

@nikomatsakis
Copy link
Contributor Author

I agree a lint is probably not needed. I'm not sure whether I think @oli-obk's suggested patch is correct (like, I literally haven't looked into it -- it probably is).

@oli-obk
Copy link
Contributor

oli-obk commented Apr 16, 2020

While attempting to fix #70685 (see #71192 for the changes) I got all of our tests to pass, except for https://github.com/rust-lang/rust/blob/master/src/test/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs which references this issue. I have reinstated the current behaviour, but the site of the required change that I suggested above has changed. It's now in the Rvalue::ty method.

@RalfJung
Copy link
Member

Curiously, doing the same thing with a non-mut static is detected by the borrow checker.

@RalfJung
Copy link
Member

@oli-obk

The fix is to change https://github.com/rust-lang/rust/blob/a916ac22b9f7f1f0f7aba0a41a789b3ecd765018/src/librustc/ty/util.rs#L666.L667 to

Isn't that a problem with unsafety checking? You said elsewhere that this crucially is a raw pointer to ensure that accesses are unsafe.

@oli-obk
Copy link
Contributor

oli-obk commented Nov 20, 2020

We do unsafety checking properly nowadays:

// If the projection root is an artifical local that we introduced when
// desugaring `static`, give a more specific error message
// (avoid the general "raw pointer" clause below, that would only be confusing).
if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info {
if self.tcx.is_mutable_static(def_id) {
self.require_unsafe(
UnsafetyViolationKind::General,
UnsafetyViolationDetails::UseOfMutableStatic,
);
return;

So the raw pointer checks aren't actually necessary, even though they are a good safety net for our use of LocalInfo for soundness.

@RalfJung
Copy link
Member

"properly" for some notion of "properly". ;) Relying on LocalInfo feels rather fragile.

@VorpalBlade
Copy link

Will this be solved in edition 2024 by the changes to static mut being done? Or will this still be an issue?

@arielb1
Copy link
Contributor

arielb1 commented Nov 19, 2024

What is the bug here? Why is borrowing a static mut for 'static "less sound" than anything else unsafe you can do with a static mut?

Is the problem that static mut tempts people to write functions taking &'static mut Self in an incorrect manner? In that case I believe the 2024 changes would solve it.

@RalfJung
Copy link
Member

The borrow checker clearly tries to account for the fact that thread_local statics don't have 'static lifetime... but then that doesn't apply to thread_local static mut. IMO that's clearly a bug. But it's not a soundness bug since creating such a reference is still unsafe.

@arielb1
Copy link
Contributor

arielb1 commented Nov 20, 2024

So the annoying thing is that the thread local rvalue needs to return *mut T to avoid taking a reference (which would invalidate all pre-existing shared pointers), but that does not remember the lifetime.

Or does

static mut X: (u32, u32) = (0, 0);

fn a() {
    &mut X.1;
}

translating to

_0: &mut (u32, u32) = ThreadLocalRef(X);
_1: &mut u32 = &mut (*_0).0

actually does not annoy MIRI too much

Therefore, there is a semantic reason for static muts being a dereference of a raw pointer, which means no lifetime checking.

Other way of describing this:

static mut X: Foo = ...;
X

is the same as

static X: RacyUnsafeCell<Foo> = ...;
X.get()

and since the latter allows getting 'static references, the former should too.

@RalfJung
Copy link
Member

Again, we already must have special code for immutable thread-local statics handling this. The exact same logic should "just" also be applied to mutable thread-local statics. Or is there some reason that does not work?

@arielb1
Copy link
Contributor

arielb1 commented Nov 20, 2024

@RalfJung

All kinds of statics and thread locals work by

_0: Pointer<T> = addr_of_static()
use(_0)

For non-mut statics, Pointer<T> is &T - for thread-locals it has a lifetime that is bounded to the function, for statics it has lifetime 'static.
For mut statics, Pointer<T> can't be &mut T since that would have aliasing problems. So it is *mut T, which doesn't come with a lifetime attached.

So you basically end up with the RacyUnsafeCell "desugaring".

@arielb1
Copy link
Contributor

arielb1 commented Nov 20, 2024

The thing I like about static mut being defined to act like the syntactic sugar for RacyUnsafeCell + a lint that prevents you from taking &mut in the most tempting-unsafe way, is that it makes it 1 less thing for the language to care about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-NLL Area: Non-lexical lifetimes (NLL) F-thread_local `#![feature(thread_local)]` NLL-sound Working towards the "invalid code does not compile" goal P-medium Medium priority requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants