-
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
Warn that *const T as *mut T
is Undefined Behavior
#66136
Comments
Note that it's not UB to do this cast. This cast can be a contributing factor to code that's unsound (like https://blog.rust-lang.org/2017/02/09/Rust-1.15.1.html), but it's important not to say that the cast by itself is UB. |
Would it be correct to say that it becomes UB as soon as the pointer is actually used? Or as soon as a value is constructed from it? |
No, it all depends on how the pointer was actually made. For example, you can use |
Hmm, indeed you are right - this passes MIRI: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=efe35f2a1422ef308c66236ee3d88135 So while it's not instant UB by itself, there is absolutely no reason to do this. |
Others already raised this, here's the way I put it:
(It's kind of confusing to have the same discussion both here and at rust-lang/rust-clippy#4774... maybe both issues shouldn't have been opened with almost the same text at the same time.^^) |
Good point, I will refrain from doing so in the future. I've updated description based on the discussion. |
I think we could start out the lint in Clippy and then uplift it based on how the implementation looks and when we have a more concrete proposal. |
One nuisance with this is code that uses Though maybe there's a better way to do that? |
This seems like a good place to ask, as it concerns a cast from Suppose I have a shared reference to an atomic. Would transmuting it to a mutable pointer for FFI be a problem? let atomic = AtomicI32::new(1);
let ptr = &atomic as *const AtomicI32 as *mut i32;
unsafe {
ffi(ptr);
} Or would a dance with let atomic = AtomicI32::new(1);
unsafe {
let ptr = (&*(&atomic as *const AtomicI32 as *const UnsafeCell<i32>)).get();
ffi(ptr);
} |
Right now I think the official ruling is that you must go through Stacked Borrows says that |
@RalfJung Thank you, that makes sense. I wonder if it would be useful to add a small |
Atomic as_mut_ptr I encountered the following pattern a few times: In Rust we use some atomic type like `AtomicI32`, and an FFI interface exposes this as `*mut i32` (or some similar `libc` type). It was not obvious to me if a just transmuting a pointer to the atomic was acceptable, or if this should use a cast that goes through an `UnsafeCell`. See rust-lang#66136 (comment) Transmuting the pointer directly: ```rust let atomic = AtomicI32::new(1); let ptr = &atomic as *const AtomicI32 as *mut i32; unsafe { ffi(ptr); } ``` A dance with `UnsafeCell`: ```rust let atomic = AtomicI32::new(1); unsafe { let ptr = (&*(&atomic as *const AtomicI32 as *const UnsafeCell<i32>)).get(); ffi(ptr); } ``` Maybe in the end both ways could be valid. But why not expose a direct method to get a pointer from the standard library? An `as_mut_ptr` method on atomics can be safe, because only the use of the resulting pointer is where things can get unsafe. I documented its use for FFI, and "Doing non-atomic reads and writes on the resulting integer can be a data race." The standard library could make use this method in a few places in the WASM module. cc @RalfJung as you answered my original question.
About the proposed lint, I once added extern "C" {
// N.B., mutability can be easily incorrect in FFI calls -- as
// in C, the default is mutable pointers.
fn ffi(c: *mut u8);
fn int_ffi(c: *mut i32);
}
ffi(a.as_ptr() as *mut _);
int_ffi(num as *const _ as *mut _);
int_ffi(&3 as *const _ as *mut _); However, it probably should be changed to recognize methods from standard library accepting |
Casting a
*const T
to*mut T
may lead to memory corruption since it allows mutation of shared state. Even if the*const T
happened to be unique, it is still undefined behavior and the optimizer may break such code in interesting ways. In a nutshell, this is as bad as transmuting a&
into&mut
. The compiler should warn against doing this.Update: as pointed out below, there are cases when that does not immediately trigger UB, but in those cases there is no reason to do this in the first place.
This often occurs when people try to consume a data structure and create a new one from it, e.g.
in which case the proper solution is to rewrite it as
This also may happen when people try to mutate shared state through a
&
, in which case they need aCell
,RefCell
or anUnsafeCell
instead.Playground with a real-world snippet that fails MIRI: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=b28a15e3d99616b03caafdd794550946
This pattern seems to be quite widespread - quoting @RalfJung on Zulip:
I have already requested a Clippy lint for this, but this looks important enough to warn against by default, without relying on optional tooling to catch this.
The text was updated successfully, but these errors were encountered: