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

Static checks for &mut in final value of constant with #![feature(const_mut_refs)] #71212

Closed
ecstatic-morse opened this issue Apr 16, 2020 · 14 comments · Fixed by #78578
Closed
Assignees
Labels
A-const-eval Area: constant evaluation (mir interpretation) C-bug Category: This is a bug. F-const_mut_refs `#![feature(const_mut_refs)]` glacier ICE tracked in rust-lang/glacier. 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

@ecstatic-morse
Copy link
Contributor

ecstatic-morse commented Apr 16, 2020

On stable, we prevent users from creating an &mut that points to memory inside a const by forbidding the creation of mutable references during const-eval. This limitation is only temporary, see #57349. We have a feature flag, const_mut_refs, that allows users to create mutable references, but no attempt is made to prevent &mut from escaping into the final value of a const like so:

#![feature(const_mut_refs)]
const FOO: &mut i32 = &mut 4;

fn main() {
    *FOO = 2;
}

This errors on the current nightly, and if there were a feature gate that allowed it, we would get an ICE:

error: internal compiler error: src/librustc_mir/interpret/intern.rs:238: const qualif failed to prevent mutable references

I think we've not yet settled on the semantics we want. We're allowing them in const fn and relying on the borrow checker to prevent references from escaping.

@ecstatic-morse
Copy link
Contributor Author

ecstatic-morse commented Apr 16, 2020

static mut is also an interesting case. Should the following compile? Perhaps, although with my proposal the user would be forced to wrap the initializer of FOO_MUT in a const fn. This suggests to me that checking for &mut in the type of a const is not the correct solution.

static mut FOO: i32 = 4;
const FOO_MUT: &mut i32 = unsafe { &mut FOO };

fn main() {
    *FOO_MUT = 2;
}

@ecstatic-morse ecstatic-morse self-assigned this Apr 16, 2020
@jonas-schievink jonas-schievink added A-const-eval Area: constant evaluation (mir interpretation) C-bug Category: This is a bug. F-const_mut_refs `#![feature(const_mut_refs)]` I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ 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. labels Apr 16, 2020
@ecstatic-morse ecstatic-morse added F-const_mut_refs `#![feature(const_mut_refs)]` B-unstable Blocker: Implemented in the nightly compiler and unstable. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ requires-nightly This issue requires a nightly compiler in some way. C-bug Category: This is a bug. and removed C-bug Category: This is a bug. F-const_mut_refs `#![feature(const_mut_refs)]` I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. requires-nightly This issue requires a nightly compiler in some way. labels Apr 16, 2020
@ecstatic-morse
Copy link
Contributor Author

Label race XD

@ecstatic-morse ecstatic-morse removed the B-unstable Blocker: Implemented in the nightly compiler and unstable. label Apr 16, 2020
@ecstatic-morse
Copy link
Contributor Author

ecstatic-morse commented Apr 16, 2020

cc @rust-lang/wg-const-eval

@RalfJung
Copy link
Member

static mut FOO: i32 = 4;
const FOO_MUT: &mut i32 = unsafe { &mut FOO };

fn main() {
    *FOO_MUT = 2;
}

Another interesting question here could be aliasing. Usually mutable references are unique. Does that mean that the following is UB because uniqueness got violated (FOO was accessed through a different place between creation and use of FOO_MUT)?

static mut FOO: i32 = 4;
const FOO_MUT: &mut i32 = unsafe { &mut FOO };

fn main() { unsafe {
    FOO += 5;
    *FOO_MUT = 2;
} }

@RalfJung
Copy link
Member

(Also strangely I did not get emailed about the ping above.)

@ecstatic-morse
Copy link
Contributor Author

ecstatic-morse commented Apr 17, 2020

Ah, that's a good point. In that case, I think we can ignore that example as an argument against the type visitor approach.

Perhaps this is a better example. I believe that things at the top level of a const initializer have a static storage duration. I'm not actually sure whether this is user-observable and guaranteed since rvalue static promotion is pretty aggressive inside constants. NULL and A currently compile on stable and, assuming that I'm right that neither A nor B dangle, reading from either one is not UB. B, however, must fail to compile, but a type-based check on the final value of a constant would reject NULL as well.

// Compile on stable and work as expected.
const NULL: *mut i32 = ptr::null_mut(); 
const A: *const i32 = &4;

// Must not compile.
const B: *mut i32 = &mut 4;

fn main() {
    println!("{}", unsafe { *A });
    // unsafe { *B = 4 } // Bad news
}

@JohnTitor
Copy link
Member

Triage: the ICE no longer occurs with the latest nightly by #71665.

@RalfJung
Copy link
Member

The bug is not primarily about the ICE though, it is about what static checks we need in place to avoid mis-behaved &mut constants.

@RalfJung RalfJung removed the I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ label May 17, 2020
@RalfJung
Copy link
Member

What actually happens currently is that interning detects the mutable reference in a const and complains about it -- good.

I wonder if validation should do that check, instead... but currently validation is a separate query so there is always the risk that it does not run. :/ Things would be so much easier if the raw const query would always do validation and thus ensure that only validated values ever enter the rest of the compiler...

@oli-obk
Copy link
Contributor

oli-obk commented May 19, 2020

Things would be so much easier if the raw const query would always do validation and thus ensure that only validated values ever enter the rest of the compiler...

If we make validation and interning stop when encountering GlobalAlloc::Static, we can do this.

@RalfJung
Copy link
Member

@oli-obk That would be awesome! I laid down my thoughts in #72396. If that works, I'd be really happy. :D

@oli-obk
Copy link
Contributor

oli-obk commented Dec 4, 2020

Another fun bug with static mut and const_mut_refs:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=baf2a1bbbf7ba1b5a1c9d7015c2d5b2c

#![feature(const_mut_refs)]
pub mod a {
    #[no_mangle]
    pub static mut FOO: &mut [i32] = &mut [42];
}

pub mod b {
    #[no_mangle]
    pub static mut BAR: &mut [i32] = unsafe { &mut *crate::a::FOO };
}

fn main() {
    unsafe {
        assert_eq!(a::FOO.as_ptr(), b::BAR.as_ptr());
    }
}

The assertion fails, even though I belive that it should most definitely not. Only base allocations of statics have a guaranteed address and won't get deduplicated.

@RalfJung
Copy link
Member

RalfJung commented Dec 5, 2020

@oli-obk Shouldn't that be a separate bug? That has nothing to do with static checks or so, this seems like a codegen issue. &mut *crate::a::FOO should return the original pointer.

@oli-obk
Copy link
Contributor

oli-obk commented Dec 7, 2020

It's indeed a separate bug, but also a blocker for this feature gate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Area: constant evaluation (mir interpretation) C-bug Category: This is a bug. F-const_mut_refs `#![feature(const_mut_refs)]` glacier ICE tracked in rust-lang/glacier. 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.

6 participants