Reference and raw pointer aliasing

  1. Mutating through a pointer obtained from a shared reference is UB unless mutation happens inside UnsafeCell. While this is true for Rust code, what about the situation when raw pointer is never written to from Rust, only from the other side of FFI boundary? Is this also UB per se?
  2. Is dereferencing a *const _(no mutation) while there is an aliasing &mut _ an UB? Again, what about when dereferencing happens on the other side of the FFI boundary and not in Rust code? Does that make a difference?

To be absolutely clear, if you have an &T to some T stored inside an UnsafeCell, it is UB to write to that T through raw pointers obtained from the &T. The thing that Rust does allow is writing to pointers derived from a call to UnsafeCell::get.

Yes, it is UB to write to a raw pointer derived from an immutable reference, full stop. It doesn't matter if you do it, or if you ask C to do it for you.

No, but once you have performed that dereference, it is UB to ever use the &mut again. Again, FFI boundaries are irrelevant.

7 Likes

thank you for the answer. Here is a related question. This might seem silly, but does the following code have a defined behavior?

use std::cell::UnsafeCell;

fn transmute<T>(var: &mut T) where T: std::fmt::Display + std::default::Default {
    unsafe {
        // 2. transmuting given reference to &mut UnsafeCell<_>
        let transmuted: &mut UnsafeCell<T> = std::mem::transmute(var);

        // 3. mutating through transmuted reference
        *transmuted.get() = Default::default();

        // 4. dropping transmuted mutable reference
    }
}
fn main() {
    let mut s = 1i32;
    // 1. taking mutable reference
    let s_ref = &mut s;

    // magic happens inside
    transmute(s_ref);

    // 5. continue to use mutable reference
    println!("{}", s_ref);
}

I suspect this should be valid code.

Yes, that is fine. In fact, this is what Cell::from_mut does.

Awesome, thank you so much. Now I can hide my UnsafeCell API :partying_face:

I will just note that mutable references have no destructors. It is the location of their last use that matters, not when they would have been destroyed if they had a destructor.

This is also what I tried to convey with this:

No, but once you have performed that dereference, it is UB to ever use the &mut again.

It's worth noting that you can't always use the standard library as a reference for defined behavior. Since it is released in tandem, std is able to make assumptions about the current compiler implementation, but that makes no guarantee about the future for your own code.

When you look in the source code of std, it is very good at pointing this out when it is the case.

Ideally, yes, but the absence of such warnings still doesn't make a specification.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.