-
Notifications
You must be signed in to change notification settings - Fork 355
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
miri accepts when embedded UnsafeCell in mutable Pin
reference is changed
#1860
Comments
Is this a duplicate of #1665? |
Note that your example also is flagged by Miri with
So I think this is a combination of rust-lang/unsafe-code-guidelines#125 ( |
Naive question: given that |
Almost everything in this space is undecided. ;) But since it fails with |
Ok, what about if I replace the use std::cell::UnsafeCell;
use std::ptr;
struct A {
x: UnsafeCell<isize>,
ptr_x: *mut isize,
}
impl A {
fn new(x: isize) -> Box<A> {
let mut a = Box::new(A {
x: UnsafeCell::new(x),
ptr_x: ptr::null_mut(),
});
a.ptr_x = a.x.get_mut();
a
}
fn change_x(this: *mut A) {
unsafe {
(*this).ptr_x.write(0);
}
}
#[inline(never)]
fn wait_for_x(this: *mut A) {
while unsafe { *(*this).x.get() == 42 } {
A::change_x(this);
}
}
}
fn main() {
let a = Box::into_raw(A::new(42));
println!("waiting for Godot");
A::wait_for_x(a);
println!("done");
} MIRI doesn't flag it by default, but does flag it when Based on a previous post I read on Github (from you IIRC) I was under the assumption that I can basically write C-style unsafe code if I use raw pointers and do not mix mutable references and raw pointers (and follow C's aliasing rules.) |
Yes, that is correct. It is unfortunately rather easy to accidentally create a reference in Rust. For example, calling |
|
The problem is this line: You basically must stop using the |
Here is a version that truly uses only raw pointers, and this one is accepted even with #![feature(unsafe_cell_raw_get)]
use std::cell::UnsafeCell;
use std::ptr;
struct A {
x: isize,
ptr_x: *mut isize,
}
impl A {
fn new(x: isize) -> *mut A {
let a = Box::new(A {
x,
ptr_x: ptr::null_mut(),
});
let a = Box::into_raw(a);
unsafe { (*a).ptr_x = ptr::addr_of_mut!((*a).x); }
a
}
fn change_x(this: *mut A) {
unsafe {
(*this).ptr_x.write(0);
}
}
#[inline(never)]
fn wait_for_x(this: *mut A) {
while unsafe { (*this).x == 42 } {
A::change_x(this);
}
}
}
fn main() {
let a = A::new(42);
println!("waiting for Godot");
A::wait_for_x(a);
println!("done");
} |
Thanks, that makes sense, and that's good for my actual use case which doesn't have Boxes anyway. Is there any receiver-style syntax in Rust for raw pointers or will I have to write most of the code using the |
|
A side note (probably better suited for a discussion on IRLO or the user's forum). Rust is making it very hard to combine safe and (necessary) unsafe code in a way that makes one feel confident about the boundary between the two. Even "classic" data structures like an intrusive circular doubly-linked list appear to be unimplementable unless one stays exclusively in raw pointers, and in that case the boundary between the "raw pointer world" and (what I would really like to be the majority of the application) the safe world seems to be drifting in a way that favors the former and reduces the size of the latter, which is obviously not why we want to use Rust. This diminishes the usefulness of Rust IMO if the resulting code is basically a C version built with raw pointers because it's so difficult to correctly cross the boundary into safe Rust. Rust has basically taken what is already a subtle subject (pointers) and squared its complexity. It's also a bit deceptive to present raw pointers, references, and smart pointers as pointer types in Rust when in fact writing code that crosses between the types is as difficult to write as it appears to be or at least to me. |
Thanks for the feedback! The aliasing rules in Rust are not final yet, and concerns like this are indeed a major reason for that. Maybe there's a better model out there that is less fragile? More research is needed. Intrusive circular doubly-linked lists are indeed a challenge in Rust. But with my verification lens on, I have to say that this is also a highly non-trivial data structure in terms of who is allow to perform which kinds of accesses to which memory when. So I am not surprised that this data structure is challenging -- you picked a really hard example! Many other data structures are a lot easier. You might also be interested in rust-lang/rust#63818 -- solving this problem will possibly introduce a kind of But yes, this is more of a UCG thing, as now we are talking about how the rules could be improved to make them easier to work with. There are already quite a few Stacked Borrows related discussions, and this comment is related to (at least) rust-lang/unsafe-code-guidelines#133 and rust-lang/unsafe-code-guidelines#148. I'll close the Miri issue now since Miri is working as intended and I think the concrete questions you had about Miri behavior were answered. |
To add to that point -- to my knowledge, even with (But if you want to discuss this further, maybe open a forum thread somewhere and ping me; this is not the right place.) |
It's my understanding that changing a memory location through a pointer that is an alias of a mutable reference is disallowed in Rust, and that miri could/should help detecting such scenarios.
Consider this playground reproduced here:
Should miri flag UB in this code?
ps: compare to this similar playground that does not use
Pin
. Here, miri flags UB and the release/nightly compiler correctly exploits the UB by emitting an infinite loop.The text was updated successfully, but these errors were encountered: