Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upConsider removing UB-happy `static mut` in a future edition. #53639
Comments
eddyb
added
I-nominated
T-lang
labels
Aug 23, 2018
This comment has been minimized.
This comment has been minimized.
|
I don't know at all whether we should do this or not atm. But to make this decision I have an... ...Idea: We should do a crater run to scrutinize how many legitimate usages there are and how many ones there are that have UB. |
This comment has been minimized.
This comment has been minimized.
|
cc @rust-lang/wg-unsafe-code-guidelines -- we have this group just for this sort of thing =) |
This comment has been minimized.
This comment has been minimized.
|
I'm 100% behind getting rid of |
This comment has been minimized.
This comment has been minimized.
|
We should clearly document the undefined behavior, but I don't think we should remove the ability to have (unsafe) global mutable locations directly, without any layer of indirection. That would make it more difficult to build certain types of lock-free synchronization, for instance. |
This comment has been minimized.
This comment has been minimized.
|
@joshtriplett But you can always use |
This comment has been minimized.
This comment has been minimized.
|
Can you give an example of what you mean? You can't do much more with |
This comment has been minimized.
This comment has been minimized.
Yeah, and that's just as unsafe. So TBH I do not see the point. |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung No, it's not, and I've explained why. With EDIT: Now if "references exist" cannot be UB, ever, then this is not a problem, but IIUC, they do. |
This comment has been minimized.
This comment has been minimized.
|
It's not actually different from an |
This comment has been minimized.
This comment has been minimized.
|
@eddyb Ah okay I see -- that has nothing to with with I agree |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung Okay I should make it clearer that the |
This comment has been minimized.
This comment has been minimized.
|
If we could weaken ... tough people would probably still turn them into references ASAP. |
This comment has been minimized.
This comment has been minimized.
How would they instantiate their custom type in stable Rust? User To me this sounds like it would reduce what you can do in the 2018 edition. In the 2015 edition you can create |
This comment has been minimized.
This comment has been minimized.
|
@japaric Okay, that's a good point. I removed the bound from the Note that you can use an associated constant if you don't need arguments in your constructor (e.g. Depending on what you're doing you might be able to make your fields public, but as was in the case of cc @Centril @oli-obk Do we want to force people to replace their "custom synchronization abstractions" involving
What kinds of types?
Hmm, Given I wish the |
This comment has been minimized.
This comment has been minimized.
|
@rfcbot poll @rust-lang/libs @rust-lang/lang Should we add #[repr(transparent)]
pub struct RacyUnsafeCell<T>(UnsafeCell<T>);
unsafe impl<T: Sync> Sync for RacyUnsafeCell<T> {}
impl<T> RacyUnsafeCell<T> {
pub const fn new(x: T) -> Self {
RacyUnsafeCell(UnsafeCell::new(x))
}
pub fn get(&self) -> *mut T {
self.0.get()
}
} |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 23, 2018
•
|
Team member @eddyb has asked teams: T-lang, T-libs, for consensus on:
|
This comment has been minimized.
This comment has been minimized.
|
@RalfJung What we want is That is, the user of the abstraction should be outside of the "unsafe zone" and be able to use only safe code to interact with the abstraction, otherwise it's not really a good abstraction. |
This comment has been minimized.
This comment has been minimized.
|
In embedded we use // we generate this function using macros
#[export_name = "SOME_KNOWN_NAME_TO_HAVE_THE_LINKER_PUT_THIS_IN_THE_RIGHT_PLACE"]
pub unsafe extern "C" fn interrupt_handler_that_has_some_random_name_unknown_to_the_user() {
static mut STATE: usize = 0; // initial value provided by the user
on_button_pressed(&mut STATE);
}
// the user provides this function along with the initial value for the state
fn on_button_pressed(count: &mut usize) {
*count += 1;
println!("Button has been pressed {} times", *count);
}This way the end user indirectly uses As there are no newtypes involved the user can use primitives and types I'm not against this feature as long as the above pattern continues to work on stable Rust 2018. |
This comment has been minimized.
This comment has been minimized.
|
If there's an API that @japaric and libs are happy with, I'm happy to remove |
This comment has been minimized.
This comment has been minimized.
|
Personally, I think we should just deprecate |
This comment has been minimized.
This comment has been minimized.
|
@japaric Ah, if you're doing code generation, you can have your own version of There's a more powerful pattern I prefer using in those situations: // Lifetime-invariant ZST token. Probably doesn't even need the invariance.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct InterruptsDisabled<'a>(PhantomData<&'a mut &'a ()>);
// Public field type to get around the lack of stable `const fn`.
pub struct InterruptLock<T: ?Sized + Send>(pub UnsafeCell<T>);
// AFAIK this should behave like Mutex.
unsafe impl<T: ?Sized + Send> Sync for InterruptLock<T> {}
impl<T: ?Sized + Send> InterruptLock<T> {
// This gives access to the `T` that's `Send` but maybe not `!Sync`,
// for *at most* the duration that the interrupts are disabled for.
// Note: I wanted to implement `Index` but that can't constrain lifetimes.
fn get<'a>(&'a self, _: InterruptsDisabled<'a>) -> &'a T {
unsafe { &*self.0.get() }
}
}
// Note that you bake any of these into `InterruptLock` without `const fn`,
// because you would need a private field to ensure nobody can touch it.
// OTOH, `UnsafeCell<T>` makes *only constructing* the field public.
pub type InterruptCell<T: ?Sized + Send> = InterruptLock<Cell<T>>;
pub type InterruptRefCell<T: ?Sized + Send> = InterruptLock<RefCell<T>>;#[export_name = "SOME_KNOWN_NAME_TO_HAVE_THE_LINKER_PUT_THIS_IN_THE_RIGHT_PLACE"]
pub unsafe extern "C" fn interrupt_handler_that_has_some_random_name_unknown_to_the_user(
token: InterruptsDisabled,
) {
on_button_pressed(token);
}
static COUNT: InterruptCell<usize> = InterruptLock(UnsafeCell::new(
Cell::new(0),
));
fn on_button_pressed(token: InterruptsDisabled) {
let count = COUNT.get(token);
count.set(count.get() + 1);
println!("Button has been pressed {} times", count.get());
}There's another variation on this, where you make the token non- #[export_name = "SOME_KNOWN_NAME_TO_HAVE_THE_LINKER_PUT_THIS_IN_THE_RIGHT_PLACE"]
pub unsafe extern "C" fn interrupt_handler_that_has_some_random_name_unknown_to_the_user(
token: InterruptsDisabled,
) {
on_button_pressed(token);
}
static COUNT: InterruptLock<usize> = InterruptLock(UnsafeCell::new(
0,
));
fn on_button_pressed(mut token: InterruptsDisabled) {
// This mutable borrow of `token` prevents using it for
// accessing any `InterruptLock`s, including `COUNT` itself.
// The previous version was like `&token` from this one.
let count = COUNT.get_mut(&mut token);
*count += 1;
println!("Button has been pressed {} times", *count);
}(I didn't write the implementation details for the second version for brevity's sake, if desired I can edit this later) You can even allow creating a Theoretically you can even create a IMO a safe abstraction like this is a good long-term investment, since it can handle any sort of cc @RalfJung Has anything like this been proven safe? |
This comment has been minimized.
This comment has been minimized.
|
@eddyb note that the non-reentrancy that @japaric describes is for that particular interrupt handler (and any lower priority, but that’s hard to do anything useful with). Interrupts are not globally disabled during an interrupt handler on architectures like ARMv6-M. It might be possible to use a more complicated token scheme where each interrupt has its own token type, but I don’t know if anyone has looked into some way to make that actually usable. |
nikomatsakis
removed
the
I-nominated
label
Aug 23, 2018
This comment has been minimized.
This comment has been minimized.
|
@Nemo157 Ah, that's a very interesting detail! Yeah you can just have one token type per level and use Another idea that I came up with while discussing with @arielb1 on IRC: We could only allow private That, combined with deprecation of private |
This comment has been minimized.
This comment has been minimized.
IIRC, AVR actually allows for recursive interrupt handlers:
It's up to the programmer to choose (and thus appropriately handle) this case. |
This comment has been minimized.
This comment has been minimized.
On every architecture I'm aware of, you can explicitly set the interrupt flag after entering a handler; this is not AVR-specific. (On architectures with interrupt prioritization, like Cortex-M, you'll also need to manipulate priorities.) But the point here is that this needs to be done explicitly, with unsafe code; the architecture guarantees that you wouldn't reenter the ISR if you don't specifically request that. So this is perfectly in line with the usual safety rules. |
This comment has been minimized.
This comment has been minimized.
|
Some C APIs expose mutable globals that my Rust code right now access via: extern "C" {
pub static mut FOO: BAR;
}Some C APIs require their users to define mutable globals, that the C library then accesses. Right now my Rust code interfaces with those using: #[export(name = "foo")]
pub static mut FOO: BAR = ..initializer..;How do I access mutable globals from C libraries, and how do I provide mutable globals to C libraries, without An example of a C library that uses both is |
This comment has been minimized.
This comment has been minimized.
|
I don’t like this. It’s already unsafe. That’s good enough. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I got two other RFCs I still want to write that I care about more strongly, so I don't think I'd get to this one any time soon -- sorry. |
This comment has been minimized.
This comment has been minimized.
|
I also don't have time for this, unfortunately. |
This comment has been minimized.
This comment has been minimized.
|
I don't see the point of doing this since all the accesses are unsafe by default. I don't know what the FFI story is for this is but please do consider it :) |
This comment has been minimized.
This comment has been minimized.
|
I'd like to see a demonstration or at the very least hand-wavy explanation that the proposed generic replacement for |
This comment has been minimized.
This comment has been minimized.
|
Take a look at the IR for this example. I used FFI functions to prevent optimization of the loads and stores away. If you look at the IR, you'll see that both stores to the statics are optimized down to a single store instruction. It's not a comprehensive proof, of course, but it demonstrates that the optimizer can figure this out. |
This comment has been minimized.
This comment has been minimized.
|
@alercah Thanks. That's good to know. I'm somewhat less opposed to the proposal now, especially if this optimisation holds on all platforms... even though there still exist other reasons for maintaining it (namely uniformity and the fact has already opened the door to UB by using unsafe, so why bother). |
SimonSapin
referenced this issue
Nov 2, 2018
Closed
Implement Copy and Clone for UnsafeCell<T> #55207
This comment has been minimized.
This comment has been minimized.
|
#55207 (comment) proposes redefining |
This comment has been minimized.
This comment has been minimized.
|
@alexreg As for FFI, And Anyway, we're not doing this in Rust 2018, and I agree with suggestions of general deprecation. |
eddyb
changed the title
Consider removing UB-happy `static mut` in Rust 2018.
Consider removing UB-happy `static mut` in a future edition.
Nov 2, 2018
This comment has been minimized.
This comment has been minimized.
|
@eddyb Okay, fair enough then! |
bluss
referenced this issue
Nov 24, 2018
Closed
Use static with AtomicPtr instead of static mut for ifunc #42
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@Centril I don’t understand what that means or how it relates to min_const_fn |
This comment has been minimized.
This comment has been minimized.
|
@SimonSapin I'm not sure either; but apparently private struct fields in const fns were a blocker wrt. deprecating struct Foo {
field: u8,
}
impl Foo {
const fn bar() -> Self {
Self { field: 0 }
}
}which apparently unblocks something? @eddyb can elaborate perhaps. |
This comment has been minimized.
This comment has been minimized.
|
One of the main points of discussion on this issue has been the lack of stable |
gnzlbg
referenced this issue
Dec 10, 2018
Open
Why are some constants like MPI_REQUEST_NULL statics ? #62
oli-obk
referenced this issue
Dec 17, 2018
Closed
cannot mutate statics in initalizer of another static #56903
This comment has been minimized.
This comment has been minimized.
|
Instead of killing With rust-lang/rfcs#2582, we can tell people that |
This comment has been minimized.
This comment has been minimized.
|
That still doesn't solve the footgun of any sort of concurrent modification of a |
This comment has been minimized.
This comment has been minimized.
|
@retep998 Neither does removing |
This comment has been minimized.
This comment has been minimized.
|
@retep998 note that creating a |
This comment has been minimized.
This comment has been minimized.
|
@gnzlbg But One advantage is that a value of type The much bigger advantage is that |
This comment has been minimized.
This comment has been minimized.
|
@eddyb I was only referring to the issue that @retep998 mentioned. Just using As you mention, using a |
BurntSushi
added a commit
to BurntSushi/rust-memchr
that referenced
this issue
Feb 12, 2019
BurntSushi
added a commit
to BurntSushi/rust-memchr
that referenced
this issue
Feb 12, 2019
This comment has been minimized.
This comment has been minimized.
@retep998 This is the case I've seen too.
@RalfJung Do you mean in the current state of things or in proposed one? Currently it doesn't (although does require For example, if we take following Rust code: pub fn f() -> u8 {
let mut x = 10;
let x_ref = &x;
x = 20;
*x_ref
}Then, as expected, we get a borrow checker error due to mutation of variable while it's still borrowed immutably:
But, if we use pub unsafe fn g() -> u8 {
static mut X: u8 = 10;
let x_ref = &X;
X = 20;
*x_ref
}without any errors and raw pointers involved, despite mutating an already-borrowed variable. |
This comment has been minimized.
This comment has been minimized.
I meant with my proposal. |
eddyb commentedAug 23, 2018
•
edited
static mutis almost impossible to use correctly, see rust-lang-nursery/lazy-static.rs#117 for an example in the widely-usedlazy-static.You must be able to show that every borrow of the
static mutis not reentrant (as opposed to regular interior mutability, which only requires reentrance-freedom when accessing data), which is almost entirely impossible in real-world scenarios.We have a chance at removing it from Rust2018 and force people to use a proper synchronization abstraction (e.g.
lazy_static!+Mutex), or in lieu of one,thread_local!/scoped_thread_local!.If they were using
static mutwith custom synchronization logic, they should do this:And then use
CustomSynchronizingAbstractionwith regularstatics, safely.This matches the "soundness boundary" of Rust APIs, whereas
static mutis more like C.cc @RalfJung @rust-lang/compiler @rust-lang/lang